@masumdev/markforge 0.2.3 → 0.2.5

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
@@ -40,10 +40,17 @@ var src_exports = {};
40
40
  __export(src_exports, {
41
41
  DEFAULT_CONFIG: () => DEFAULT_CONFIG,
42
42
  MARKFORGE_VERSION: () => MARKFORGE_VERSION,
43
+ Orientation: () => Orientation,
44
+ OutputFormat: () => OutputFormat,
45
+ PAPER_DIMENSIONS_TWIP: () => PAPER_DIMENSIONS_TWIP,
46
+ PaperSizeEnum: () => PaperSizeEnum,
43
47
  SYNTAX_COLORS: () => SYNTAX_COLORS,
48
+ SyntaxTheme: () => SyntaxTheme,
44
49
  THEMES: () => THEMES,
45
- THEME_ACADEMIC: () => THEME_ACADEMIC,
50
+ THEME_CORPORATE: () => THEME_CORPORATE,
46
51
  THEME_DEFAULT: () => THEME_DEFAULT,
52
+ Theme: () => Theme,
53
+ WatermarkPosition: () => WatermarkPosition,
47
54
  buildDocxDocument: () => buildDocxDocument,
48
55
  buildHtmlDocument: () => buildHtmlDocument,
49
56
  buildPdfDocument: () => buildPdfDocument,
@@ -52,6 +59,7 @@ __export(src_exports, {
52
59
  escapeHtml: () => escapeHtml,
53
60
  findChromeExecutable: () => findChromeExecutable,
54
61
  formatServerTimestamp: () => formatServerTimestamp,
62
+ generateThemeCss: () => generateThemeCss,
55
63
  getMarkforgeVersion: () => getMarkforgeVersion,
56
64
  getMimeType: () => getMimeType,
57
65
  highlightCodeToHtml: () => highlightCodeToHtml,
@@ -59,11 +67,16 @@ __export(src_exports, {
59
67
  inlineHtmlImages: () => inlineHtmlImages,
60
68
  loadConfig: () => loadConfig,
61
69
  markforge: () => compileMarkdown,
70
+ normalizeHeaderFooter: () => normalizeHeaderFooter,
71
+ normalizeHeaderFooterSlot: () => normalizeHeaderFooterSlot,
72
+ normalizeWatermark: () => normalizeWatermark,
62
73
  parseInlineSpans: () => parseInlineSpans,
63
- parseMarginToTwip: () => parseMarginToTwip,
74
+ parseMarginToTwip: () => parseMarginToTwip2,
64
75
  parseMarkdownDocument: () => parseMarkdownDocument,
65
76
  renderInlinesToHtml: () => renderInlinesToHtml,
66
77
  renderMermaidToPng: () => renderMermaidToPng,
78
+ replaceDocumentTokens: () => replaceDocumentTokens,
79
+ resolveDocumentConfig: () => resolveDocumentConfig,
67
80
  resolveImage: () => resolveImage,
68
81
  slugify: () => slugify,
69
82
  tokenizeCodeLine: () => tokenizeCodeLine
@@ -585,6 +598,51 @@ var SYNTAX_COLORS_LIGHT = {
585
598
  plain: "24292E"
586
599
  // Near-black — identifiers
587
600
  };
601
+ var SYNTAX_THEMES = {
602
+ "github-dark": SYNTAX_COLORS,
603
+ "dark": SYNTAX_COLORS,
604
+ "github-light": SYNTAX_COLORS_LIGHT,
605
+ "light": SYNTAX_COLORS_LIGHT,
606
+ "dracula": {
607
+ keyword: "FF79C6",
608
+ string: "F1FA8C",
609
+ comment: "6272A4",
610
+ number: "BD93F9",
611
+ boolean: "BD93F9",
612
+ function: "50FA7B",
613
+ type: "8BE9FD",
614
+ operator: "FF79C6",
615
+ punctuation: "F8F8F2",
616
+ plain: "F8F8F2"
617
+ },
618
+ "monokai": {
619
+ keyword: "F92672",
620
+ string: "E6DB74",
621
+ comment: "75715E",
622
+ number: "AE81FF",
623
+ boolean: "AE81FF",
624
+ function: "A6E22E",
625
+ type: "66D9EF",
626
+ operator: "F92672",
627
+ punctuation: "F8F8F2",
628
+ plain: "F8F8F2"
629
+ },
630
+ "nord": {
631
+ keyword: "81A1C1",
632
+ string: "A3BE8C",
633
+ comment: "616E88",
634
+ number: "B48EAD",
635
+ boolean: "81A1C1",
636
+ function: "88C0D0",
637
+ type: "8FBCBB",
638
+ operator: "81A1C1",
639
+ punctuation: "ECEFF4",
640
+ plain: "D8DEE9"
641
+ }
642
+ };
643
+ function getSyntaxPalette(themeName = "github-dark") {
644
+ return SYNTAX_THEMES[themeName.toLowerCase()] || SYNTAX_THEMES["github-dark"];
645
+ }
588
646
  var JS_KEYWORDS = /* @__PURE__ */ new Set([
589
647
  "import",
590
648
  "from",
@@ -728,9 +786,9 @@ var SQL_KEYWORDS = /* @__PURE__ */ new Set([
728
786
  "foreign",
729
787
  "references"
730
788
  ]);
731
- function tokenizeCodeLine(line, lang = "", theme = "dark") {
789
+ function tokenizeCodeLine(line, lang = "", theme = "github-dark") {
732
790
  var _a;
733
- const COLORS = theme === "light" ? SYNTAX_COLORS_LIGHT : SYNTAX_COLORS;
791
+ const COLORS = getSyntaxPalette(theme);
734
792
  if (!line) {
735
793
  return [{ text: " ", type: "plain", colorHex: COLORS.plain }];
736
794
  }
@@ -813,6 +871,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
813
871
  tokens.push({ text: word, type: "keyword", colorHex: COLORS.keyword, bold: true });
814
872
  continue;
815
873
  }
874
+ if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
875
+ tokens.push({ text: word, type: "type", colorHex: COLORS.type });
876
+ continue;
877
+ }
816
878
  let nextNonWs = pos;
817
879
  while (nextNonWs < line.length && /\s/.test(line[nextNonWs])) {
818
880
  nextNonWs++;
@@ -821,10 +883,6 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
821
883
  tokens.push({ text: word, type: "function", colorHex: COLORS.function });
822
884
  continue;
823
885
  }
824
- if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
825
- tokens.push({ text: word, type: "type", colorHex: COLORS.type });
826
- continue;
827
- }
828
886
  tokens.push({ text: word, type: "plain", colorHex: COLORS.plain });
829
887
  continue;
830
888
  }
@@ -837,10 +895,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
837
895
  }
838
896
  return tokens;
839
897
  }
840
- function highlightCodeToHtml(code, lang = "") {
898
+ function highlightCodeToHtml(code, lang = "", theme = "github-dark") {
841
899
  const lines = (code || "").split("\n");
842
900
  const htmlLines = lines.map((line) => {
843
- const tokens = tokenizeCodeLine(line, lang);
901
+ const tokens = tokenizeCodeLine(line, lang, theme);
844
902
  return tokens.map((t) => {
845
903
  const escaped = t.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
846
904
  if (t.type === "plain") return escaped;
@@ -852,21 +910,62 @@ function highlightCodeToHtml(code, lang = "") {
852
910
 
853
911
  // src/core/mermaid/mermaidRenderer.ts
854
912
  var import_node_child_process2 = require("child_process");
855
- var fs4 = __toESM(require("fs"));
913
+ var fs5 = __toESM(require("fs"));
856
914
  var os2 = __toESM(require("os"));
857
- var path4 = __toESM(require("path"));
858
- var import_node_url2 = require("url");
915
+ var path5 = __toESM(require("path"));
916
+ var import_node_url3 = require("url");
859
917
 
860
918
  // src/core/pdf/pdfBuilder.ts
861
- var fs3 = __toESM(require("fs"));
862
- var path3 = __toESM(require("path"));
919
+ var fs4 = __toESM(require("fs"));
920
+ var path4 = __toESM(require("path"));
863
921
  var os = __toESM(require("os"));
864
- var import_node_url = require("url");
922
+ var import_node_url2 = require("url");
865
923
  var import_node_child_process = require("child_process");
866
924
 
867
925
  // src/core/html/htmlBuilder.ts
868
- var fs2 = __toESM(require("fs"));
869
- var path2 = __toESM(require("path"));
926
+ var fs3 = __toESM(require("fs"));
927
+ var path3 = __toESM(require("path"));
928
+
929
+ // src/config/types.ts
930
+ var OutputFormat = /* @__PURE__ */ ((OutputFormat2) => {
931
+ OutputFormat2["DOCX"] = "docx";
932
+ OutputFormat2["PDF"] = "pdf";
933
+ OutputFormat2["HTML"] = "html";
934
+ OutputFormat2["PNG"] = "png";
935
+ return OutputFormat2;
936
+ })(OutputFormat || {});
937
+ var Theme = /* @__PURE__ */ ((Theme2) => {
938
+ Theme2["CORPORATE"] = "corporate";
939
+ return Theme2;
940
+ })(Theme || {});
941
+ var Orientation = /* @__PURE__ */ ((Orientation2) => {
942
+ Orientation2["PORTRAIT"] = "portrait";
943
+ Orientation2["LANDSCAPE"] = "landscape";
944
+ return Orientation2;
945
+ })(Orientation || {});
946
+ var PaperSizeEnum = /* @__PURE__ */ ((PaperSizeEnum2) => {
947
+ PaperSizeEnum2["A4"] = "A4";
948
+ PaperSizeEnum2["LETTER"] = "Letter";
949
+ PaperSizeEnum2["LEGAL"] = "Legal";
950
+ PaperSizeEnum2["A3"] = "A3";
951
+ PaperSizeEnum2["A5"] = "A5";
952
+ return PaperSizeEnum2;
953
+ })(PaperSizeEnum || {});
954
+ var SyntaxTheme = /* @__PURE__ */ ((SyntaxTheme2) => {
955
+ SyntaxTheme2["GITHUB_DARK"] = "github-dark";
956
+ SyntaxTheme2["GITHUB_LIGHT"] = "github-light";
957
+ SyntaxTheme2["DRACULA"] = "dracula";
958
+ SyntaxTheme2["MONOKAI"] = "monokai";
959
+ SyntaxTheme2["NORD"] = "nord";
960
+ return SyntaxTheme2;
961
+ })(SyntaxTheme || {});
962
+ var WatermarkPosition = /* @__PURE__ */ ((WatermarkPosition2) => {
963
+ WatermarkPosition2["DIAGONAL"] = "diagonal";
964
+ WatermarkPosition2["CENTER"] = "center";
965
+ WatermarkPosition2["TOP_RIGHT"] = "top-right";
966
+ WatermarkPosition2["BOTTOM_RIGHT"] = "bottom-right";
967
+ return WatermarkPosition2;
968
+ })(WatermarkPosition || {});
870
969
 
871
970
  // src/core/html/htmlThemes.ts
872
971
  var THEME_COMPONENTS = `
@@ -949,7 +1048,7 @@ hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
949
1048
  pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
950
1049
  }
951
1050
  `;
952
- var THEME_DEFAULT = `
1051
+ var THEME_CORPORATE = `
953
1052
  :root {
954
1053
  --mf-bg: #ffffff;
955
1054
  --mf-text: #0f172a;
@@ -961,49 +1060,415 @@ var THEME_DEFAULT = `
961
1060
  --mf-card-bg: #f8fafc;
962
1061
  --mf-code-bg: #0f172a;
963
1062
  --mf-code-text: #f8fafc;
964
- --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
1063
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
965
1064
  --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
966
1065
  }
967
1066
  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; }
968
- .document-container { max-width: 860px; margin: 0 auto; }
1067
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
969
1068
  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; }
970
- h1 { font-size: 2.2rem; border-bottom: 2px solid #33CDCF; padding-bottom: 0.5rem; }
971
- h2 { font-size: 1.6rem; color: #009DA0; border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
1069
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1070
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
972
1071
  h3 { font-size: 1.3rem; }
973
1072
  h4 { font-size: 1.1rem; }
974
1073
  p { margin: 0.8rem 0; }
975
1074
  `;
976
- var THEME_ACADEMIC = `
1075
+ var THEME_DEFAULT = THEME_CORPORATE;
1076
+ var THEMES = {
1077
+ corporate: THEME_CORPORATE,
1078
+ default: THEME_CORPORATE
1079
+ };
1080
+ function generateThemeCss(theme) {
1081
+ if (!theme || theme === "corporate" || theme === "default" || theme === "corporate" /* CORPORATE */) {
1082
+ return THEME_CORPORATE;
1083
+ }
1084
+ if (typeof theme === "object") {
1085
+ const bg = theme.backgroundColor || "#ffffff";
1086
+ const text = theme.textColor || "#0f172a";
1087
+ const textMuted = theme.textMuted || "#64748b";
1088
+ const primary = theme.primaryColor || "#33CDCF";
1089
+ const primaryDark = theme.primaryDark || primary;
1090
+ const primaryLight = theme.primaryLight || "#ECFDFD";
1091
+ const border = theme.borderColor || "#e2e8f0";
1092
+ const cardBg = theme.cardBackground || "#f8fafc";
1093
+ const codeBg = theme.codeBackground || "#0f172a";
1094
+ const codeText = theme.codeText || "#f8fafc";
1095
+ const font = theme.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
1096
+ const fontMono = theme.fontMono || "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
1097
+ return `
977
1098
  :root {
978
- --mf-bg: #ffffff;
979
- --mf-text: #1a1a1a;
980
- --mf-text-muted: #555;
981
- --mf-primary: #33CDCF;
982
- --mf-primary-dark: #009DA0;
983
- --mf-primary-light: #ECFDFD;
984
- --mf-border: #ccc;
985
- --mf-card-bg: #f9f9f9;
986
- --mf-code-bg: #1e1e1e;
987
- --mf-code-text: #d4d4d4;
988
- --mf-font-family: "Merriweather", "Georgia", "Times New Roman", serif;
989
- --mf-font-mono: "Courier New", Courier, monospace;
1099
+ --mf-bg: ${bg};
1100
+ --mf-text: ${text};
1101
+ --mf-text-muted: ${textMuted};
1102
+ --mf-primary: ${primary};
1103
+ --mf-primary-dark: ${primaryDark};
1104
+ --mf-primary-light: ${primaryLight};
1105
+ --mf-border: ${border};
1106
+ --mf-card-bg: ${cardBg};
1107
+ --mf-code-bg: ${codeBg};
1108
+ --mf-code-text: ${codeText};
1109
+ --mf-font-family: ${font};
1110
+ --mf-font-mono: ${fontMono};
990
1111
  }
991
- body { font-family: var(--mf-font-family); font-size: 16px; line-height: 1.8; padding: 3rem; color: var(--mf-text); }
992
- .document-container { max-width: 780px; margin: 0 auto; text-align: justify; }
993
- h1, h2, h3 { font-family: "Times New Roman", Times, serif; font-weight: bold; text-align: left; }
994
- h1 { font-size: 2rem; border-bottom: 1px solid #000; padding-bottom: 0.3rem; }
995
- h2 { font-size: 1.4rem; border-bottom: 1px solid #ccc; padding-bottom: 0.2rem; }
996
- h3 { font-size: 1.2rem; }
997
- p { margin: 0.9rem 0; }
1112
+ 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; }
1113
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1114
+ 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; }
1115
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1116
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
1117
+ h3 { font-size: 1.3rem; }
1118
+ h4 { font-size: 1.1rem; }
1119
+ p { margin: 0.8rem 0; }
1120
+ ${theme.customCss || ""}
998
1121
  `;
999
- var THEMES = {
1000
- default: THEME_DEFAULT,
1001
- academic: THEME_ACADEMIC,
1002
- github: THEME_DEFAULT,
1003
- corporate: THEME_DEFAULT,
1004
- minimal: THEME_DEFAULT,
1005
- dracula: THEME_DEFAULT
1122
+ }
1123
+ if (typeof theme === "string" && THEMES[theme]) {
1124
+ return THEMES[theme];
1125
+ }
1126
+ return THEME_CORPORATE;
1127
+ }
1128
+
1129
+ // src/config/loadConfig.ts
1130
+ var fs2 = __toESM(require("fs"));
1131
+ var path2 = __toESM(require("path"));
1132
+ var import_node_url = require("url");
1133
+ var YAML = __toESM(require("yaml"));
1134
+ var DEFAULT_CONFIG_FILENAMES = [
1135
+ "markforge.config.json",
1136
+ ".markforgerc.json",
1137
+ "markforge.config.yaml",
1138
+ "markforge.config.yml",
1139
+ ".markforgerc.yaml",
1140
+ ".markforgerc.yml",
1141
+ ".markforgerc",
1142
+ "markforge.config.ts",
1143
+ "markforge.config.js",
1144
+ "markforge.config.mjs",
1145
+ "markforge.config.cjs"
1146
+ ];
1147
+ var DEFAULT_CONFIG = {
1148
+ to: ["docx", "pdf"],
1149
+ outputDir: void 0,
1150
+ theme: "default",
1151
+ css: void 0,
1152
+ orientation: "portrait",
1153
+ paperSize: "A4",
1154
+ margins: {
1155
+ top: "2.5cm",
1156
+ bottom: "2.5cm",
1157
+ left: "2.5cm",
1158
+ right: "2.5cm"
1159
+ },
1160
+ header: void 0,
1161
+ footer: {
1162
+ right: "Page {page} of {pages}"
1163
+ },
1164
+ toc: false,
1165
+ watermark: void 0,
1166
+ embedImages: true,
1167
+ metadata: void 0,
1168
+ watch: false,
1169
+ serve: false,
1170
+ port: 4e3,
1171
+ open: false,
1172
+ bundleHtml: true,
1173
+ syntaxTheme: "github-dark"
1174
+ };
1175
+ function discoverConfigFile(startDir) {
1176
+ const dirsToCheck = [];
1177
+ let curr = path2.resolve(startDir);
1178
+ while (curr) {
1179
+ dirsToCheck.push(curr);
1180
+ const parent = path2.dirname(curr);
1181
+ if (parent === curr) break;
1182
+ curr = parent;
1183
+ }
1184
+ const cwd = path2.resolve(process.cwd());
1185
+ if (!dirsToCheck.includes(cwd)) {
1186
+ dirsToCheck.push(cwd);
1187
+ }
1188
+ for (const dir of dirsToCheck) {
1189
+ for (const filename of DEFAULT_CONFIG_FILENAMES) {
1190
+ const candidate = path2.join(dir, filename);
1191
+ if (fs2.existsSync(candidate) && fs2.statSync(candidate).isFile()) {
1192
+ return candidate;
1193
+ }
1194
+ }
1195
+ }
1196
+ return null;
1197
+ }
1198
+ async function loadConfig(customPath, startDir = process.cwd()) {
1199
+ let resolvedPath = null;
1200
+ if (customPath) {
1201
+ resolvedPath = path2.isAbsolute(customPath) ? customPath : path2.resolve(process.cwd(), customPath);
1202
+ if (!fs2.existsSync(resolvedPath)) {
1203
+ const altPath = path2.resolve(startDir, customPath);
1204
+ if (fs2.existsSync(altPath)) {
1205
+ resolvedPath = altPath;
1206
+ } else {
1207
+ throw new Error(`Configuration file not found: ${resolvedPath}`);
1208
+ }
1209
+ }
1210
+ } else {
1211
+ resolvedPath = discoverConfigFile(startDir);
1212
+ }
1213
+ if (!resolvedPath) {
1214
+ return {
1215
+ config: { ...DEFAULT_CONFIG },
1216
+ configPath: null
1217
+ };
1218
+ }
1219
+ const ext = path2.extname(resolvedPath).toLowerCase();
1220
+ let userConfig = {};
1221
+ try {
1222
+ if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
1223
+ const raw = fs2.readFileSync(resolvedPath, "utf-8").trim();
1224
+ try {
1225
+ userConfig = JSON.parse(raw);
1226
+ } catch {
1227
+ userConfig = YAML.parse(raw) || {};
1228
+ }
1229
+ } else if (ext === ".yaml" || ext === ".yml") {
1230
+ const raw = fs2.readFileSync(resolvedPath, "utf-8");
1231
+ userConfig = YAML.parse(raw) || {};
1232
+ } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
1233
+ try {
1234
+ const fileUrl = `${(0, import_node_url.pathToFileURL)(resolvedPath).href}?t=${Date.now()}`;
1235
+ const mod = await import(fileUrl);
1236
+ const rawExport = mod.default ?? mod.config ?? mod;
1237
+ userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
1238
+ } catch (importErr) {
1239
+ try {
1240
+ const mod = require(resolvedPath);
1241
+ const rawExport = mod.default ?? mod.config ?? mod;
1242
+ userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
1243
+ } catch {
1244
+ throw importErr;
1245
+ }
1246
+ }
1247
+ }
1248
+ } catch (err) {
1249
+ throw new Error(
1250
+ `Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
1251
+ );
1252
+ }
1253
+ if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
1254
+ delete userConfig.$schema;
1255
+ }
1256
+ const parsedConfig = userConfig;
1257
+ const mergedConfig = {
1258
+ ...DEFAULT_CONFIG,
1259
+ ...parsedConfig,
1260
+ margins: {
1261
+ ...DEFAULT_CONFIG.margins,
1262
+ ...parsedConfig.margins || {}
1263
+ },
1264
+ header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
1265
+ footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
1266
+ metadata: {
1267
+ ...DEFAULT_CONFIG.metadata || {},
1268
+ ...parsedConfig.metadata || {}
1269
+ }
1270
+ };
1271
+ return {
1272
+ config: mergedConfig,
1273
+ configPath: resolvedPath
1274
+ };
1275
+ }
1276
+
1277
+ // src/config/resolveConfig.ts
1278
+ var PAPER_DIMENSIONS_TWIP = {
1279
+ A4: { width: 11906, height: 16838 },
1280
+ // 210mm x 297mm
1281
+ Letter: { width: 12240, height: 15840 },
1282
+ // 8.5in x 11in
1283
+ Legal: { width: 12240, height: 20160 },
1284
+ // 8.5in x 14in
1285
+ A3: { width: 16838, height: 23811 },
1286
+ // 297mm x 420mm
1287
+ A5: { width: 8390, height: 11906 }
1288
+ // 148mm x 210mm
1006
1289
  };
1290
+ function parseMarginToTwip(margin, defaultTwip = 1440) {
1291
+ if (typeof margin === "number") return margin;
1292
+ if (!margin) return defaultTwip;
1293
+ const str = margin.trim().toLowerCase();
1294
+ if (str.endsWith("cm")) {
1295
+ const cm = parseFloat(str);
1296
+ return isNaN(cm) ? defaultTwip : Math.round(cm * 566.929);
1297
+ }
1298
+ if (str.endsWith("mm")) {
1299
+ const mm = parseFloat(str);
1300
+ return isNaN(mm) ? defaultTwip : Math.round(mm * 56.6929);
1301
+ }
1302
+ if (str.endsWith("in") || str.endsWith("inch")) {
1303
+ const inch = parseFloat(str);
1304
+ return isNaN(inch) ? defaultTwip : Math.round(inch * 1440);
1305
+ }
1306
+ if (str.endsWith("pt")) {
1307
+ const pt = parseFloat(str);
1308
+ return isNaN(pt) ? defaultTwip : Math.round(pt * 20);
1309
+ }
1310
+ const val = parseFloat(str);
1311
+ return isNaN(val) ? defaultTwip : Math.round(val);
1312
+ }
1313
+ function formatMarginCss(margin, defaultCss = "2.5cm") {
1314
+ if (margin === void 0 || margin === null) return defaultCss;
1315
+ if (typeof margin === "number") return `${margin}pt`;
1316
+ const str = margin.trim();
1317
+ if (!str) return defaultCss;
1318
+ if (/^[0-9.]+$/.test(str)) return `${str}pt`;
1319
+ return str;
1320
+ }
1321
+ function replaceDocumentTokens(template = "", meta) {
1322
+ 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(/\{company\}/gi, meta.company || "");
1323
+ }
1324
+ function normalizeWatermark(rawWatermark) {
1325
+ if (!rawWatermark) {
1326
+ return void 0;
1327
+ }
1328
+ if (typeof rawWatermark === "string") {
1329
+ const text = rawWatermark.trim();
1330
+ if (!text) return void 0;
1331
+ return {
1332
+ text,
1333
+ color: "#94a3b8",
1334
+ opacity: 0.08,
1335
+ fontSize: 54,
1336
+ rotate: -45,
1337
+ position: "diagonal"
1338
+ };
1339
+ }
1340
+ if (typeof rawWatermark === "object") {
1341
+ if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
1342
+ return {
1343
+ text: rawWatermark.text.trim(),
1344
+ color: rawWatermark.color || "#94a3b8",
1345
+ opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
1346
+ fontSize: rawWatermark.fontSize || 54,
1347
+ rotate: typeof rawWatermark.rotate === "number" ? rawWatermark.rotate : -45,
1348
+ position: rawWatermark.position || "diagonal"
1349
+ };
1350
+ }
1351
+ return void 0;
1352
+ }
1353
+ function normalizeHeaderFooterSlot(rawSlot, parent, meta) {
1354
+ if (!rawSlot) return void 0;
1355
+ if (typeof rawSlot === "string") {
1356
+ const text = replaceDocumentTokens(rawSlot, meta).trim();
1357
+ if (!text) return void 0;
1358
+ return {
1359
+ text,
1360
+ color: (parent == null ? void 0 : parent.color) || "#94A3B8",
1361
+ fontSize: (parent == null ? void 0 : parent.size) || 9,
1362
+ fontFamily: (parent == null ? void 0 : parent.font) || "Segoe UI",
1363
+ bold: false,
1364
+ italic: false
1365
+ };
1366
+ }
1367
+ if (typeof rawSlot === "object") {
1368
+ const text = replaceDocumentTokens(rawSlot.text || "", meta).trim();
1369
+ if (!text) return void 0;
1370
+ return {
1371
+ text,
1372
+ color: rawSlot.color || (parent == null ? void 0 : parent.color) || "#94A3B8",
1373
+ fontSize: rawSlot.fontSize || (parent == null ? void 0 : parent.size) || 9,
1374
+ fontFamily: rawSlot.fontFamily || (parent == null ? void 0 : parent.font) || "Segoe UI",
1375
+ bold: Boolean(rawSlot.bold),
1376
+ italic: Boolean(rawSlot.italic)
1377
+ };
1378
+ }
1379
+ return void 0;
1380
+ }
1381
+ function normalizeHeaderFooter(raw, meta) {
1382
+ if (!raw) return void 0;
1383
+ const left = normalizeHeaderFooterSlot(raw.left, raw, meta);
1384
+ const center = normalizeHeaderFooterSlot(raw.center, raw, meta);
1385
+ const right = normalizeHeaderFooterSlot(raw.right, raw, meta);
1386
+ if (!left && !center && !right) return void 0;
1387
+ return {
1388
+ left,
1389
+ center,
1390
+ right,
1391
+ font: raw.font || "Segoe UI",
1392
+ size: raw.size || 9,
1393
+ color: raw.color || "#94A3B8",
1394
+ divider: Boolean(raw.divider),
1395
+ dividerColor: raw.dividerColor || "#E2E8F0"
1396
+ };
1397
+ }
1398
+ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1399
+ const configMeta = userConfig.metadata || {};
1400
+ const mergedMeta = { ...configMeta, ...frontmatter };
1401
+ const title = mergedMeta.title || "MarkForge Document";
1402
+ const subtitle = mergedMeta.subtitle || void 0;
1403
+ const author = Array.isArray(mergedMeta.author) ? mergedMeta.author.join(", ") : mergedMeta.author || void 0;
1404
+ const date = mergedMeta.date || void 0;
1405
+ const version = mergedMeta.version || void 0;
1406
+ const company = mergedMeta.company || void 0;
1407
+ const lang = mergedMeta.lang || "en";
1408
+ const tokenContext = { title, subtitle, author, version, date, company };
1409
+ const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
1410
+ const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
1411
+ const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
1412
+ const baseDim = PAPER_DIMENSIONS_TWIP[paperSize] || PAPER_DIMENSIONS_TWIP.A4;
1413
+ const paperDimensions = orientation === "landscape" ? { widthTwip: baseDim.height, heightTwip: baseDim.width } : { widthTwip: baseDim.width, heightTwip: baseDim.height };
1414
+ const fmMargin = mergedMeta.margins || {};
1415
+ const cfgMargin = userConfig.margins || {};
1416
+ const defMargin = DEFAULT_CONFIG.margins || {};
1417
+ const topRaw = fmMargin.top ?? cfgMargin.top ?? defMargin.top ?? "2.5cm";
1418
+ const bottomRaw = fmMargin.bottom ?? cfgMargin.bottom ?? defMargin.bottom ?? "2.5cm";
1419
+ const leftRaw = fmMargin.left ?? cfgMargin.left ?? defMargin.left ?? "2.5cm";
1420
+ const rightRaw = fmMargin.right ?? cfgMargin.right ?? defMargin.right ?? "2.5cm";
1421
+ const margins = {
1422
+ top: formatMarginCss(topRaw),
1423
+ bottom: formatMarginCss(bottomRaw),
1424
+ left: formatMarginCss(leftRaw),
1425
+ right: formatMarginCss(rightRaw),
1426
+ topTwip: parseMarginToTwip(topRaw),
1427
+ bottomTwip: parseMarginToTwip(bottomRaw),
1428
+ leftTwip: parseMarginToTwip(leftRaw),
1429
+ rightTwip: parseMarginToTwip(rightRaw)
1430
+ };
1431
+ const rawHeader = mergedMeta.header || userConfig.header || DEFAULT_CONFIG.header;
1432
+ const rawFooter = mergedMeta.footer || userConfig.footer || DEFAULT_CONFIG.footer;
1433
+ const header = normalizeHeaderFooter(rawHeader, tokenContext);
1434
+ const footer = normalizeHeaderFooter(rawFooter, tokenContext);
1435
+ const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
1436
+ const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
1437
+ const watermark = normalizeWatermark(rawWatermark);
1438
+ const cssList = [];
1439
+ const addCss = (item) => {
1440
+ if (!item) return;
1441
+ if (Array.isArray(item)) cssList.push(...item);
1442
+ else cssList.push(item);
1443
+ };
1444
+ addCss(userConfig.css);
1445
+ addCss(mergedMeta.css);
1446
+ const embedImages = typeof userConfig.embedImages === "boolean" ? userConfig.embedImages : DEFAULT_CONFIG.embedImages;
1447
+ const bundleHtml = typeof userConfig.bundleHtml === "boolean" ? userConfig.bundleHtml : DEFAULT_CONFIG.bundleHtml;
1448
+ const syntaxTheme = userConfig.syntaxTheme || DEFAULT_CONFIG.syntaxTheme || "github-dark";
1449
+ return {
1450
+ title,
1451
+ subtitle,
1452
+ author,
1453
+ date,
1454
+ version,
1455
+ company,
1456
+ lang,
1457
+ theme,
1458
+ orientation,
1459
+ paperSize,
1460
+ paperDimensions,
1461
+ margins,
1462
+ header,
1463
+ footer,
1464
+ toc,
1465
+ watermark,
1466
+ css: cssList,
1467
+ embedImages,
1468
+ bundleHtml,
1469
+ syntaxTheme
1470
+ };
1471
+ }
1007
1472
 
1008
1473
  // src/core/html/htmlBuilder.ts
1009
1474
  function escapeHtml(str) {
@@ -1056,43 +1521,41 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1056
1521
  return result;
1057
1522
  }
1058
1523
  async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1059
- var _a;
1060
- const metadata = { ...config.metadata, ...doc.metadata };
1061
- const themeName = metadata.theme || config.theme || "default";
1062
- const baseThemeCss = THEMES[themeName] || THEMES.default;
1524
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1525
+ const baseThemeCss = generateThemeCss(resolved.theme);
1063
1526
  let customCss = "";
1064
- if (config.css) {
1065
- const cssList = Array.isArray(config.css) ? config.css : [config.css];
1066
- for (const cssPath of cssList) {
1067
- const fullCssPath = path2.isAbsolute(cssPath) ? cssPath : path2.resolve(baseDir, cssPath);
1068
- if (fs2.existsSync(fullCssPath)) {
1069
- customCss += `
1527
+ for (const cssPath of resolved.css) {
1528
+ const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
1529
+ if (fs3.existsSync(fullCssPath)) {
1530
+ customCss += `
1070
1531
  /* Custom CSS: ${cssPath} */
1071
- ` + fs2.readFileSync(fullCssPath, "utf-8");
1072
- }
1532
+ ` + fs3.readFileSync(fullCssPath, "utf-8");
1073
1533
  }
1074
1534
  }
1075
1535
  const inlinedCss = doc.inlinedStyles.join("\n");
1076
1536
  let bodyHtml = "";
1077
- if (metadata.title) {
1537
+ if (resolved.title) {
1078
1538
  bodyHtml += ` <header class="document-header">
1079
1539
  `;
1080
- bodyHtml += ` <h1 class="document-title">${escapeHtml(metadata.title)}</h1>
1540
+ bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
1081
1541
  `;
1082
- if (metadata.subtitle) {
1083
- bodyHtml += ` <div class="document-subtitle">${escapeHtml(metadata.subtitle)}</div>
1542
+ if (resolved.subtitle) {
1543
+ bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
1084
1544
  `;
1085
1545
  }
1086
- if (metadata.author || metadata.date) {
1546
+ if (resolved.author || resolved.date || resolved.version) {
1087
1547
  bodyHtml += ` <div class="document-meta">
1088
1548
  `;
1089
- if (metadata.author) {
1090
- const authors = Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author;
1091
- bodyHtml += ` <span>Author: ${escapeHtml(authors)}</span>
1549
+ if (resolved.author) {
1550
+ bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
1551
+ `;
1552
+ }
1553
+ if (resolved.version) {
1554
+ bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
1092
1555
  `;
1093
1556
  }
1094
- if (metadata.date) {
1095
- bodyHtml += ` <span>Date: ${escapeHtml(metadata.date)}</span>
1557
+ if (resolved.date) {
1558
+ bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
1096
1559
  `;
1097
1560
  }
1098
1561
  bodyHtml += ` </div>
@@ -1101,22 +1564,20 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1101
1564
  bodyHtml += ` </header>
1102
1565
  `;
1103
1566
  }
1104
- if (config.toc || metadata.toc) {
1105
- if (doc.tocEntries.length > 0) {
1106
- bodyHtml += ` <nav class="table-of-contents">
1567
+ if (resolved.toc && doc.tocEntries.length > 0) {
1568
+ bodyHtml += ` <nav class="table-of-contents">
1107
1569
  `;
1108
- bodyHtml += ` <h2>Table of Contents</h2>
1570
+ bodyHtml += ` <h2>Table of Contents</h2>
1109
1571
  <ul>
1110
1572
  `;
1111
- for (const entry of doc.tocEntries) {
1112
- const indent = " ".repeat(entry.level);
1113
- bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1573
+ for (const entry of doc.tocEntries) {
1574
+ const indent = " ".repeat(entry.level);
1575
+ bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1114
1576
  `;
1115
- }
1116
- bodyHtml += ` </ul>
1577
+ }
1578
+ bodyHtml += ` </ul>
1117
1579
  </nav>
1118
1580
  `;
1119
- }
1120
1581
  }
1121
1582
  for (const node of doc.nodes) {
1122
1583
  if (node.type === "heading") {
@@ -1134,7 +1595,7 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1134
1595
  if (node.type === "codeBlock") {
1135
1596
  const lang = node.language || "";
1136
1597
  const langClass = lang ? ` class="language-${escapeHtml(lang)}"` : "";
1137
- const highlighted = highlightCodeToHtml(node.text || "", lang);
1598
+ const highlighted = highlightCodeToHtml(node.text || "", lang, resolved.syntaxTheme);
1138
1599
  bodyHtml += ` <pre><code${langClass}>${highlighted}</code></pre>
1139
1600
  `;
1140
1601
  continue;
@@ -1178,15 +1639,12 @@ ${escapeHtml(node.text || "")}
1178
1639
  for (const row of node.children) {
1179
1640
  bodyHtml += ` <tr>
1180
1641
  `;
1181
- if (row.children) {
1182
- for (let colIdx = 0; colIdx < row.children.length; colIdx++) {
1183
- const cell = row.children[colIdx];
1184
- const tag = row.isHeader ? "th" : "td";
1185
- const align = ((_a = node.align) == null ? void 0 : _a[colIdx]) ? ` style="text-align: ${node.align[colIdx]}"` : "";
1186
- const cellInner = await renderInlinesToHtml(cell.inlines, baseDir);
1187
- bodyHtml += ` <${tag}${align}>${cellInner}</${tag}>
1642
+ for (const cell of row.children || []) {
1643
+ const tag = cell.isHeader ? "th" : "td";
1644
+ const align = cell.align ? ` align="${cell.align}"` : "";
1645
+ const inner = await renderInlinesToHtml(cell.inlines, baseDir);
1646
+ bodyHtml += ` <${tag}${align}>${inner}</${tag}>
1188
1647
  `;
1189
- }
1190
1648
  }
1191
1649
  bodyHtml += ` </tr>
1192
1650
  `;
@@ -1200,57 +1658,52 @@ ${escapeHtml(node.text || "")}
1200
1658
  bodyHtml += ` <${tag}>
1201
1659
  `;
1202
1660
  for (const item of node.children) {
1203
- const itemInner = await renderInlinesToHtml(item.inlines, baseDir);
1204
- let prefix = "";
1205
- if (item.checked !== void 0) {
1206
- prefix = `<input type="checkbox" disabled ${item.checked ? "checked" : ""}/> `;
1207
- }
1208
- bodyHtml += ` <li>${prefix}${itemInner}</li>
1661
+ const inner = await renderInlinesToHtml(item.inlines, baseDir);
1662
+ bodyHtml += ` <li>${inner}</li>
1209
1663
  `;
1210
1664
  }
1211
1665
  bodyHtml += ` </${tag}>
1212
1666
  `;
1213
1667
  continue;
1214
1668
  }
1215
- if (node.type === "htmlBlock") {
1216
- bodyHtml += ` ${node.rawHtml}
1669
+ if (node.type === "thematicBreak") {
1670
+ bodyHtml += ` <hr />
1217
1671
  `;
1218
1672
  continue;
1219
1673
  }
1220
- if (node.type === "thematicBreak") {
1221
- bodyHtml += ` <hr />
1674
+ if (node.type === "htmlBlock") {
1675
+ bodyHtml += ` ${node.text}
1222
1676
  `;
1223
1677
  continue;
1224
1678
  }
1225
1679
  }
1226
- const wmConfig = config.watermark ?? metadata.watermark;
1227
- let watermarkHtml = "";
1228
1680
  let watermarkCss = "";
1229
- if (wmConfig) {
1230
- const wmText = typeof wmConfig === "string" ? wmConfig : wmConfig.text;
1231
- const opacity = typeof wmConfig === "object" && wmConfig.opacity !== void 0 ? wmConfig.opacity : 0.12;
1232
- const rotate = typeof wmConfig === "object" && wmConfig.rotate !== void 0 ? wmConfig.rotate : -45;
1233
- const color = typeof wmConfig === "object" && wmConfig.color ? wmConfig.color : "#94A3B8";
1234
- const fontSize = typeof wmConfig === "object" && wmConfig.fontSize ? `${wmConfig.fontSize}pt` : "52pt";
1681
+ let watermarkHtml = "";
1682
+ if (resolved.watermark) {
1683
+ const wm = resolved.watermark;
1235
1684
  watermarkCss = `
1236
1685
  .document-watermark {
1237
1686
  position: fixed;
1238
1687
  top: 50%;
1239
1688
  left: 50%;
1240
- transform: translate(-50%, -50%) rotate(${rotate}deg);
1241
- font-size: ${fontSize};
1242
- font-weight: 800;
1243
- color: ${color};
1244
- opacity: ${opacity};
1689
+ transform: translate(-50%, -50%) rotate(${wm.rotate}deg);
1690
+ font-size: ${wm.fontSize}pt;
1691
+ font-weight: 900;
1692
+ color: ${wm.color};
1693
+ opacity: ${wm.opacity};
1245
1694
  pointer-events: none;
1695
+ z-index: 0;
1246
1696
  user-select: none;
1247
- z-index: 9999;
1248
1697
  text-transform: uppercase;
1249
1698
  letter-spacing: 0.15em;
1250
1699
  white-space: nowrap;
1251
1700
  }
1701
+ .document-container {
1702
+ position: relative;
1703
+ z-index: 1;
1704
+ }
1252
1705
  `;
1253
- watermarkHtml = ` <div class="document-watermark">${escapeHtml(wmText)}</div>
1706
+ watermarkHtml = ` <div class="document-watermark">${escapeHtml(wm.text)}</div>
1254
1707
  `;
1255
1708
  }
1256
1709
  const hasMermaid = doc.nodes.some((n) => n.type === "mermaid");
@@ -1269,13 +1722,12 @@ ${escapeHtml(node.text || "")}
1269
1722
  }
1270
1723
  });
1271
1724
  </script>` : "";
1272
- const documentTitle = metadata.title || "MarkForge Document";
1273
1725
  return `<!DOCTYPE html>
1274
- <html lang="${metadata.lang || "en"}">
1726
+ <html lang="${resolved.lang}">
1275
1727
  <head>
1276
1728
  <meta charset="UTF-8">
1277
1729
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1278
- <title>${escapeHtml(documentTitle)}</title>
1730
+ <title>${escapeHtml(resolved.title)}</title>
1279
1731
  <style>
1280
1732
  ${THEME_COMPONENTS}
1281
1733
  ${baseThemeCss}
@@ -1294,10 +1746,10 @@ ${bodyHtml} </div>
1294
1746
 
1295
1747
  // src/core/pdf/pdfBuilder.ts
1296
1748
  function findChromeExecutable() {
1297
- if (process.env.CHROME_PATH && fs3.existsSync(process.env.CHROME_PATH)) {
1749
+ if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
1298
1750
  return process.env.CHROME_PATH;
1299
1751
  }
1300
- if (process.env.PUPPETEER_EXECUTABLE_PATH && fs3.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
1752
+ if (process.env.PUPPETEER_EXECUTABLE_PATH && fs4.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
1301
1753
  return process.env.PUPPETEER_EXECUTABLE_PATH;
1302
1754
  }
1303
1755
  const isWin = process.platform === "win32";
@@ -1319,14 +1771,14 @@ function findChromeExecutable() {
1319
1771
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
1320
1772
  "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
1321
1773
  "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
1774
+ // Windows — Microsoft Edge (Native Windows 10/11 browser, enterprise whitelist friendly)
1775
+ `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
1776
+ `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
1777
+ `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
1322
1778
  // Windows — Google Chrome
1323
1779
  `${winProgramFiles}\\Google\\Chrome\\Application\\chrome.exe`,
1324
1780
  `${winProgramFilesX86}\\Google\\Chrome\\Application\\chrome.exe`,
1325
1781
  `${winLocalAppData}\\Google\\Chrome\\Application\\chrome.exe`,
1326
- // Windows — Microsoft Edge (ships with Windows 10/11)
1327
- `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
1328
- `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
1329
- `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
1330
1782
  // Windows — Brave Browser
1331
1783
  `${winProgramFiles}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
1332
1784
  `${winProgramFilesX86}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
@@ -1336,7 +1788,7 @@ function findChromeExecutable() {
1336
1788
  ].filter(Boolean);
1337
1789
  for (const candidate of candidates) {
1338
1790
  try {
1339
- if (fs3.existsSync(candidate)) {
1791
+ if (fs4.existsSync(candidate)) {
1340
1792
  return candidate;
1341
1793
  }
1342
1794
  } catch {
@@ -1349,7 +1801,7 @@ function findChromeExecutable() {
1349
1801
  const res = (0, import_node_child_process.spawnSync)(cmd, [name], { encoding: "utf-8" });
1350
1802
  if (res.status === 0 && res.stdout.trim()) {
1351
1803
  const binPath = res.stdout.split(/\r?\n/)[0].trim();
1352
- if (fs3.existsSync(binPath)) return binPath;
1804
+ if (fs4.existsSync(binPath)) return binPath;
1353
1805
  }
1354
1806
  }
1355
1807
  } catch {
@@ -1357,22 +1809,50 @@ function findChromeExecutable() {
1357
1809
  return null;
1358
1810
  }
1359
1811
  function injectPagedMediaStyles(html, config, metadata) {
1360
- var _a, _b, _c, _d;
1361
- const merged = { ...config.metadata, ...metadata };
1362
- const orientation = merged.orientation || config.orientation || "portrait";
1363
- const size = merged.paperSize || config.paperSize || "A4";
1364
- const margins = merged.margins || config.margins || {};
1365
- const top = margins.top || ((_a = config.margins) == null ? void 0 : _a.top) || "2.5cm";
1366
- const bottom = margins.bottom || ((_b = config.margins) == null ? void 0 : _b.bottom) || "2.5cm";
1367
- const left = margins.left || ((_c = config.margins) == null ? void 0 : _c.left) || "2.5cm";
1368
- const right = margins.right || ((_d = config.margins) == null ? void 0 : _d.right) || "2.5cm";
1369
- const headerCfg = merged.header;
1370
- const footerCfg = merged.footer;
1371
- const headerLeft = (headerCfg == null ? void 0 : headerCfg.left) ?? "";
1372
- const headerCenter = (headerCfg == null ? void 0 : headerCfg.center) ?? "";
1373
- const headerRight = (headerCfg == null ? void 0 : headerCfg.right) ?? "";
1374
- const footerLeft = ((footerCfg == null ? void 0 : footerCfg.left) ?? "").replace("{page}", "").replace("{pages}", "").trim();
1375
- const esc = (s) => s.replace(/"/g, '"').replace(/\\/g, "\\\\");
1812
+ var _a, _b, _c, _d, _e, _f;
1813
+ const resolved = resolveDocumentConfig(metadata || {}, config);
1814
+ const size = resolved.paperSize;
1815
+ const orientation = resolved.orientation;
1816
+ const top = resolved.margins.top;
1817
+ const bottom = resolved.margins.bottom;
1818
+ const left = resolved.margins.left;
1819
+ const right = resolved.margins.right;
1820
+ const esc = (s) => s.replace(/"/g, '\\"').replace(/\\/g, "\\\\");
1821
+ const buildZoneCss = (pos, zone, isPageCounter = false) => {
1822
+ if (!zone && !isPageCounter) return "";
1823
+ const color = (zone == null ? void 0 : zone.color) || "#94a3b8";
1824
+ const fontSize = (zone == null ? void 0 : zone.fontSize) ? `${zone.fontSize}pt` : "9pt";
1825
+ const fontFamily = (zone == null ? void 0 : zone.fontFamily) ? `font-family: ${zone.fontFamily};` : "";
1826
+ const fontWeight = (zone == null ? void 0 : zone.bold) ? "font-weight: bold;" : "";
1827
+ const fontStyle = (zone == null ? void 0 : zone.italic) ? "font-style: italic;" : "";
1828
+ let content = "";
1829
+ if (isPageCounter) {
1830
+ if ((zone == null ? void 0 : zone.text) && (zone.text.includes("{page}") || zone.text.includes("{pages}"))) {
1831
+ const parts = zone.text.split(/(\{page\}|\{pages\})/gi);
1832
+ const cssParts = parts.map((part) => {
1833
+ if (part.toLowerCase() === "{page}") return "counter(page)";
1834
+ if (part.toLowerCase() === "{pages}") return "counter(pages)";
1835
+ return `"${esc(part)}"`;
1836
+ });
1837
+ content = cssParts.join(" ");
1838
+ } else if (zone == null ? void 0 : zone.text) {
1839
+ content = `"${esc(zone.text)}"`;
1840
+ } else {
1841
+ content = `"Page " counter(page) " of " counter(pages)`;
1842
+ }
1843
+ } else if (zone == null ? void 0 : zone.text) {
1844
+ content = `"${esc(zone.text)}"`;
1845
+ }
1846
+ if (!content) return "";
1847
+ return `@${pos} {
1848
+ content: ${content};
1849
+ font-size: ${fontSize};
1850
+ color: ${color};
1851
+ ${fontFamily}
1852
+ ${fontWeight}
1853
+ ${fontStyle}
1854
+ }`;
1855
+ };
1376
1856
  const pagedCss = `
1377
1857
  @page {
1378
1858
  size: ${size} ${orientation};
@@ -1380,15 +1860,12 @@ function injectPagedMediaStyles(html, config, metadata) {
1380
1860
  margin-bottom: ${bottom};
1381
1861
  margin-left: ${left};
1382
1862
  margin-right: ${right};
1383
- ${headerLeft ? `@top-left { content: "${esc(headerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1384
- ${headerCenter ? `@top-center { content: "${esc(headerCenter)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1385
- ${headerRight ? `@top-right { content: "${esc(headerRight)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1386
- ${footerLeft ? `@bottom-left { content: "${esc(footerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1387
- @bottom-right {
1388
- content: "Page " counter(page) " of " counter(pages);
1389
- font-size: 9pt;
1390
- color: #94a3b8;
1391
- }
1863
+ ${buildZoneCss("top-left", (_a = resolved.header) == null ? void 0 : _a.left)}
1864
+ ${buildZoneCss("top-center", (_b = resolved.header) == null ? void 0 : _b.center)}
1865
+ ${buildZoneCss("top-right", (_c = resolved.header) == null ? void 0 : _c.right)}
1866
+ ${buildZoneCss("bottom-left", (_d = resolved.footer) == null ? void 0 : _d.left)}
1867
+ ${buildZoneCss("bottom-center", (_e = resolved.footer) == null ? void 0 : _e.center)}
1868
+ ${buildZoneCss("bottom-right", (_f = resolved.footer) == null ? void 0 : _f.right, true)}
1392
1869
  }
1393
1870
  @media print {
1394
1871
  body { padding: 0; }
@@ -1445,56 +1922,72 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1445
1922
  if (chromePath) {
1446
1923
  const tmpId = Math.random().toString(36).substring(2, 9);
1447
1924
  const tmpDir = os.tmpdir();
1448
- const tmpHtml = path3.join(tmpDir, `markforge_${tmpId}.html`);
1449
- const tmpPdf = path3.join(tmpDir, `markforge_${tmpId}.pdf`);
1925
+ const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
1926
+ const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
1927
+ const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
1928
+ const isolatedFlags = [
1929
+ `--user-data-dir=${tmpProfile}`,
1930
+ "--no-first-run",
1931
+ "--no-default-browser-check",
1932
+ "--disable-sync",
1933
+ "--disable-background-networking",
1934
+ "--disable-component-update",
1935
+ "--disable-default-apps",
1936
+ "--disable-extensions",
1937
+ "--disable-domain-reliability",
1938
+ "--disable-client-side-phishing-detection",
1939
+ "--disable-breakpad",
1940
+ "--disable-component-extensions-with-background-pages",
1941
+ "--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
1942
+ "--password-store=basic",
1943
+ "--use-mock-keychain",
1944
+ "--mute-audio",
1945
+ "--no-service-autorun",
1946
+ "--disable-gpu",
1947
+ "--no-sandbox",
1948
+ "--disable-setuid-sandbox",
1949
+ "--allow-file-access-from-files",
1950
+ "--disable-web-security",
1951
+ "--force-color-profile=srgb",
1952
+ "--no-pdf-header-footer"
1953
+ ];
1450
1954
  try {
1451
- fs3.writeFileSync(tmpHtml, pagedHtml, "utf-8");
1452
- const fileUrl = (0, import_node_url.pathToFileURL)(tmpHtml).href;
1955
+ fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
1956
+ const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
1453
1957
  let res = (0, import_node_child_process.spawnSync)(
1454
1958
  chromePath,
1455
1959
  [
1456
1960
  "--headless=new",
1457
- "--disable-gpu",
1458
- "--no-sandbox",
1459
- "--disable-setuid-sandbox",
1460
- "--allow-file-access-from-files",
1461
- "--disable-web-security",
1462
- "--force-color-profile=srgb",
1961
+ ...isolatedFlags,
1463
1962
  "--run-all-compositor-stages-before-draw",
1464
1963
  "--virtual-time-budget=8000",
1465
- "--no-pdf-header-footer",
1466
1964
  `--print-to-pdf=${tmpPdf}`,
1467
1965
  fileUrl
1468
1966
  ],
1469
1967
  { timeout: 3e4 }
1470
1968
  );
1471
- if ((res.status !== 0 || !fs3.existsSync(tmpPdf)) && chromePath) {
1969
+ if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
1472
1970
  res = (0, import_node_child_process.spawnSync)(
1473
1971
  chromePath,
1474
1972
  [
1475
1973
  "--headless",
1476
- "--disable-gpu",
1477
- "--no-sandbox",
1478
- "--disable-setuid-sandbox",
1479
- "--allow-file-access-from-files",
1480
- "--disable-web-security",
1481
- "--force-color-profile=srgb",
1482
- "--no-pdf-header-footer",
1974
+ ...isolatedFlags,
1483
1975
  `--print-to-pdf=${tmpPdf}`,
1484
1976
  fileUrl
1485
1977
  ],
1486
1978
  { timeout: 3e4 }
1487
1979
  );
1488
1980
  }
1489
- if (fs3.existsSync(tmpPdf) && fs3.statSync(tmpPdf).size > 0) {
1490
- const pdfBuffer = fs3.readFileSync(tmpPdf);
1981
+ if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
1982
+ const pdfBuffer = fs4.readFileSync(tmpPdf);
1491
1983
  return pdfBuffer;
1492
1984
  }
1493
1985
  } catch {
1494
1986
  } finally {
1495
1987
  try {
1496
- if (fs3.existsSync(tmpHtml)) fs3.unlinkSync(tmpHtml);
1497
- if (fs3.existsSync(tmpPdf)) fs3.unlinkSync(tmpPdf);
1988
+ if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
1989
+ if (fs4.existsSync(tmpPdf)) fs4.unlinkSync(tmpPdf);
1990
+ if (fs4.existsSync(tmpProfile)) fs4.rmSync(tmpProfile, { recursive: true, force: true });
1498
1991
  } catch {
1499
1992
  }
1500
1993
  }
@@ -1510,8 +2003,8 @@ async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
1510
2003
  }
1511
2004
  const tmpId = Math.random().toString(36).substring(2, 9);
1512
2005
  const tmpDir = os2.tmpdir();
1513
- const tmpHtml = path4.join(tmpDir, `mermaid_${tmpId}.html`);
1514
- const tmpScreenshot = path4.join(tmpDir, `mermaid_${tmpId}.png`);
2006
+ const tmpHtml = path5.join(tmpDir, `mermaid_${tmpId}.html`);
2007
+ const tmpScreenshot = path5.join(tmpDir, `mermaid_${tmpId}.png`);
1515
2008
  const htmlContent = `<!DOCTYPE html>
1516
2009
  <html>
1517
2010
  <head>
@@ -1550,8 +2043,8 @@ ${mermaidCode}
1550
2043
  </body>
1551
2044
  </html>`;
1552
2045
  try {
1553
- fs4.writeFileSync(tmpHtml, htmlContent, "utf-8");
1554
- const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
2046
+ fs5.writeFileSync(tmpHtml, htmlContent, "utf-8");
2047
+ const fileUrl = (0, import_node_url3.pathToFileURL)(tmpHtml).href;
1555
2048
  const res = (0, import_node_child_process2.spawnSync)(
1556
2049
  chromePath,
1557
2050
  [
@@ -1569,15 +2062,15 @@ ${mermaidCode}
1569
2062
  ],
1570
2063
  { timeout: 15e3 }
1571
2064
  );
1572
- if (res.status === 0 && fs4.existsSync(tmpScreenshot)) {
1573
- const buffer = fs4.readFileSync(tmpScreenshot);
2065
+ if (res.status === 0 && fs5.existsSync(tmpScreenshot)) {
2066
+ const buffer = fs5.readFileSync(tmpScreenshot);
1574
2067
  return buffer;
1575
2068
  }
1576
2069
  } catch {
1577
2070
  } finally {
1578
2071
  try {
1579
- if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
1580
- if (fs4.existsSync(tmpScreenshot)) fs4.unlinkSync(tmpScreenshot);
2072
+ if (fs5.existsSync(tmpHtml)) fs5.unlinkSync(tmpHtml);
2073
+ if (fs5.existsSync(tmpScreenshot)) fs5.unlinkSync(tmpScreenshot);
1581
2074
  } catch {
1582
2075
  }
1583
2076
  }
@@ -1585,7 +2078,7 @@ ${mermaidCode}
1585
2078
  }
1586
2079
 
1587
2080
  // src/core/docx/docxBuilder.ts
1588
- function parseMarginToTwip(margin, defaultTwip = 1440) {
2081
+ function parseMarginToTwip2(margin, defaultTwip = 1440) {
1589
2082
  if (typeof margin === "number") return margin;
1590
2083
  if (!margin) return defaultTwip;
1591
2084
  const str = margin.trim().toLowerCase();
@@ -1608,7 +2101,7 @@ function parseMarginToTwip(margin, defaultTwip = 1440) {
1608
2101
  const val = parseFloat(str);
1609
2102
  return isNaN(val) ? defaultTwip : Math.round(val);
1610
2103
  }
1611
- async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
2104
+ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), options = {}) {
1612
2105
  const runs = [];
1613
2106
  for (const span of spans) {
1614
2107
  if (span.type === "image" && span.url) {
@@ -1638,7 +2131,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1638
2131
  new import_docx.TextRun({
1639
2132
  text: span.content,
1640
2133
  style: "Hyperlink",
1641
- color: "0969DA",
2134
+ color: "009DA0",
2135
+ font: options.font,
2136
+ size: options.size,
2137
+ bold: options.bold,
1642
2138
  underline: {}
1643
2139
  })
1644
2140
  ],
@@ -1651,7 +2147,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1651
2147
  runs.push(
1652
2148
  new import_docx.TextRun({
1653
2149
  text: span.content,
1654
- bold: true
2150
+ bold: true,
2151
+ font: options.font,
2152
+ size: options.size,
2153
+ color: options.color,
2154
+ italics: options.italics
1655
2155
  })
1656
2156
  );
1657
2157
  continue;
@@ -1660,7 +2160,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1660
2160
  runs.push(
1661
2161
  new import_docx.TextRun({
1662
2162
  text: span.content,
1663
- italics: true
2163
+ italics: true,
2164
+ font: options.font,
2165
+ size: options.size,
2166
+ color: options.color,
2167
+ bold: options.bold
1664
2168
  })
1665
2169
  );
1666
2170
  continue;
@@ -1669,7 +2173,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1669
2173
  runs.push(
1670
2174
  new import_docx.TextRun({
1671
2175
  text: span.content,
1672
- strike: true
2176
+ strike: true,
2177
+ font: options.font,
2178
+ size: options.size,
2179
+ color: options.color
1673
2180
  })
1674
2181
  );
1675
2182
  continue;
@@ -1677,22 +2184,23 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1677
2184
  if (span.type === "code") {
1678
2185
  runs.push(
1679
2186
  new import_docx.TextRun({
1680
- text: ` ${span.content} `,
2187
+ text: span.content,
1681
2188
  font: "Consolas",
2189
+ size: options.size ? options.size - 2 : 19,
2190
+ color: "0F172A",
1682
2191
  shading: {
1683
2192
  type: import_docx.ShadingType.CLEAR,
1684
- fill: "F1F5F9",
1685
- color: "0F172A"
2193
+ fill: "F1F5F9"
1686
2194
  }
1687
2195
  })
1688
2196
  );
1689
2197
  continue;
1690
2198
  }
1691
2199
  if (span.type === "htmlInline") {
1692
- let colorHex;
2200
+ let colorHex = options.color;
1693
2201
  let bgHex;
1694
- let isBold = false;
1695
- let isItalic = false;
2202
+ let isBold = !!options.bold;
2203
+ let isItalic = !!options.italics;
1696
2204
  if (span.style) {
1697
2205
  if (span.style.color) {
1698
2206
  colorHex = span.style.color.replace("#", "").trim();
@@ -1708,13 +2216,9 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1708
2216
  }
1709
2217
  }
1710
2218
  if (span.children && span.children.length > 0) {
1711
- const childRuns = await convertInlinesToTextRuns(span.children, baseDir);
2219
+ const childRuns = await convertInlinesToTextRuns(span.children, baseDir, options);
1712
2220
  for (const child of childRuns) {
1713
- if (child instanceof import_docx.TextRun) {
1714
- runs.push(child);
1715
- } else {
1716
- runs.push(child);
1717
- }
2221
+ runs.push(child);
1718
2222
  }
1719
2223
  continue;
1720
2224
  }
@@ -1724,6 +2228,8 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1724
2228
  color: colorHex,
1725
2229
  bold: isBold,
1726
2230
  italics: isItalic,
2231
+ font: options.font,
2232
+ size: options.size,
1727
2233
  shading: bgHex ? { type: import_docx.ShadingType.CLEAR, fill: bgHex } : void 0
1728
2234
  })
1729
2235
  );
@@ -1731,91 +2237,239 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1731
2237
  }
1732
2238
  runs.push(
1733
2239
  new import_docx.TextRun({
1734
- text: span.content
2240
+ text: span.content,
2241
+ font: options.font,
2242
+ size: options.size,
2243
+ color: options.color,
2244
+ bold: options.bold,
2245
+ italics: options.italics
1735
2246
  })
1736
2247
  );
1737
2248
  }
1738
2249
  return runs;
1739
2250
  }
1740
2251
  async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1741
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1742
- const metadata = { ...config.metadata, ...doc.metadata };
2252
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2253
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1743
2254
  const docElements = [];
1744
- if (metadata.title) {
2255
+ const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
2256
+ const primaryHex = (themeProps.primaryColor || "#33CDCF").replace("#", "");
2257
+ const primaryDarkHex = (themeProps.primaryDark || "#009DA0").replace("#", "");
2258
+ const textHex = (themeProps.textColor || "#0F172A").replace("#", "");
2259
+ const textMutedHex = (themeProps.textMuted || "#64748B").replace("#", "");
2260
+ const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
2261
+ const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
2262
+ const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
2263
+ if (resolved.title) {
1745
2264
  docElements.push(
1746
2265
  new import_docx.Paragraph({
1747
- text: metadata.title,
1748
- heading: import_docx.HeadingLevel.TITLE,
1749
- spacing: { before: 200, after: 120 }
2266
+ children: [
2267
+ new import_docx.TextRun({
2268
+ text: resolved.title,
2269
+ bold: true,
2270
+ size: 44,
2271
+ // 22pt
2272
+ color: textHex,
2273
+ font: defaultFont
2274
+ })
2275
+ ],
2276
+ spacing: { before: 120, after: 80 }
1750
2277
  })
1751
2278
  );
1752
- if (metadata.subtitle) {
2279
+ if (resolved.subtitle) {
1753
2280
  docElements.push(
1754
2281
  new import_docx.Paragraph({
1755
2282
  children: [
1756
2283
  new import_docx.TextRun({
1757
- text: metadata.subtitle,
1758
- italics: true,
1759
- color: "64748B",
1760
- size: 24
2284
+ text: resolved.subtitle,
2285
+ color: textMutedHex,
2286
+ size: 24,
1761
2287
  // 12pt
2288
+ font: defaultFont
1762
2289
  })
1763
2290
  ],
1764
- spacing: { after: 180 }
2291
+ spacing: { before: 60, after: 120 }
1765
2292
  })
1766
2293
  );
1767
2294
  }
1768
- if (metadata.author || metadata.date) {
2295
+ if (resolved.author || resolved.date || resolved.version || resolved.company) {
1769
2296
  const metaParts = [];
1770
- if (metadata.author) metaParts.push(`Author: ${Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author}`);
1771
- if (metadata.date) metaParts.push(`Date: ${metadata.date}`);
2297
+ if (resolved.author) metaParts.push(`Author: ${resolved.author}`);
2298
+ if (resolved.version) metaParts.push(`Version: ${resolved.version}`);
2299
+ if (resolved.date) metaParts.push(`Date: ${resolved.date}`);
1772
2300
  docElements.push(
1773
2301
  new import_docx.Paragraph({
1774
2302
  children: [
1775
2303
  new import_docx.TextRun({
1776
- text: metaParts.join(" | "),
1777
- color: "94A3B8",
1778
- size: 20
1779
- // 10pt
2304
+ text: metaParts.join(" "),
2305
+ color: textMutedHex,
2306
+ size: 18,
2307
+ // 9pt
2308
+ font: defaultFont
1780
2309
  })
1781
2310
  ],
1782
- spacing: { after: 360 },
2311
+ spacing: { before: 40, after: 240 },
1783
2312
  border: {
1784
2313
  bottom: {
1785
- color: "E2E8F0",
1786
- space: 10,
2314
+ color: borderHex,
2315
+ space: 12,
1787
2316
  style: import_docx.BorderStyle.SINGLE,
1788
- size: 6
2317
+ size: 4
1789
2318
  }
1790
2319
  }
1791
2320
  })
1792
2321
  );
1793
2322
  }
1794
2323
  }
1795
- for (const node of doc.nodes) {
1796
- if (node.type === "heading") {
1797
- let headingLevel = import_docx.HeadingLevel.HEADING_1;
1798
- if (node.level === 2) headingLevel = import_docx.HeadingLevel.HEADING_2;
1799
- if (node.level === 3) headingLevel = import_docx.HeadingLevel.HEADING_3;
1800
- if (node.level === 4) headingLevel = import_docx.HeadingLevel.HEADING_4;
1801
- if (node.level === 5) headingLevel = import_docx.HeadingLevel.HEADING_5;
1802
- if (node.level === 6) headingLevel = import_docx.HeadingLevel.HEADING_6;
1803
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1804
- docElements.push(
2324
+ if (resolved.toc) {
2325
+ const headingNodes = doc.nodes.filter(
2326
+ (n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
2327
+ );
2328
+ if (headingNodes.length > 0) {
2329
+ const tocParagraphs = [
1805
2330
  new import_docx.Paragraph({
1806
- heading: headingLevel,
1807
- children: runs,
1808
- spacing: { before: 240, after: 120 }
2331
+ children: [
2332
+ new import_docx.TextRun({
2333
+ text: "TABLE OF CONTENTS",
2334
+ bold: true,
2335
+ size: 18,
2336
+ // 9pt
2337
+ color: textMutedHex,
2338
+ font: defaultFont
2339
+ })
2340
+ ],
2341
+ spacing: { after: 120 },
2342
+ border: {
2343
+ bottom: {
2344
+ color: borderHex,
2345
+ space: 6,
2346
+ style: import_docx.BorderStyle.SINGLE,
2347
+ size: 4
2348
+ }
2349
+ }
1809
2350
  })
1810
- );
2351
+ ];
2352
+ for (const h of headingNodes) {
2353
+ const indentLeft = (h.level - 1) * 240;
2354
+ tocParagraphs.push(
2355
+ new import_docx.Paragraph({
2356
+ indent: { left: indentLeft },
2357
+ children: [
2358
+ new import_docx.TextRun({
2359
+ text: h.text,
2360
+ bold: h.level === 1,
2361
+ color: primaryDarkHex,
2362
+ size: h.level === 1 ? 21 : 20,
2363
+ font: defaultFont
2364
+ })
2365
+ ],
2366
+ spacing: { before: 20, after: 20 }
2367
+ })
2368
+ );
2369
+ }
2370
+ const tocCard = new import_docx.Table({
2371
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
2372
+ columnWidths: [9e3],
2373
+ rows: [
2374
+ new import_docx.TableRow({
2375
+ children: [
2376
+ new import_docx.TableCell({
2377
+ width: { size: 9e3, type: import_docx.WidthType.DXA },
2378
+ shading: { fill: cardBgHex, type: import_docx.ShadingType.CLEAR },
2379
+ margins: { top: 140, bottom: 140, left: 180, right: 180 },
2380
+ borders: {
2381
+ top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
2382
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
2383
+ left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
2384
+ right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex }
2385
+ },
2386
+ children: tocParagraphs
2387
+ })
2388
+ ]
2389
+ })
2390
+ ]
2391
+ });
2392
+ docElements.push(tocCard);
2393
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 200 } }));
2394
+ }
2395
+ }
2396
+ for (const node of doc.nodes) {
2397
+ if (node.type === "heading") {
2398
+ if (node.level === 1) {
2399
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2400
+ font: defaultFont,
2401
+ size: 34,
2402
+ // 17pt
2403
+ color: textHex,
2404
+ bold: true
2405
+ });
2406
+ docElements.push(
2407
+ new import_docx.Paragraph({
2408
+ heading: import_docx.HeadingLevel.HEADING_1,
2409
+ children: runs,
2410
+ spacing: { before: 360, after: 140 },
2411
+ border: {
2412
+ bottom: {
2413
+ color: primaryHex,
2414
+ space: 6,
2415
+ style: import_docx.BorderStyle.SINGLE,
2416
+ size: 16
2417
+ }
2418
+ }
2419
+ })
2420
+ );
2421
+ } else if (node.level === 2) {
2422
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2423
+ font: defaultFont,
2424
+ size: 26,
2425
+ // 13pt
2426
+ color: primaryDarkHex,
2427
+ bold: true
2428
+ });
2429
+ docElements.push(
2430
+ new import_docx.Paragraph({
2431
+ heading: import_docx.HeadingLevel.HEADING_2,
2432
+ children: runs,
2433
+ spacing: { before: 280, after: 100 },
2434
+ border: {
2435
+ bottom: {
2436
+ color: borderHex,
2437
+ space: 4,
2438
+ style: import_docx.BorderStyle.SINGLE,
2439
+ size: 4
2440
+ }
2441
+ }
2442
+ })
2443
+ );
2444
+ } else {
2445
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2446
+ font: defaultFont,
2447
+ size: 22,
2448
+ // 11pt
2449
+ color: textHex,
2450
+ bold: true
2451
+ });
2452
+ docElements.push(
2453
+ new import_docx.Paragraph({
2454
+ heading: node.level === 3 ? import_docx.HeadingLevel.HEADING_3 : import_docx.HeadingLevel.HEADING_4,
2455
+ children: runs,
2456
+ spacing: { before: 200, after: 80 }
2457
+ })
2458
+ );
2459
+ }
1811
2460
  continue;
1812
2461
  }
1813
2462
  if (node.type === "paragraph") {
1814
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2463
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2464
+ font: defaultFont,
2465
+ size: 22,
2466
+ // 11pt
2467
+ color: "334155"
2468
+ });
1815
2469
  docElements.push(
1816
2470
  new import_docx.Paragraph({
1817
2471
  children: runs,
1818
- spacing: { before: 60, after: 140 }
2472
+ spacing: { before: 40, after: 140, line: 280 }
1819
2473
  })
1820
2474
  );
1821
2475
  continue;
@@ -1887,7 +2541,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1887
2541
  bgFill = "F5F3FF";
1888
2542
  title = "IMPORTANT";
1889
2543
  }
1890
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2544
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2545
+ font: defaultFont,
2546
+ size: 21,
2547
+ color: "334155"
2548
+ });
1891
2549
  const calloutTable = new import_docx.Table({
1892
2550
  width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
1893
2551
  columnWidths: [9e3],
@@ -1902,7 +2560,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1902
2560
  top: { style: import_docx.BorderStyle.NONE },
1903
2561
  bottom: { style: import_docx.BorderStyle.NONE },
1904
2562
  right: { style: import_docx.BorderStyle.NONE },
1905
- left: { style: import_docx.BorderStyle.SINGLE, size: 16, color: borderColor }
2563
+ left: { style: import_docx.BorderStyle.SINGLE, size: 20, color: borderColor }
1906
2564
  },
1907
2565
  children: [
1908
2566
  new import_docx.Paragraph({
@@ -1911,13 +2569,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1911
2569
  text: `[${title}]`,
1912
2570
  bold: true,
1913
2571
  color: borderColor,
2572
+ font: defaultFont,
1914
2573
  size: 20
1915
2574
  })
1916
2575
  ],
1917
- spacing: { after: 60 }
2576
+ spacing: { after: 40 }
1918
2577
  }),
1919
2578
  new import_docx.Paragraph({
1920
- children: runs
2579
+ children: runs,
2580
+ spacing: { line: 270 }
1921
2581
  })
1922
2582
  ]
1923
2583
  })
@@ -1935,17 +2595,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1935
2595
  if (lines.length > 0) {
1936
2596
  for (const lineText of lines) {
1937
2597
  const spans = parseInlineSpans(lineText.replace(/^>\s?/, "").trim());
1938
- const lineRuns = await convertInlinesToTextRuns(spans, baseDir);
2598
+ const lineRuns = await convertInlinesToTextRuns(spans, baseDir, {
2599
+ font: defaultFont,
2600
+ size: 21,
2601
+ color: "475569",
2602
+ italics: true
2603
+ });
1939
2604
  quoteParagraphs.push(
1940
2605
  new import_docx.Paragraph({
1941
2606
  children: lineRuns,
1942
- spacing: { before: 40, after: 40 }
2607
+ spacing: { before: 20, after: 20, line: 260 }
1943
2608
  })
1944
2609
  );
1945
2610
  }
1946
2611
  } else {
1947
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1948
- quoteParagraphs.push(new import_docx.Paragraph({ children: runs }));
2612
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2613
+ font: defaultFont,
2614
+ size: 21,
2615
+ color: "475569",
2616
+ italics: true
2617
+ });
2618
+ quoteParagraphs.push(new import_docx.Paragraph({ children: runs, spacing: { line: 260 } }));
1949
2619
  }
1950
2620
  const quoteTable = new import_docx.Table({
1951
2621
  width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
@@ -1961,7 +2631,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1961
2631
  top: { style: import_docx.BorderStyle.NONE },
1962
2632
  bottom: { style: import_docx.BorderStyle.NONE },
1963
2633
  right: { style: import_docx.BorderStyle.NONE },
1964
- left: { style: import_docx.BorderStyle.SINGLE, size: 12, color: "33CDCF" }
2634
+ left: { style: import_docx.BorderStyle.SINGLE, size: 16, color: primaryHex }
1965
2635
  },
1966
2636
  children: quoteParagraphs
1967
2637
  })
@@ -1996,7 +2666,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1996
2666
  docElements.push(
1997
2667
  new import_docx.Paragraph({
1998
2668
  children: [
1999
- new import_docx.TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: "33CDCF" })
2669
+ new import_docx.TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: primaryHex })
2000
2670
  ],
2001
2671
  spacing: { before: 60, after: 60 }
2002
2672
  })
@@ -2008,8 +2678,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2008
2678
  const tableRows = [];
2009
2679
  const numCols = ((_b = (_a = node.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) || 1;
2010
2680
  const colWidth = Math.floor(9e3 / numCols);
2011
- for (const rowNode of node.children) {
2681
+ for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
2682
+ const rowNode = node.children[rowIdx];
2012
2683
  const cells = [];
2684
+ const isHeader = rowNode.isHeader;
2685
+ const rowBg = isHeader ? "F1F5F9" : rowIdx % 2 === 0 ? "FFFFFF" : "F8FAFC";
2013
2686
  if (rowNode.children) {
2014
2687
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
2015
2688
  const cellNode = rowNode.children[colIdx];
@@ -2017,22 +2690,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2017
2690
  let alignment = import_docx.AlignmentType.LEFT;
2018
2691
  if (align === "center") alignment = import_docx.AlignmentType.CENTER;
2019
2692
  if (align === "right") alignment = import_docx.AlignmentType.RIGHT;
2020
- const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir);
2693
+ const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir, {
2694
+ font: defaultFont,
2695
+ size: 20,
2696
+ color: isHeader ? "0F172A" : "334155",
2697
+ bold: isHeader
2698
+ });
2021
2699
  cells.push(
2022
2700
  new import_docx.TableCell({
2023
2701
  width: { size: colWidth, type: import_docx.WidthType.DXA },
2024
- shading: rowNode.isHeader ? { fill: "F1F5F9", type: import_docx.ShadingType.CLEAR } : void 0,
2025
- margins: { top: 100, bottom: 100, left: 120, right: 120 },
2702
+ shading: { fill: rowBg, type: import_docx.ShadingType.CLEAR },
2703
+ margins: { top: 100, bottom: 100, left: 140, right: 140 },
2026
2704
  borders: {
2027
- top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
2028
- bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
2029
- left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
2030
- right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" }
2705
+ top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2706
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: isHeader ? 8 : 4, color: isHeader ? "CBD5E1" : "E2E8F0" },
2707
+ left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2708
+ right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" }
2031
2709
  },
2032
2710
  children: [
2033
2711
  new import_docx.Paragraph({
2034
2712
  alignment,
2035
- children: rowNode.isHeader ? runs.map((r) => r instanceof import_docx.TextRun ? new import_docx.TextRun({ ...r, bold: true, color: "0F172A" }) : r) : runs
2713
+ children: runs
2036
2714
  })
2037
2715
  ]
2038
2716
  })
@@ -2041,7 +2719,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2041
2719
  }
2042
2720
  tableRows.push(
2043
2721
  new import_docx.TableRow({
2044
- tableHeader: rowNode.isHeader,
2722
+ tableHeader: isHeader,
2045
2723
  children: cells
2046
2724
  })
2047
2725
  );
@@ -2057,7 +2735,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2057
2735
  }
2058
2736
  if (node.type === "list" && node.children) {
2059
2737
  for (const item of node.children) {
2060
- const runs = await convertInlinesToTextRuns(item.inlines, baseDir);
2738
+ const runs = await convertInlinesToTextRuns(item.inlines, baseDir, {
2739
+ font: defaultFont,
2740
+ size: 21,
2741
+ color: "334155"
2742
+ });
2061
2743
  let prefix = "";
2062
2744
  if (item.checked !== void 0) {
2063
2745
  prefix = item.checked ? "[X] " : "[ ] ";
@@ -2069,7 +2751,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2069
2751
  ...prefix ? [new import_docx.TextRun({ text: prefix, bold: true, font: "Consolas" })] : [],
2070
2752
  ...runs
2071
2753
  ],
2072
- spacing: { before: 40, after: 40 }
2754
+ spacing: { before: 20, after: 20, line: 260 }
2073
2755
  })
2074
2756
  );
2075
2757
  }
@@ -2144,88 +2826,205 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2144
2826
  continue;
2145
2827
  }
2146
2828
  }
2147
- const headerObj = metadata.header || config.header;
2148
- const footerObj = metadata.footer || config.footer;
2149
- const headerTabStops = [
2150
- { type: import_docx.TabStopType.CENTER, position: 4513 },
2151
- { type: import_docx.TabStopType.RIGHT, position: 9026 }
2152
- ];
2153
- const docHeader = headerObj ? new import_docx.Header({
2829
+ const contentWidthTwip = Math.max(
2830
+ 1e3,
2831
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
2832
+ );
2833
+ const centerPos = Math.round(contentWidthTwip / 2);
2834
+ const rightPos = contentWidthTwip;
2835
+ const headerRuns = [];
2836
+ if ((_d = resolved.header) == null ? void 0 : _d.left) {
2837
+ headerRuns.push(
2838
+ new import_docx.TextRun({
2839
+ text: resolved.header.left.text,
2840
+ color: resolved.header.left.color.replace("#", ""),
2841
+ size: (resolved.header.left.fontSize || 9) * 2,
2842
+ font: resolved.header.left.fontFamily || defaultFont,
2843
+ bold: resolved.header.left.bold,
2844
+ italics: resolved.header.left.italic
2845
+ })
2846
+ );
2847
+ }
2848
+ headerRuns.push(new import_docx.TextRun({ text: " " }));
2849
+ if ((_e = resolved.header) == null ? void 0 : _e.center) {
2850
+ headerRuns.push(
2851
+ new import_docx.TextRun({
2852
+ text: resolved.header.center.text,
2853
+ color: resolved.header.center.color.replace("#", ""),
2854
+ size: (resolved.header.center.fontSize || 9) * 2,
2855
+ font: resolved.header.center.fontFamily || defaultFont,
2856
+ bold: resolved.header.center.bold,
2857
+ italics: resolved.header.center.italic
2858
+ })
2859
+ );
2860
+ }
2861
+ headerRuns.push(new import_docx.TextRun({ text: " " }));
2862
+ if ((_f = resolved.header) == null ? void 0 : _f.right) {
2863
+ headerRuns.push(
2864
+ new import_docx.TextRun({
2865
+ text: resolved.header.right.text,
2866
+ color: resolved.header.right.color.replace("#", ""),
2867
+ size: (resolved.header.right.fontSize || 9) * 2,
2868
+ font: resolved.header.right.fontFamily || defaultFont,
2869
+ bold: resolved.header.right.bold,
2870
+ italics: resolved.header.right.italic
2871
+ })
2872
+ );
2873
+ }
2874
+ const docHeader = resolved.header ? new import_docx.Header({
2154
2875
  children: [
2155
2876
  new import_docx.Paragraph({
2156
- tabStops: headerTabStops,
2157
- children: [
2158
- // Left zone
2159
- ...headerObj.left ? [
2877
+ tabStops: [
2878
+ {
2879
+ type: import_docx.TabStopType.CENTER,
2880
+ position: centerPos
2881
+ },
2882
+ {
2883
+ type: import_docx.TabStopType.RIGHT,
2884
+ position: rightPos
2885
+ }
2886
+ ],
2887
+ border: resolved.header.divider ? {
2888
+ bottom: {
2889
+ style: import_docx.BorderStyle.SINGLE,
2890
+ size: 4,
2891
+ space: 6,
2892
+ color: (resolved.header.dividerColor || "#CBD5E1").replace("#", "")
2893
+ }
2894
+ } : void 0,
2895
+ children: headerRuns,
2896
+ spacing: { after: 120 }
2897
+ })
2898
+ ]
2899
+ }) : void 0;
2900
+ const footerRuns = [];
2901
+ if ((_g = resolved.footer) == null ? void 0 : _g.left) {
2902
+ footerRuns.push(
2903
+ new import_docx.TextRun({
2904
+ text: resolved.footer.left.text,
2905
+ color: resolved.footer.left.color.replace("#", ""),
2906
+ size: (resolved.footer.left.fontSize || 9) * 2,
2907
+ font: resolved.footer.left.fontFamily || defaultFont,
2908
+ bold: resolved.footer.left.bold,
2909
+ italics: resolved.footer.left.italic
2910
+ })
2911
+ );
2912
+ }
2913
+ footerRuns.push(new import_docx.TextRun({ text: " " }));
2914
+ if ((_h = resolved.footer) == null ? void 0 : _h.center) {
2915
+ footerRuns.push(
2916
+ new import_docx.TextRun({
2917
+ text: resolved.footer.center.text,
2918
+ color: resolved.footer.center.color.replace("#", ""),
2919
+ size: (resolved.footer.center.fontSize || 9) * 2,
2920
+ font: resolved.footer.center.fontFamily || defaultFont,
2921
+ bold: resolved.footer.center.bold,
2922
+ italics: resolved.footer.center.italic
2923
+ })
2924
+ );
2925
+ }
2926
+ footerRuns.push(new import_docx.TextRun({ text: " " }));
2927
+ if ((_i = resolved.footer) == null ? void 0 : _i.right) {
2928
+ const rZone = resolved.footer.right;
2929
+ const rColor = rZone.color.replace("#", "");
2930
+ const rSize = (rZone.fontSize || 9) * 2;
2931
+ const rFont = rZone.fontFamily || defaultFont;
2932
+ const rBold = rZone.bold;
2933
+ const rItalics = rZone.italic;
2934
+ if (rZone.text.includes("{page}") || rZone.text.includes("{pages}")) {
2935
+ const parts = rZone.text.split(/(\{page\}|\{pages\})/gi);
2936
+ for (const part of parts) {
2937
+ if (part.toLowerCase() === "{page}") {
2938
+ footerRuns.push(
2160
2939
  new import_docx.TextRun({
2161
- text: headerObj.left.replace("{title}", metadata.title || ""),
2162
- color: "94A3B8",
2163
- size: 18
2940
+ children: [import_docx.PageNumber.CURRENT],
2941
+ color: rColor,
2942
+ size: rSize,
2943
+ font: rFont,
2944
+ bold: rBold,
2945
+ italics: rItalics
2164
2946
  })
2165
- ] : [],
2166
- // Center zone (tab + text)
2167
- ...headerObj.center ? [
2168
- new import_docx.TextRun({ text: " ", color: "94A3B8", size: 18 }),
2947
+ );
2948
+ } else if (part.toLowerCase() === "{pages}") {
2949
+ footerRuns.push(
2169
2950
  new import_docx.TextRun({
2170
- text: headerObj.center.replace("{title}", metadata.title || ""),
2171
- color: "94A3B8",
2172
- size: 18
2951
+ children: [import_docx.PageNumber.TOTAL_PAGES],
2952
+ color: rColor,
2953
+ size: rSize,
2954
+ font: rFont,
2955
+ bold: rBold,
2956
+ italics: rItalics
2173
2957
  })
2174
- ] : [],
2175
- // Right zone (tab + text) — skip extra tab if center already used one
2176
- ...headerObj.right ? [
2177
- new import_docx.TextRun({
2178
- text: headerObj.left || headerObj.center ? " " : "",
2179
- color: "94A3B8",
2180
- size: 18
2181
- }),
2958
+ );
2959
+ } else if (part) {
2960
+ footerRuns.push(
2182
2961
  new import_docx.TextRun({
2183
- text: headerObj.right.replace("{title}", metadata.title || ""),
2184
- color: "94A3B8",
2185
- size: 18
2962
+ text: part,
2963
+ color: rColor,
2964
+ size: rSize,
2965
+ font: rFont,
2966
+ bold: rBold,
2967
+ italics: rItalics
2186
2968
  })
2187
- ] : []
2188
- ]
2189
- })
2190
- ]
2191
- }) : void 0;
2192
- const docFooter = footerObj ? new import_docx.Footer({
2969
+ );
2970
+ }
2971
+ }
2972
+ } else {
2973
+ footerRuns.push(
2974
+ new import_docx.TextRun({
2975
+ text: rZone.text,
2976
+ color: rColor,
2977
+ size: rSize,
2978
+ font: rFont,
2979
+ bold: rBold,
2980
+ italics: rItalics
2981
+ })
2982
+ );
2983
+ }
2984
+ }
2985
+ const docFooter = resolved.footer ? new import_docx.Footer({
2193
2986
  children: [
2194
2987
  new import_docx.Paragraph({
2195
- tabStops: headerTabStops,
2196
- children: [
2197
- // Left zone
2198
- ...footerObj.left ? [
2199
- new import_docx.TextRun({
2200
- text: footerObj.left.replace("{page}", "").replace("{pages}", "").trim(),
2201
- color: "94A3B8",
2202
- size: 18
2203
- })
2204
- ] : [],
2205
- // Right zone: always includes page number if right is configured or footer exists
2206
- new import_docx.TextRun({ text: " ", color: "94A3B8", size: 18 }),
2207
- new import_docx.TextRun({ text: "Page ", color: "94A3B8", size: 18 }),
2208
- new import_docx.TextRun({ children: [import_docx.PageNumber.CURRENT], color: "94A3B8", size: 18 }),
2209
- new import_docx.TextRun({ text: " of ", color: "94A3B8", size: 18 }),
2210
- new import_docx.TextRun({ children: [import_docx.PageNumber.TOTAL_PAGES], color: "94A3B8", size: 18 })
2211
- ]
2988
+ tabStops: [
2989
+ {
2990
+ type: import_docx.TabStopType.CENTER,
2991
+ position: centerPos
2992
+ },
2993
+ {
2994
+ type: import_docx.TabStopType.RIGHT,
2995
+ position: rightPos
2996
+ }
2997
+ ],
2998
+ border: resolved.footer.divider ? {
2999
+ top: {
3000
+ style: import_docx.BorderStyle.SINGLE,
3001
+ size: 4,
3002
+ space: 6,
3003
+ color: (resolved.footer.dividerColor || "#CBD5E1").replace("#", "")
3004
+ }
3005
+ } : void 0,
3006
+ children: footerRuns,
3007
+ spacing: { before: 120 }
2212
3008
  })
2213
3009
  ]
2214
3010
  }) : void 0;
2215
- const topMargin = parseMarginToTwip(((_d = metadata.margins) == null ? void 0 : _d.top) || ((_e = config.margins) == null ? void 0 : _e.top), 1440);
2216
- const bottomMargin = parseMarginToTwip(((_f = metadata.margins) == null ? void 0 : _f.bottom) || ((_g = config.margins) == null ? void 0 : _g.bottom), 1440);
2217
- const leftMargin = parseMarginToTwip(((_h = metadata.margins) == null ? void 0 : _h.left) || ((_i = config.margins) == null ? void 0 : _i.left), 1440);
2218
- const rightMargin = parseMarginToTwip(((_j = metadata.margins) == null ? void 0 : _j.right) || ((_k = config.margins) == null ? void 0 : _k.right), 1440);
2219
- const isLandscape = (metadata.orientation || config.orientation) === "landscape";
3011
+ const isLandscape = resolved.orientation === "landscape";
2220
3012
  const document = new import_docx.Document({
2221
3013
  styles: {
2222
3014
  default: {
2223
3015
  document: {
2224
3016
  run: {
2225
- font: "Segoe UI",
2226
- size: 22,
2227
- // 11pt
2228
- color: "0F172A"
3017
+ font: defaultFont,
3018
+ size: 21,
3019
+ // 10.5pt
3020
+ color: textHex
3021
+ },
3022
+ paragraph: {
3023
+ spacing: {
3024
+ line: 276,
3025
+ // 1.15 line spacing
3026
+ after: 140
3027
+ }
2229
3028
  }
2230
3029
  }
2231
3030
  }
@@ -2235,13 +3034,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2235
3034
  properties: {
2236
3035
  page: {
2237
3036
  size: {
3037
+ width: resolved.paperDimensions.widthTwip,
3038
+ height: resolved.paperDimensions.heightTwip,
2238
3039
  orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
2239
3040
  },
2240
3041
  margin: {
2241
- top: topMargin,
2242
- bottom: bottomMargin,
2243
- left: leftMargin,
2244
- right: rightMargin,
3042
+ top: resolved.margins.topTwip,
3043
+ bottom: resolved.margins.bottomTwip,
3044
+ left: resolved.margins.leftTwip,
3045
+ right: resolved.margins.rightTwip,
2245
3046
  header: 720,
2246
3047
  footer: 720
2247
3048
  }
@@ -2256,109 +3057,6 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2256
3057
  return await import_docx.Packer.toBuffer(document);
2257
3058
  }
2258
3059
 
2259
- // src/config/loadConfig.ts
2260
- var fs5 = __toESM(require("fs"));
2261
- var path5 = __toESM(require("path"));
2262
- var import_node_url3 = require("url");
2263
- var YAML = __toESM(require("yaml"));
2264
- var DEFAULT_CONFIG_FILENAMES = [
2265
- "markforge.config.json",
2266
- ".markforgerc.json",
2267
- "markforge.config.yaml",
2268
- "markforge.config.yml",
2269
- ".markforgerc.yaml",
2270
- ".markforgerc.yml",
2271
- ".markforgerc",
2272
- "markforge.config.ts",
2273
- "markforge.config.js",
2274
- "markforge.config.mjs",
2275
- "markforge.config.cjs"
2276
- ];
2277
- var DEFAULT_CONFIG = {
2278
- to: ["docx", "pdf"],
2279
- outputDir: void 0,
2280
- theme: "default",
2281
- css: void 0,
2282
- orientation: "portrait",
2283
- paperSize: "A4",
2284
- margins: {
2285
- top: "2.5cm",
2286
- bottom: "2.5cm",
2287
- left: "2.5cm",
2288
- right: "2.5cm"
2289
- },
2290
- header: void 0,
2291
- footer: {
2292
- right: "Page {page} of {pages}"
2293
- },
2294
- toc: false,
2295
- watermark: void 0,
2296
- embedImages: true,
2297
- metadata: void 0,
2298
- watch: false,
2299
- serve: false,
2300
- port: 4e3,
2301
- open: false,
2302
- bundleHtml: true,
2303
- syntaxTheme: "github-dark"
2304
- };
2305
- async function loadConfig(customPath, cwd = process.cwd()) {
2306
- let resolvedPath = null;
2307
- if (customPath) {
2308
- resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(cwd, customPath);
2309
- if (!fs5.existsSync(resolvedPath)) {
2310
- throw new Error(`Configuration file not found: ${resolvedPath}`);
2311
- }
2312
- } else {
2313
- for (const filename of DEFAULT_CONFIG_FILENAMES) {
2314
- const candidate = path5.resolve(cwd, filename);
2315
- if (fs5.existsSync(candidate)) {
2316
- resolvedPath = candidate;
2317
- break;
2318
- }
2319
- }
2320
- }
2321
- if (!resolvedPath) {
2322
- return {
2323
- config: { ...DEFAULT_CONFIG },
2324
- configPath: null
2325
- };
2326
- }
2327
- const ext = path5.extname(resolvedPath).toLowerCase();
2328
- let userConfig = {};
2329
- if (ext === ".json" || resolvedPath.endsWith(".markforgerc")) {
2330
- const raw = fs5.readFileSync(resolvedPath, "utf-8");
2331
- userConfig = JSON.parse(raw);
2332
- } else if (ext === ".yaml" || ext === ".yml") {
2333
- const raw = fs5.readFileSync(resolvedPath, "utf-8");
2334
- userConfig = YAML.parse(raw);
2335
- } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
2336
- try {
2337
- const fileUrl = (0, import_node_url3.pathToFileURL)(resolvedPath).href;
2338
- const mod = await import(fileUrl);
2339
- userConfig = mod.default || mod;
2340
- } catch {
2341
- const required = require(resolvedPath);
2342
- userConfig = required.default || required;
2343
- }
2344
- }
2345
- const mergedConfig = {
2346
- ...DEFAULT_CONFIG,
2347
- ...userConfig,
2348
- margins: {
2349
- ...DEFAULT_CONFIG.margins,
2350
- ...userConfig.margins
2351
- },
2352
- header: userConfig.header || DEFAULT_CONFIG.header,
2353
- footer: userConfig.footer || DEFAULT_CONFIG.footer,
2354
- metadata: userConfig.metadata
2355
- };
2356
- return {
2357
- config: mergedConfig,
2358
- configPath: resolvedPath
2359
- };
2360
- }
2361
-
2362
3060
  // src/core/engine.ts
2363
3061
  function formatServerTimestamp(date = /* @__PURE__ */ new Date()) {
2364
3062
  const pad = (n) => String(n).padStart(2, "0");
@@ -2519,10 +3217,17 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
2519
3217
  0 && (module.exports = {
2520
3218
  DEFAULT_CONFIG,
2521
3219
  MARKFORGE_VERSION,
3220
+ Orientation,
3221
+ OutputFormat,
3222
+ PAPER_DIMENSIONS_TWIP,
3223
+ PaperSizeEnum,
2522
3224
  SYNTAX_COLORS,
3225
+ SyntaxTheme,
2523
3226
  THEMES,
2524
- THEME_ACADEMIC,
3227
+ THEME_CORPORATE,
2525
3228
  THEME_DEFAULT,
3229
+ Theme,
3230
+ WatermarkPosition,
2526
3231
  buildDocxDocument,
2527
3232
  buildHtmlDocument,
2528
3233
  buildPdfDocument,
@@ -2531,6 +3236,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
2531
3236
  escapeHtml,
2532
3237
  findChromeExecutable,
2533
3238
  formatServerTimestamp,
3239
+ generateThemeCss,
2534
3240
  getMarkforgeVersion,
2535
3241
  getMimeType,
2536
3242
  highlightCodeToHtml,
@@ -2538,11 +3244,16 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
2538
3244
  inlineHtmlImages,
2539
3245
  loadConfig,
2540
3246
  markforge,
3247
+ normalizeHeaderFooter,
3248
+ normalizeHeaderFooterSlot,
3249
+ normalizeWatermark,
2541
3250
  parseInlineSpans,
2542
3251
  parseMarginToTwip,
2543
3252
  parseMarkdownDocument,
2544
3253
  renderInlinesToHtml,
2545
3254
  renderMermaidToPng,
3255
+ replaceDocumentTokens,
3256
+ resolveDocumentConfig,
2546
3257
  resolveImage,
2547
3258
  slugify,
2548
3259
  tokenizeCodeLine