@masumdev/markforge 0.2.4 → 0.3.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
@@ -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,17 @@ __export(src_exports, {
59
67
  inlineHtmlImages: () => inlineHtmlImages,
60
68
  loadConfig: () => loadConfig,
61
69
  markforge: () => compileMarkdown,
70
+ normalizeHeaderFooter: () => normalizeHeaderFooter,
71
+ normalizeHeaderFooterSlot: () => normalizeHeaderFooterSlot,
72
+ normalizeSignatures: () => normalizeSignatures,
73
+ normalizeWatermark: () => normalizeWatermark,
62
74
  parseInlineSpans: () => parseInlineSpans,
63
- parseMarginToTwip: () => parseMarginToTwip,
75
+ parseMarginToTwip: () => parseMarginToTwip2,
64
76
  parseMarkdownDocument: () => parseMarkdownDocument,
65
77
  renderInlinesToHtml: () => renderInlinesToHtml,
66
78
  renderMermaidToPng: () => renderMermaidToPng,
79
+ replaceDocumentTokens: () => replaceDocumentTokens,
80
+ resolveDocumentConfig: () => resolveDocumentConfig,
67
81
  resolveImage: () => resolveImage,
68
82
  slugify: () => slugify,
69
83
  tokenizeCodeLine: () => tokenizeCodeLine
@@ -585,6 +599,51 @@ var SYNTAX_COLORS_LIGHT = {
585
599
  plain: "24292E"
586
600
  // Near-black — identifiers
587
601
  };
602
+ var SYNTAX_THEMES = {
603
+ "github-dark": SYNTAX_COLORS,
604
+ "dark": SYNTAX_COLORS,
605
+ "github-light": SYNTAX_COLORS_LIGHT,
606
+ "light": SYNTAX_COLORS_LIGHT,
607
+ "dracula": {
608
+ keyword: "FF79C6",
609
+ string: "F1FA8C",
610
+ comment: "6272A4",
611
+ number: "BD93F9",
612
+ boolean: "BD93F9",
613
+ function: "50FA7B",
614
+ type: "8BE9FD",
615
+ operator: "FF79C6",
616
+ punctuation: "F8F8F2",
617
+ plain: "F8F8F2"
618
+ },
619
+ "monokai": {
620
+ keyword: "F92672",
621
+ string: "E6DB74",
622
+ comment: "75715E",
623
+ number: "AE81FF",
624
+ boolean: "AE81FF",
625
+ function: "A6E22E",
626
+ type: "66D9EF",
627
+ operator: "F92672",
628
+ punctuation: "F8F8F2",
629
+ plain: "F8F8F2"
630
+ },
631
+ "nord": {
632
+ keyword: "81A1C1",
633
+ string: "A3BE8C",
634
+ comment: "616E88",
635
+ number: "B48EAD",
636
+ boolean: "81A1C1",
637
+ function: "88C0D0",
638
+ type: "8FBCBB",
639
+ operator: "81A1C1",
640
+ punctuation: "ECEFF4",
641
+ plain: "D8DEE9"
642
+ }
643
+ };
644
+ function getSyntaxPalette(themeName = "github-dark") {
645
+ return SYNTAX_THEMES[themeName.toLowerCase()] || SYNTAX_THEMES["github-dark"];
646
+ }
588
647
  var JS_KEYWORDS = /* @__PURE__ */ new Set([
589
648
  "import",
590
649
  "from",
@@ -728,9 +787,9 @@ var SQL_KEYWORDS = /* @__PURE__ */ new Set([
728
787
  "foreign",
729
788
  "references"
730
789
  ]);
731
- function tokenizeCodeLine(line, lang = "", theme = "dark") {
790
+ function tokenizeCodeLine(line, lang = "", theme = "github-dark") {
732
791
  var _a;
733
- const COLORS = theme === "light" ? SYNTAX_COLORS_LIGHT : SYNTAX_COLORS;
792
+ const COLORS = getSyntaxPalette(theme);
734
793
  if (!line) {
735
794
  return [{ text: " ", type: "plain", colorHex: COLORS.plain }];
736
795
  }
@@ -813,6 +872,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
813
872
  tokens.push({ text: word, type: "keyword", colorHex: COLORS.keyword, bold: true });
814
873
  continue;
815
874
  }
875
+ if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
876
+ tokens.push({ text: word, type: "type", colorHex: COLORS.type });
877
+ continue;
878
+ }
816
879
  let nextNonWs = pos;
817
880
  while (nextNonWs < line.length && /\s/.test(line[nextNonWs])) {
818
881
  nextNonWs++;
@@ -821,10 +884,6 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
821
884
  tokens.push({ text: word, type: "function", colorHex: COLORS.function });
822
885
  continue;
823
886
  }
824
- if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
825
- tokens.push({ text: word, type: "type", colorHex: COLORS.type });
826
- continue;
827
- }
828
887
  tokens.push({ text: word, type: "plain", colorHex: COLORS.plain });
829
888
  continue;
830
889
  }
@@ -837,10 +896,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
837
896
  }
838
897
  return tokens;
839
898
  }
840
- function highlightCodeToHtml(code, lang = "") {
899
+ function highlightCodeToHtml(code, lang = "", theme = "github-dark") {
841
900
  const lines = (code || "").split("\n");
842
901
  const htmlLines = lines.map((line) => {
843
- const tokens = tokenizeCodeLine(line, lang);
902
+ const tokens = tokenizeCodeLine(line, lang, theme);
844
903
  return tokens.map((t) => {
845
904
  const escaped = t.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
846
905
  if (t.type === "plain") return escaped;
@@ -852,21 +911,62 @@ function highlightCodeToHtml(code, lang = "") {
852
911
 
853
912
  // src/core/mermaid/mermaidRenderer.ts
854
913
  var import_node_child_process2 = require("child_process");
855
- var fs4 = __toESM(require("fs"));
914
+ var fs5 = __toESM(require("fs"));
856
915
  var os2 = __toESM(require("os"));
857
- var path4 = __toESM(require("path"));
858
- var import_node_url2 = require("url");
916
+ var path5 = __toESM(require("path"));
917
+ var import_node_url3 = require("url");
859
918
 
860
919
  // src/core/pdf/pdfBuilder.ts
861
- var fs3 = __toESM(require("fs"));
862
- var path3 = __toESM(require("path"));
920
+ var fs4 = __toESM(require("fs"));
921
+ var path4 = __toESM(require("path"));
863
922
  var os = __toESM(require("os"));
864
- var import_node_url = require("url");
923
+ var import_node_url2 = require("url");
865
924
  var import_node_child_process = require("child_process");
866
925
 
867
926
  // src/core/html/htmlBuilder.ts
868
- var fs2 = __toESM(require("fs"));
869
- var path2 = __toESM(require("path"));
927
+ var fs3 = __toESM(require("fs"));
928
+ var path3 = __toESM(require("path"));
929
+
930
+ // src/config/types.ts
931
+ var OutputFormat = /* @__PURE__ */ ((OutputFormat2) => {
932
+ OutputFormat2["DOCX"] = "docx";
933
+ OutputFormat2["PDF"] = "pdf";
934
+ OutputFormat2["HTML"] = "html";
935
+ OutputFormat2["PNG"] = "png";
936
+ return OutputFormat2;
937
+ })(OutputFormat || {});
938
+ var Theme = /* @__PURE__ */ ((Theme2) => {
939
+ Theme2["CORPORATE"] = "corporate";
940
+ return Theme2;
941
+ })(Theme || {});
942
+ var Orientation = /* @__PURE__ */ ((Orientation2) => {
943
+ Orientation2["PORTRAIT"] = "portrait";
944
+ Orientation2["LANDSCAPE"] = "landscape";
945
+ return Orientation2;
946
+ })(Orientation || {});
947
+ var PaperSizeEnum = /* @__PURE__ */ ((PaperSizeEnum2) => {
948
+ PaperSizeEnum2["A4"] = "A4";
949
+ PaperSizeEnum2["LETTER"] = "Letter";
950
+ PaperSizeEnum2["LEGAL"] = "Legal";
951
+ PaperSizeEnum2["A3"] = "A3";
952
+ PaperSizeEnum2["A5"] = "A5";
953
+ return PaperSizeEnum2;
954
+ })(PaperSizeEnum || {});
955
+ var SyntaxTheme = /* @__PURE__ */ ((SyntaxTheme2) => {
956
+ SyntaxTheme2["GITHUB_DARK"] = "github-dark";
957
+ SyntaxTheme2["GITHUB_LIGHT"] = "github-light";
958
+ SyntaxTheme2["DRACULA"] = "dracula";
959
+ SyntaxTheme2["MONOKAI"] = "monokai";
960
+ SyntaxTheme2["NORD"] = "nord";
961
+ return SyntaxTheme2;
962
+ })(SyntaxTheme || {});
963
+ var WatermarkPosition = /* @__PURE__ */ ((WatermarkPosition2) => {
964
+ WatermarkPosition2["DIAGONAL"] = "diagonal";
965
+ WatermarkPosition2["CENTER"] = "center";
966
+ WatermarkPosition2["TOP_RIGHT"] = "top-right";
967
+ WatermarkPosition2["BOTTOM_RIGHT"] = "bottom-right";
968
+ return WatermarkPosition2;
969
+ })(WatermarkPosition || {});
870
970
 
871
971
  // src/core/html/htmlThemes.ts
872
972
  var THEME_COMPONENTS = `
@@ -949,7 +1049,7 @@ hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
949
1049
  pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
950
1050
  }
951
1051
  `;
952
- var THEME_DEFAULT = `
1052
+ var THEME_CORPORATE = `
953
1053
  :root {
954
1054
  --mf-bg: #ffffff;
955
1055
  --mf-text: #0f172a;
@@ -961,49 +1061,479 @@ var THEME_DEFAULT = `
961
1061
  --mf-card-bg: #f8fafc;
962
1062
  --mf-code-bg: #0f172a;
963
1063
  --mf-code-text: #f8fafc;
964
- --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
1064
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
965
1065
  --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
966
1066
  }
967
1067
  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; }
1068
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
969
1069
  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; }
1070
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1071
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
972
1072
  h3 { font-size: 1.3rem; }
973
1073
  h4 { font-size: 1.1rem; }
974
1074
  p { margin: 0.8rem 0; }
975
1075
  `;
976
- var THEME_ACADEMIC = `
1076
+ var THEME_DEFAULT = THEME_CORPORATE;
1077
+ var THEMES = {
1078
+ corporate: THEME_CORPORATE,
1079
+ default: THEME_CORPORATE
1080
+ };
1081
+ function generateThemeCss(theme) {
1082
+ if (!theme || theme === "corporate" || theme === "default" || theme === "corporate" /* CORPORATE */) {
1083
+ return THEME_CORPORATE;
1084
+ }
1085
+ if (typeof theme === "object") {
1086
+ const bg = theme.backgroundColor || "#ffffff";
1087
+ const text = theme.textColor || "#0f172a";
1088
+ const textMuted = theme.textMuted || "#64748b";
1089
+ const primary = theme.primaryColor || "#33CDCF";
1090
+ const primaryDark = theme.primaryDark || primary;
1091
+ const primaryLight = theme.primaryLight || "#ECFDFD";
1092
+ const border = theme.borderColor || "#e2e8f0";
1093
+ const cardBg = theme.cardBackground || "#f8fafc";
1094
+ const codeBg = theme.codeBackground || "#0f172a";
1095
+ const codeText = theme.codeText || "#f8fafc";
1096
+ const font = theme.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
1097
+ const fontMono = theme.fontMono || "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
1098
+ return `
977
1099
  :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;
1100
+ --mf-bg: ${bg};
1101
+ --mf-text: ${text};
1102
+ --mf-text-muted: ${textMuted};
1103
+ --mf-primary: ${primary};
1104
+ --mf-primary-dark: ${primaryDark};
1105
+ --mf-primary-light: ${primaryLight};
1106
+ --mf-border: ${border};
1107
+ --mf-card-bg: ${cardBg};
1108
+ --mf-code-bg: ${codeBg};
1109
+ --mf-code-text: ${codeText};
1110
+ --mf-font-family: ${font};
1111
+ --mf-font-mono: ${fontMono};
990
1112
  }
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; }
1113
+ 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; }
1114
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1115
+ 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; }
1116
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1117
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
1118
+ h3 { font-size: 1.3rem; }
1119
+ h4 { font-size: 1.1rem; }
1120
+ p { margin: 0.8rem 0; }
1121
+ ${theme.customCss || ""}
998
1122
  `;
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
1123
+ }
1124
+ if (typeof theme === "string" && THEMES[theme]) {
1125
+ return THEMES[theme];
1126
+ }
1127
+ return THEME_CORPORATE;
1128
+ }
1129
+
1130
+ // src/config/loadConfig.ts
1131
+ var fs2 = __toESM(require("fs"));
1132
+ var path2 = __toESM(require("path"));
1133
+ var import_node_url = require("url");
1134
+ var YAML = __toESM(require("yaml"));
1135
+ var DEFAULT_CONFIG_FILENAMES = [
1136
+ "markforge.config.json",
1137
+ ".markforgerc.json",
1138
+ "markforge.config.yaml",
1139
+ "markforge.config.yml",
1140
+ ".markforgerc.yaml",
1141
+ ".markforgerc.yml",
1142
+ ".markforgerc",
1143
+ "markforge.config.ts",
1144
+ "markforge.config.js",
1145
+ "markforge.config.mjs",
1146
+ "markforge.config.cjs"
1147
+ ];
1148
+ var DEFAULT_CONFIG = {
1149
+ to: ["docx", "pdf"],
1150
+ outputDir: void 0,
1151
+ theme: "default",
1152
+ css: void 0,
1153
+ orientation: "portrait",
1154
+ paperSize: "A4",
1155
+ margins: {
1156
+ top: "2.5cm",
1157
+ bottom: "2.5cm",
1158
+ left: "2.5cm",
1159
+ right: "2.5cm"
1160
+ },
1161
+ header: void 0,
1162
+ footer: {
1163
+ right: "Page {page} of {pages}"
1164
+ },
1165
+ toc: false,
1166
+ watermark: void 0,
1167
+ embedImages: true,
1168
+ metadata: void 0,
1169
+ watch: false,
1170
+ serve: false,
1171
+ port: 4e3,
1172
+ open: false,
1173
+ bundleHtml: true,
1174
+ syntaxTheme: "github-dark"
1175
+ };
1176
+ function discoverConfigFile(startDir) {
1177
+ const dirsToCheck = [];
1178
+ let curr = path2.resolve(startDir);
1179
+ while (curr) {
1180
+ dirsToCheck.push(curr);
1181
+ const parent = path2.dirname(curr);
1182
+ if (parent === curr) break;
1183
+ curr = parent;
1184
+ }
1185
+ const cwd = path2.resolve(process.cwd());
1186
+ if (!dirsToCheck.includes(cwd)) {
1187
+ dirsToCheck.push(cwd);
1188
+ }
1189
+ for (const dir of dirsToCheck) {
1190
+ for (const filename of DEFAULT_CONFIG_FILENAMES) {
1191
+ const candidate = path2.join(dir, filename);
1192
+ if (fs2.existsSync(candidate) && fs2.statSync(candidate).isFile()) {
1193
+ return candidate;
1194
+ }
1195
+ }
1196
+ }
1197
+ return null;
1198
+ }
1199
+ async function loadConfig(customPath, startDir = process.cwd()) {
1200
+ let resolvedPath = null;
1201
+ if (customPath) {
1202
+ resolvedPath = path2.isAbsolute(customPath) ? customPath : path2.resolve(process.cwd(), customPath);
1203
+ if (!fs2.existsSync(resolvedPath)) {
1204
+ const altPath = path2.resolve(startDir, customPath);
1205
+ if (fs2.existsSync(altPath)) {
1206
+ resolvedPath = altPath;
1207
+ } else {
1208
+ throw new Error(`Configuration file not found: ${resolvedPath}`);
1209
+ }
1210
+ }
1211
+ } else {
1212
+ resolvedPath = discoverConfigFile(startDir);
1213
+ }
1214
+ if (!resolvedPath) {
1215
+ return {
1216
+ config: { ...DEFAULT_CONFIG },
1217
+ configPath: null
1218
+ };
1219
+ }
1220
+ const ext = path2.extname(resolvedPath).toLowerCase();
1221
+ let userConfig = {};
1222
+ try {
1223
+ if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
1224
+ const raw = fs2.readFileSync(resolvedPath, "utf-8").trim();
1225
+ try {
1226
+ userConfig = JSON.parse(raw);
1227
+ } catch {
1228
+ userConfig = YAML.parse(raw) || {};
1229
+ }
1230
+ } else if (ext === ".yaml" || ext === ".yml") {
1231
+ const raw = fs2.readFileSync(resolvedPath, "utf-8");
1232
+ userConfig = YAML.parse(raw) || {};
1233
+ } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
1234
+ try {
1235
+ const fileUrl = `${(0, import_node_url.pathToFileURL)(resolvedPath).href}?t=${Date.now()}`;
1236
+ const mod = await import(fileUrl);
1237
+ const rawExport = mod.default ?? mod.config ?? mod;
1238
+ userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
1239
+ } catch (importErr) {
1240
+ try {
1241
+ const mod = require(resolvedPath);
1242
+ const rawExport = mod.default ?? mod.config ?? mod;
1243
+ userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
1244
+ } catch {
1245
+ throw importErr;
1246
+ }
1247
+ }
1248
+ }
1249
+ } catch (err) {
1250
+ throw new Error(
1251
+ `Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
1252
+ );
1253
+ }
1254
+ if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
1255
+ delete userConfig.$schema;
1256
+ }
1257
+ const parsedConfig = userConfig;
1258
+ const mergedConfig = {
1259
+ ...DEFAULT_CONFIG,
1260
+ ...parsedConfig,
1261
+ margins: {
1262
+ ...DEFAULT_CONFIG.margins,
1263
+ ...parsedConfig.margins || {}
1264
+ },
1265
+ header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
1266
+ footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
1267
+ metadata: {
1268
+ ...DEFAULT_CONFIG.metadata || {},
1269
+ ...parsedConfig.metadata || {}
1270
+ }
1271
+ };
1272
+ return {
1273
+ config: mergedConfig,
1274
+ configPath: resolvedPath
1275
+ };
1276
+ }
1277
+
1278
+ // src/config/resolveConfig.ts
1279
+ var PAPER_DIMENSIONS_TWIP = {
1280
+ A4: { width: 11906, height: 16838 },
1281
+ // 210mm x 297mm
1282
+ Letter: { width: 12240, height: 15840 },
1283
+ // 8.5in x 11in
1284
+ Legal: { width: 12240, height: 20160 },
1285
+ // 8.5in x 14in
1286
+ A3: { width: 16838, height: 23811 },
1287
+ // 297mm x 420mm
1288
+ A5: { width: 8390, height: 11906 }
1289
+ // 148mm x 210mm
1006
1290
  };
1291
+ function parseMarginToTwip(margin, defaultTwip = 1440) {
1292
+ if (typeof margin === "number") return margin;
1293
+ if (!margin) return defaultTwip;
1294
+ const str = margin.trim().toLowerCase();
1295
+ if (str.endsWith("cm")) {
1296
+ const cm = parseFloat(str);
1297
+ return isNaN(cm) ? defaultTwip : Math.round(cm * 566.929);
1298
+ }
1299
+ if (str.endsWith("mm")) {
1300
+ const mm = parseFloat(str);
1301
+ return isNaN(mm) ? defaultTwip : Math.round(mm * 56.6929);
1302
+ }
1303
+ if (str.endsWith("in") || str.endsWith("inch")) {
1304
+ const inch = parseFloat(str);
1305
+ return isNaN(inch) ? defaultTwip : Math.round(inch * 1440);
1306
+ }
1307
+ if (str.endsWith("pt")) {
1308
+ const pt = parseFloat(str);
1309
+ return isNaN(pt) ? defaultTwip : Math.round(pt * 20);
1310
+ }
1311
+ const val = parseFloat(str);
1312
+ return isNaN(val) ? defaultTwip : Math.round(val);
1313
+ }
1314
+ function formatMarginCss(margin, defaultCss = "2.5cm") {
1315
+ if (margin === void 0 || margin === null) return defaultCss;
1316
+ if (typeof margin === "number") return `${margin}pt`;
1317
+ const str = margin.trim();
1318
+ if (!str) return defaultCss;
1319
+ if (/^[0-9.]+$/.test(str)) return `${str}pt`;
1320
+ return str;
1321
+ }
1322
+ function replaceDocumentTokens(template = "", meta) {
1323
+ 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 || "");
1324
+ }
1325
+ function normalizeWatermark(rawWatermark) {
1326
+ if (!rawWatermark) {
1327
+ return void 0;
1328
+ }
1329
+ if (typeof rawWatermark === "string") {
1330
+ const text = rawWatermark.trim();
1331
+ if (!text) return void 0;
1332
+ return {
1333
+ text,
1334
+ color: "#94a3b8",
1335
+ opacity: 0.08,
1336
+ fontSize: 54,
1337
+ rotate: -45,
1338
+ position: "diagonal"
1339
+ };
1340
+ }
1341
+ if (typeof rawWatermark === "object") {
1342
+ if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
1343
+ return {
1344
+ text: rawWatermark.text.trim(),
1345
+ color: rawWatermark.color || "#94a3b8",
1346
+ opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
1347
+ fontSize: rawWatermark.fontSize || 54,
1348
+ rotate: typeof rawWatermark.rotate === "number" ? rawWatermark.rotate : -45,
1349
+ position: rawWatermark.position || "diagonal"
1350
+ };
1351
+ }
1352
+ return void 0;
1353
+ }
1354
+ function normalizeHeaderFooterSlot(rawSlot, parent, meta) {
1355
+ if (!rawSlot) return void 0;
1356
+ if (typeof rawSlot === "string") {
1357
+ const text = replaceDocumentTokens(rawSlot, meta).trim();
1358
+ if (!text) return void 0;
1359
+ return {
1360
+ text,
1361
+ color: (parent == null ? void 0 : parent.color) || "#94A3B8",
1362
+ fontSize: (parent == null ? void 0 : parent.size) || 9,
1363
+ fontFamily: (parent == null ? void 0 : parent.font) || "Segoe UI",
1364
+ bold: false,
1365
+ italic: false
1366
+ };
1367
+ }
1368
+ if (typeof rawSlot === "object") {
1369
+ const text = replaceDocumentTokens(rawSlot.text || "", meta).trim();
1370
+ if (!text) return void 0;
1371
+ return {
1372
+ text,
1373
+ color: rawSlot.color || (parent == null ? void 0 : parent.color) || "#94A3B8",
1374
+ fontSize: rawSlot.fontSize || (parent == null ? void 0 : parent.size) || 9,
1375
+ fontFamily: rawSlot.fontFamily || (parent == null ? void 0 : parent.font) || "Segoe UI",
1376
+ bold: Boolean(rawSlot.bold),
1377
+ italic: Boolean(rawSlot.italic)
1378
+ };
1379
+ }
1380
+ return void 0;
1381
+ }
1382
+ function normalizeHeaderFooter(raw, meta) {
1383
+ if (!raw) return void 0;
1384
+ const left = normalizeHeaderFooterSlot(raw.left, raw, meta);
1385
+ const center = normalizeHeaderFooterSlot(raw.center, raw, meta);
1386
+ const right = normalizeHeaderFooterSlot(raw.right, raw, meta);
1387
+ if (!left && !center && !right) return void 0;
1388
+ return {
1389
+ left,
1390
+ center,
1391
+ right,
1392
+ font: raw.font || "Segoe UI",
1393
+ size: raw.size || 9,
1394
+ color: raw.color || "#94A3B8",
1395
+ divider: Boolean(raw.divider),
1396
+ dividerColor: raw.dividerColor || "#E2E8F0"
1397
+ };
1398
+ }
1399
+ function normalizeSignatures(raw, meta = {}) {
1400
+ if (!raw) return void 0;
1401
+ let rawItems = [];
1402
+ let rawConfig = {};
1403
+ if (Array.isArray(raw)) {
1404
+ rawItems = raw;
1405
+ } else if (typeof raw === "object" && Array.isArray(raw.items)) {
1406
+ rawItems = raw.items;
1407
+ rawConfig = raw;
1408
+ }
1409
+ if (rawItems.length === 0) return void 0;
1410
+ const cappedItems = rawItems.slice(0, 4);
1411
+ const items = cappedItems.map((item) => {
1412
+ const tokenCtx = meta;
1413
+ const rawName = typeof item.name === "string" ? item.name : "";
1414
+ const name = replaceDocumentTokens(rawName, tokenCtx).trim();
1415
+ const title = item.title ? replaceDocumentTokens(item.title, tokenCtx).trim() : void 0;
1416
+ const role = item.role ? replaceDocumentTokens(item.role, tokenCtx).trim() : void 0;
1417
+ let dateStr;
1418
+ if (typeof item.date === "string") {
1419
+ dateStr = replaceDocumentTokens(item.date, tokenCtx).trim();
1420
+ } else if (item.date === true) {
1421
+ dateStr = meta.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1422
+ }
1423
+ let signatureHeight = 60;
1424
+ if (typeof item.signatureHeight === "number") {
1425
+ signatureHeight = item.signatureHeight;
1426
+ } else if (typeof item.signatureHeight === "string") {
1427
+ const parsed = parseFloat(item.signatureHeight);
1428
+ if (!isNaN(parsed)) signatureHeight = parsed;
1429
+ }
1430
+ return {
1431
+ title,
1432
+ name: name || "Authorized Signatory",
1433
+ role,
1434
+ date: dateStr,
1435
+ image: item.image,
1436
+ signatureHeight
1437
+ };
1438
+ });
1439
+ const align = rawConfig.align || (items.length === 1 ? "right" : "space-between");
1440
+ const style = rawConfig.style || "line";
1441
+ const borderColor = rawConfig.borderColor || "#CBD5E1";
1442
+ const titleColor = rawConfig.titleColor || "#64748B";
1443
+ const nameColor = rawConfig.nameColor || "#0F172A";
1444
+ const roleColor = rawConfig.roleColor || "#64748B";
1445
+ const spacingBeforeRaw = rawConfig.spacingBefore ?? "2.5rem";
1446
+ const spacingBefore = formatMarginCss(spacingBeforeRaw, "2.5rem");
1447
+ const spacingBeforeTwip = parseMarginToTwip(spacingBeforeRaw, 600);
1448
+ return {
1449
+ items,
1450
+ align,
1451
+ style,
1452
+ borderColor,
1453
+ titleColor,
1454
+ nameColor,
1455
+ roleColor,
1456
+ spacingBefore,
1457
+ spacingBeforeTwip
1458
+ };
1459
+ }
1460
+ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1461
+ const configMeta = userConfig.metadata || {};
1462
+ const mergedMeta = { ...configMeta, ...frontmatter };
1463
+ const title = mergedMeta.title || "MarkForge Document";
1464
+ const subtitle = mergedMeta.subtitle || void 0;
1465
+ const author = Array.isArray(mergedMeta.author) ? mergedMeta.author.join(", ") : mergedMeta.author || void 0;
1466
+ const date = mergedMeta.date || void 0;
1467
+ const version = mergedMeta.version || void 0;
1468
+ const company = mergedMeta.company || void 0;
1469
+ const lang = mergedMeta.lang || "en";
1470
+ const tokenContext = { title, subtitle, author, version, date, company };
1471
+ const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
1472
+ const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
1473
+ const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
1474
+ const baseDim = PAPER_DIMENSIONS_TWIP[paperSize] || PAPER_DIMENSIONS_TWIP.A4;
1475
+ const paperDimensions = orientation === "landscape" ? { widthTwip: baseDim.height, heightTwip: baseDim.width } : { widthTwip: baseDim.width, heightTwip: baseDim.height };
1476
+ const fmMargin = mergedMeta.margins || {};
1477
+ const cfgMargin = userConfig.margins || {};
1478
+ const defMargin = DEFAULT_CONFIG.margins || {};
1479
+ const topRaw = fmMargin.top ?? cfgMargin.top ?? defMargin.top ?? "2.5cm";
1480
+ const bottomRaw = fmMargin.bottom ?? cfgMargin.bottom ?? defMargin.bottom ?? "2.5cm";
1481
+ const leftRaw = fmMargin.left ?? cfgMargin.left ?? defMargin.left ?? "2.5cm";
1482
+ const rightRaw = fmMargin.right ?? cfgMargin.right ?? defMargin.right ?? "2.5cm";
1483
+ const margins = {
1484
+ top: formatMarginCss(topRaw),
1485
+ bottom: formatMarginCss(bottomRaw),
1486
+ left: formatMarginCss(leftRaw),
1487
+ right: formatMarginCss(rightRaw),
1488
+ topTwip: parseMarginToTwip(topRaw),
1489
+ bottomTwip: parseMarginToTwip(bottomRaw),
1490
+ leftTwip: parseMarginToTwip(leftRaw),
1491
+ rightTwip: parseMarginToTwip(rightRaw)
1492
+ };
1493
+ const rawHeader = mergedMeta.header || userConfig.header || DEFAULT_CONFIG.header;
1494
+ const rawFooter = mergedMeta.footer || userConfig.footer || DEFAULT_CONFIG.footer;
1495
+ const header = normalizeHeaderFooter(rawHeader, tokenContext);
1496
+ const footer = normalizeHeaderFooter(rawFooter, tokenContext);
1497
+ const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
1498
+ const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
1499
+ const watermark = normalizeWatermark(rawWatermark);
1500
+ const rawSignatures = mergedMeta.signatures || userConfig.signatures;
1501
+ const signatures = normalizeSignatures(rawSignatures, tokenContext);
1502
+ const cssList = [];
1503
+ const addCss = (item) => {
1504
+ if (!item) return;
1505
+ if (Array.isArray(item)) cssList.push(...item);
1506
+ else cssList.push(item);
1507
+ };
1508
+ addCss(userConfig.css);
1509
+ addCss(mergedMeta.css);
1510
+ const embedImages = typeof userConfig.embedImages === "boolean" ? userConfig.embedImages : DEFAULT_CONFIG.embedImages;
1511
+ const bundleHtml = typeof userConfig.bundleHtml === "boolean" ? userConfig.bundleHtml : DEFAULT_CONFIG.bundleHtml;
1512
+ const syntaxTheme = userConfig.syntaxTheme || DEFAULT_CONFIG.syntaxTheme || "github-dark";
1513
+ return {
1514
+ title,
1515
+ subtitle,
1516
+ author,
1517
+ date,
1518
+ version,
1519
+ company,
1520
+ lang,
1521
+ theme,
1522
+ orientation,
1523
+ paperSize,
1524
+ paperDimensions,
1525
+ margins,
1526
+ header,
1527
+ footer,
1528
+ toc,
1529
+ signatures,
1530
+ watermark,
1531
+ css: cssList,
1532
+ embedImages,
1533
+ bundleHtml,
1534
+ syntaxTheme
1535
+ };
1536
+ }
1007
1537
 
1008
1538
  // src/core/html/htmlBuilder.ts
1009
1539
  function escapeHtml(str) {
@@ -1056,43 +1586,41 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1056
1586
  return result;
1057
1587
  }
1058
1588
  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;
1589
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1590
+ const baseThemeCss = generateThemeCss(resolved.theme);
1063
1591
  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 += `
1592
+ for (const cssPath of resolved.css) {
1593
+ const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
1594
+ if (fs3.existsSync(fullCssPath)) {
1595
+ customCss += `
1070
1596
  /* Custom CSS: ${cssPath} */
1071
- ` + fs2.readFileSync(fullCssPath, "utf-8");
1072
- }
1597
+ ` + fs3.readFileSync(fullCssPath, "utf-8");
1073
1598
  }
1074
1599
  }
1075
1600
  const inlinedCss = doc.inlinedStyles.join("\n");
1076
1601
  let bodyHtml = "";
1077
- if (metadata.title) {
1602
+ if (resolved.title) {
1078
1603
  bodyHtml += ` <header class="document-header">
1079
1604
  `;
1080
- bodyHtml += ` <h1 class="document-title">${escapeHtml(metadata.title)}</h1>
1605
+ bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
1081
1606
  `;
1082
- if (metadata.subtitle) {
1083
- bodyHtml += ` <div class="document-subtitle">${escapeHtml(metadata.subtitle)}</div>
1607
+ if (resolved.subtitle) {
1608
+ bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
1084
1609
  `;
1085
1610
  }
1086
- if (metadata.author || metadata.date) {
1611
+ if (resolved.author || resolved.date || resolved.version) {
1087
1612
  bodyHtml += ` <div class="document-meta">
1088
1613
  `;
1089
- if (metadata.author) {
1090
- const authors = Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author;
1091
- bodyHtml += ` <span>Author: ${escapeHtml(authors)}</span>
1614
+ if (resolved.author) {
1615
+ bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
1616
+ `;
1617
+ }
1618
+ if (resolved.version) {
1619
+ bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
1092
1620
  `;
1093
1621
  }
1094
- if (metadata.date) {
1095
- bodyHtml += ` <span>Date: ${escapeHtml(metadata.date)}</span>
1622
+ if (resolved.date) {
1623
+ bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
1096
1624
  `;
1097
1625
  }
1098
1626
  bodyHtml += ` </div>
@@ -1101,22 +1629,20 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1101
1629
  bodyHtml += ` </header>
1102
1630
  `;
1103
1631
  }
1104
- if (config.toc || metadata.toc) {
1105
- if (doc.tocEntries.length > 0) {
1106
- bodyHtml += ` <nav class="table-of-contents">
1632
+ if (resolved.toc && doc.tocEntries.length > 0) {
1633
+ bodyHtml += ` <nav class="table-of-contents">
1107
1634
  `;
1108
- bodyHtml += ` <h2>Table of Contents</h2>
1635
+ bodyHtml += ` <h2>Table of Contents</h2>
1109
1636
  <ul>
1110
1637
  `;
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>
1638
+ for (const entry of doc.tocEntries) {
1639
+ const indent = " ".repeat(entry.level);
1640
+ bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1114
1641
  `;
1115
- }
1116
- bodyHtml += ` </ul>
1642
+ }
1643
+ bodyHtml += ` </ul>
1117
1644
  </nav>
1118
1645
  `;
1119
- }
1120
1646
  }
1121
1647
  for (const node of doc.nodes) {
1122
1648
  if (node.type === "heading") {
@@ -1134,7 +1660,7 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1134
1660
  if (node.type === "codeBlock") {
1135
1661
  const lang = node.language || "";
1136
1662
  const langClass = lang ? ` class="language-${escapeHtml(lang)}"` : "";
1137
- const highlighted = highlightCodeToHtml(node.text || "", lang);
1663
+ const highlighted = highlightCodeToHtml(node.text || "", lang, resolved.syntaxTheme);
1138
1664
  bodyHtml += ` <pre><code${langClass}>${highlighted}</code></pre>
1139
1665
  `;
1140
1666
  continue;
@@ -1178,15 +1704,12 @@ ${escapeHtml(node.text || "")}
1178
1704
  for (const row of node.children) {
1179
1705
  bodyHtml += ` <tr>
1180
1706
  `;
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}>
1707
+ for (const cell of row.children || []) {
1708
+ const tag = cell.isHeader ? "th" : "td";
1709
+ const align = cell.align ? ` align="${cell.align}"` : "";
1710
+ const inner = await renderInlinesToHtml(cell.inlines, baseDir);
1711
+ bodyHtml += ` <${tag}${align}>${inner}</${tag}>
1188
1712
  `;
1189
- }
1190
1713
  }
1191
1714
  bodyHtml += ` </tr>
1192
1715
  `;
@@ -1200,58 +1723,187 @@ ${escapeHtml(node.text || "")}
1200
1723
  bodyHtml += ` <${tag}>
1201
1724
  `;
1202
1725
  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>
1726
+ const inner = await renderInlinesToHtml(item.inlines, baseDir);
1727
+ bodyHtml += ` <li>${inner}</li>
1209
1728
  `;
1210
1729
  }
1211
1730
  bodyHtml += ` </${tag}>
1212
1731
  `;
1213
1732
  continue;
1214
1733
  }
1215
- if (node.type === "htmlBlock") {
1216
- bodyHtml += ` ${node.rawHtml}
1734
+ if (node.type === "thematicBreak") {
1735
+ bodyHtml += ` <hr />
1217
1736
  `;
1218
1737
  continue;
1219
1738
  }
1220
- if (node.type === "thematicBreak") {
1221
- bodyHtml += ` <hr />
1739
+ if (node.type === "htmlBlock") {
1740
+ bodyHtml += ` ${node.text}
1222
1741
  `;
1223
1742
  continue;
1224
1743
  }
1225
1744
  }
1226
- const wmConfig = config.watermark ?? metadata.watermark;
1227
- let watermarkHtml = "";
1228
1745
  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";
1746
+ let watermarkHtml = "";
1747
+ if (resolved.watermark) {
1748
+ const wm = resolved.watermark;
1235
1749
  watermarkCss = `
1236
1750
  .document-watermark {
1237
1751
  position: fixed;
1238
- top: 50%;
1239
- left: 50%;
1240
- transform: translate(-50%, -50%) rotate(${rotate}deg);
1241
- font-size: ${fontSize};
1242
- font-weight: 800;
1243
- color: ${color};
1244
- opacity: ${opacity};
1752
+ top: 0;
1753
+ left: 0;
1754
+ width: 100vw;
1755
+ height: 100vh;
1245
1756
  pointer-events: none;
1757
+ z-index: 0;
1246
1758
  user-select: none;
1247
- z-index: 9999;
1248
- text-transform: uppercase;
1249
- letter-spacing: 0.15em;
1250
- white-space: nowrap;
1759
+ -webkit-user-select: none;
1251
1760
  }
1252
- `;
1253
- watermarkHtml = ` <div class="document-watermark">${escapeHtml(wmText)}</div>
1254
- `;
1761
+ .document-container {
1762
+ position: relative;
1763
+ z-index: 1;
1764
+ }
1765
+ @media print {
1766
+ .document-watermark {
1767
+ position: fixed;
1768
+ top: 0;
1769
+ left: 0;
1770
+ width: 100vw;
1771
+ height: 100vh;
1772
+ -webkit-print-color-adjust: exact;
1773
+ print-color-adjust: exact;
1774
+ }
1775
+ }
1776
+ `;
1777
+ watermarkHtml = ` <div id="markforge-watermark" class="document-watermark" aria-hidden="true"></div>
1778
+ <script>
1779
+ (function() {
1780
+ try {
1781
+ var canvas = document.createElement('canvas');
1782
+ var dpr = 3;
1783
+ var width = 800;
1784
+ var height = 1100;
1785
+ canvas.width = Math.round(width * dpr);
1786
+ canvas.height = Math.round(height * dpr);
1787
+ var ctx = canvas.getContext('2d');
1788
+ if (ctx) {
1789
+ ctx.scale(dpr, dpr);
1790
+ ctx.translate(width / 2, height / 2);
1791
+ ctx.rotate((${wm.rotate} * Math.PI) / 180);
1792
+ ctx.textAlign = 'center';
1793
+ ctx.textBaseline = 'middle';
1794
+ ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
1795
+ ctx.fillStyle = '${wm.color}';
1796
+ ctx.globalAlpha = ${wm.opacity};
1797
+ try { ctx.letterSpacing = '0.15em'; } catch(e) {}
1798
+ ctx.fillText(${JSON.stringify(wm.text.toUpperCase())}, 0, 0);
1799
+ var dataUrl = canvas.toDataURL('image/png');
1800
+ var wmEl = document.getElementById('markforge-watermark');
1801
+ if (wmEl) {
1802
+ wmEl.style.backgroundImage = 'url("' + dataUrl + '")';
1803
+ wmEl.style.backgroundRepeat = 'no-repeat';
1804
+ wmEl.style.backgroundPosition = 'center center';
1805
+ wmEl.style.backgroundSize = 'contain';
1806
+ }
1807
+ }
1808
+ } catch(err) {}
1809
+ })();
1810
+ </script>
1811
+ `;
1812
+ }
1813
+ let signaturesHtml = "";
1814
+ let signaturesCss = "";
1815
+ if (resolved.signatures && resolved.signatures.items.length > 0) {
1816
+ const sig = resolved.signatures;
1817
+ const numItems = sig.items.length;
1818
+ let justifyCss = "flex-end";
1819
+ if (sig.align === "left") justifyCss = "flex-start";
1820
+ else if (sig.align === "center") justifyCss = "center";
1821
+ else if (sig.align === "space-between") justifyCss = "space-between";
1822
+ signaturesCss = `
1823
+ .markforge-signatures {
1824
+ margin-top: ${sig.spacingBefore};
1825
+ display: grid;
1826
+ grid-template-columns: ${numItems === 1 ? sig.align === "left" ? "minmax(200px, 280px) 1fr" : sig.align === "center" ? "1fr minmax(200px, 280px) 1fr" : "1fr minmax(200px, 280px)" : `repeat(${numItems}, minmax(0, 1fr))`};
1827
+ gap: 2rem;
1828
+ page-break-inside: avoid;
1829
+ break-inside: avoid;
1830
+ }
1831
+ .markforge-signature-card {
1832
+ ${numItems === 1 && sig.align === "center" ? "grid-column: 2;" : ""}
1833
+ ${numItems === 1 && sig.align === "right" ? "grid-column: 2;" : ""}
1834
+ display: flex;
1835
+ flex-direction: column;
1836
+ ${sig.style === "box" ? `border: 1px solid ${sig.borderColor}; border-radius: 6px; padding: 14px 18px; background-color: var(--mf-card-bg, #F8FAFC);` : ""}
1837
+ }
1838
+ .markforge-sig-title {
1839
+ font-size: 0.85rem;
1840
+ color: ${sig.titleColor};
1841
+ font-weight: 600;
1842
+ margin-bottom: 6px;
1843
+ }
1844
+ .markforge-sig-space {
1845
+ height: var(--sig-height, 60px);
1846
+ display: flex;
1847
+ align-items: center;
1848
+ justify-content: center;
1849
+ margin-bottom: 6px;
1850
+ }
1851
+ .markforge-sig-space img {
1852
+ max-height: 100%;
1853
+ max-width: 100%;
1854
+ object-fit: contain;
1855
+ }
1856
+ .markforge-sig-line {
1857
+ ${sig.style === "line" ? `border-bottom: 1.5px solid ${sig.borderColor}; margin-bottom: 8px;` : ""}
1858
+ }
1859
+ .markforge-sig-name {
1860
+ font-size: 0.95rem;
1861
+ font-weight: 700;
1862
+ color: ${sig.nameColor};
1863
+ }
1864
+ .markforge-sig-role {
1865
+ font-size: 0.82rem;
1866
+ color: ${sig.roleColor};
1867
+ margin-top: 2px;
1868
+ }
1869
+ .markforge-sig-date {
1870
+ font-size: 0.78rem;
1871
+ color: ${sig.roleColor};
1872
+ margin-top: 2px;
1873
+ }
1874
+ @media print {
1875
+ .markforge-signatures {
1876
+ page-break-inside: avoid;
1877
+ break-inside: avoid;
1878
+ }
1879
+ }
1880
+ `;
1881
+ const itemCards = sig.items.map((item) => {
1882
+ const titleHtml = item.title ? `<div class="markforge-sig-title">${escapeHtml(item.title)}</div>` : "";
1883
+ let signSpaceHtml = "";
1884
+ if (item.image) {
1885
+ signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"><img src="${escapeHtml(item.image)}" alt="Signature" /></div>`;
1886
+ } else {
1887
+ signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"></div>`;
1888
+ }
1889
+ const lineHtml = sig.style === "line" ? `<div class="markforge-sig-line"></div>` : "";
1890
+ const nameHtml = `<div class="markforge-sig-name">${escapeHtml(item.name)}</div>`;
1891
+ const roleHtml = item.role ? `<div class="markforge-sig-role">${escapeHtml(item.role)}</div>` : "";
1892
+ const dateHtml = item.date ? `<div class="markforge-sig-date">Date: ${escapeHtml(item.date)}</div>` : "";
1893
+ return ` <div class="markforge-signature-card">
1894
+ ${titleHtml}
1895
+ ${signSpaceHtml}
1896
+ ${lineHtml}
1897
+ ${nameHtml}
1898
+ ${roleHtml}
1899
+ ${dateHtml}
1900
+ </div>`;
1901
+ }).join("\n");
1902
+ signaturesHtml = `
1903
+ <div class="markforge-signatures">
1904
+ ${itemCards}
1905
+ </div>
1906
+ `;
1255
1907
  }
1256
1908
  const hasMermaid = doc.nodes.some((n) => n.type === "mermaid");
1257
1909
  const mermaidScript = hasMermaid ? `<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
@@ -1269,24 +1921,24 @@ ${escapeHtml(node.text || "")}
1269
1921
  }
1270
1922
  });
1271
1923
  </script>` : "";
1272
- const documentTitle = metadata.title || "MarkForge Document";
1273
1924
  return `<!DOCTYPE html>
1274
- <html lang="${metadata.lang || "en"}">
1925
+ <html lang="${resolved.lang}">
1275
1926
  <head>
1276
1927
  <meta charset="UTF-8">
1277
1928
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1278
- <title>${escapeHtml(documentTitle)}</title>
1929
+ <title>${escapeHtml(resolved.title)}</title>
1279
1930
  <style>
1280
1931
  ${THEME_COMPONENTS}
1281
1932
  ${baseThemeCss}
1282
1933
  ${customCss}
1283
1934
  ${inlinedCss}
1284
1935
  ${watermarkCss}
1936
+ ${signaturesCss}
1285
1937
  </style>
1286
1938
  </head>
1287
1939
  <body>
1288
1940
  ${watermarkHtml} <div class="document-container">
1289
- ${bodyHtml} </div>
1941
+ ${bodyHtml}${signaturesHtml} </div>
1290
1942
  ${mermaidScript}
1291
1943
  </body>
1292
1944
  </html>`;
@@ -1294,10 +1946,10 @@ ${bodyHtml} </div>
1294
1946
 
1295
1947
  // src/core/pdf/pdfBuilder.ts
1296
1948
  function findChromeExecutable() {
1297
- if (process.env.CHROME_PATH && fs3.existsSync(process.env.CHROME_PATH)) {
1949
+ if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
1298
1950
  return process.env.CHROME_PATH;
1299
1951
  }
1300
- if (process.env.PUPPETEER_EXECUTABLE_PATH && fs3.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
1952
+ if (process.env.PUPPETEER_EXECUTABLE_PATH && fs4.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
1301
1953
  return process.env.PUPPETEER_EXECUTABLE_PATH;
1302
1954
  }
1303
1955
  const isWin = process.platform === "win32";
@@ -1319,14 +1971,14 @@ function findChromeExecutable() {
1319
1971
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
1320
1972
  "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
1321
1973
  "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
1974
+ // Windows — Microsoft Edge (Native Windows 10/11 browser, enterprise whitelist friendly)
1975
+ `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
1976
+ `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
1977
+ `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
1322
1978
  // Windows — Google Chrome
1323
1979
  `${winProgramFiles}\\Google\\Chrome\\Application\\chrome.exe`,
1324
1980
  `${winProgramFilesX86}\\Google\\Chrome\\Application\\chrome.exe`,
1325
1981
  `${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
1982
  // Windows — Brave Browser
1331
1983
  `${winProgramFiles}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
1332
1984
  `${winProgramFilesX86}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
@@ -1336,7 +1988,7 @@ function findChromeExecutable() {
1336
1988
  ].filter(Boolean);
1337
1989
  for (const candidate of candidates) {
1338
1990
  try {
1339
- if (fs3.existsSync(candidate)) {
1991
+ if (fs4.existsSync(candidate)) {
1340
1992
  return candidate;
1341
1993
  }
1342
1994
  } catch {
@@ -1349,7 +2001,7 @@ function findChromeExecutable() {
1349
2001
  const res = (0, import_node_child_process.spawnSync)(cmd, [name], { encoding: "utf-8" });
1350
2002
  if (res.status === 0 && res.stdout.trim()) {
1351
2003
  const binPath = res.stdout.split(/\r?\n/)[0].trim();
1352
- if (fs3.existsSync(binPath)) return binPath;
2004
+ if (fs4.existsSync(binPath)) return binPath;
1353
2005
  }
1354
2006
  }
1355
2007
  } catch {
@@ -1357,22 +2009,50 @@ function findChromeExecutable() {
1357
2009
  return null;
1358
2010
  }
1359
2011
  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, "\\\\");
2012
+ var _a, _b, _c, _d, _e, _f;
2013
+ const resolved = resolveDocumentConfig(metadata || {}, config);
2014
+ const size = resolved.paperSize;
2015
+ const orientation = resolved.orientation;
2016
+ const top = resolved.margins.top;
2017
+ const bottom = resolved.margins.bottom;
2018
+ const left = resolved.margins.left;
2019
+ const right = resolved.margins.right;
2020
+ const esc = (s) => s.replace(/"/g, '\\"').replace(/\\/g, "\\\\");
2021
+ const buildZoneCss = (pos, zone, isPageCounter = false) => {
2022
+ if (!zone && !isPageCounter) return "";
2023
+ const color = (zone == null ? void 0 : zone.color) || "#94a3b8";
2024
+ const fontSize = (zone == null ? void 0 : zone.fontSize) ? `${zone.fontSize}pt` : "9pt";
2025
+ const fontFamily = (zone == null ? void 0 : zone.fontFamily) ? `font-family: ${zone.fontFamily};` : "";
2026
+ const fontWeight = (zone == null ? void 0 : zone.bold) ? "font-weight: bold;" : "";
2027
+ const fontStyle = (zone == null ? void 0 : zone.italic) ? "font-style: italic;" : "";
2028
+ let content = "";
2029
+ if (isPageCounter) {
2030
+ if ((zone == null ? void 0 : zone.text) && (zone.text.includes("{page}") || zone.text.includes("{pages}"))) {
2031
+ const parts = zone.text.split(/(\{page\}|\{pages\})/gi);
2032
+ const cssParts = parts.map((part) => {
2033
+ if (part.toLowerCase() === "{page}") return "counter(page)";
2034
+ if (part.toLowerCase() === "{pages}") return "counter(pages)";
2035
+ return `"${esc(part)}"`;
2036
+ });
2037
+ content = cssParts.join(" ");
2038
+ } else if (zone == null ? void 0 : zone.text) {
2039
+ content = `"${esc(zone.text)}"`;
2040
+ } else {
2041
+ content = `"Page " counter(page) " of " counter(pages)`;
2042
+ }
2043
+ } else if (zone == null ? void 0 : zone.text) {
2044
+ content = `"${esc(zone.text)}"`;
2045
+ }
2046
+ if (!content) return "";
2047
+ return `@${pos} {
2048
+ content: ${content};
2049
+ font-size: ${fontSize};
2050
+ color: ${color};
2051
+ ${fontFamily}
2052
+ ${fontWeight}
2053
+ ${fontStyle}
2054
+ }`;
2055
+ };
1376
2056
  const pagedCss = `
1377
2057
  @page {
1378
2058
  size: ${size} ${orientation};
@@ -1380,15 +2060,12 @@ function injectPagedMediaStyles(html, config, metadata) {
1380
2060
  margin-bottom: ${bottom};
1381
2061
  margin-left: ${left};
1382
2062
  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
- }
2063
+ ${buildZoneCss("top-left", (_a = resolved.header) == null ? void 0 : _a.left)}
2064
+ ${buildZoneCss("top-center", (_b = resolved.header) == null ? void 0 : _b.center)}
2065
+ ${buildZoneCss("top-right", (_c = resolved.header) == null ? void 0 : _c.right)}
2066
+ ${buildZoneCss("bottom-left", (_d = resolved.footer) == null ? void 0 : _d.left)}
2067
+ ${buildZoneCss("bottom-center", (_e = resolved.footer) == null ? void 0 : _e.center)}
2068
+ ${buildZoneCss("bottom-right", (_f = resolved.footer) == null ? void 0 : _f.right, true)}
1392
2069
  }
1393
2070
  @media print {
1394
2071
  body { padding: 0; }
@@ -1445,56 +2122,72 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1445
2122
  if (chromePath) {
1446
2123
  const tmpId = Math.random().toString(36).substring(2, 9);
1447
2124
  const tmpDir = os.tmpdir();
1448
- const tmpHtml = path3.join(tmpDir, `markforge_${tmpId}.html`);
1449
- const tmpPdf = path3.join(tmpDir, `markforge_${tmpId}.pdf`);
2125
+ const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
2126
+ const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
2127
+ const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
2128
+ const isolatedFlags = [
2129
+ `--user-data-dir=${tmpProfile}`,
2130
+ "--no-first-run",
2131
+ "--no-default-browser-check",
2132
+ "--disable-sync",
2133
+ "--disable-background-networking",
2134
+ "--disable-component-update",
2135
+ "--disable-default-apps",
2136
+ "--disable-extensions",
2137
+ "--disable-domain-reliability",
2138
+ "--disable-client-side-phishing-detection",
2139
+ "--disable-breakpad",
2140
+ "--disable-component-extensions-with-background-pages",
2141
+ "--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
2142
+ "--password-store=basic",
2143
+ "--use-mock-keychain",
2144
+ "--mute-audio",
2145
+ "--no-service-autorun",
2146
+ "--disable-gpu",
2147
+ "--no-sandbox",
2148
+ "--disable-setuid-sandbox",
2149
+ "--allow-file-access-from-files",
2150
+ "--disable-web-security",
2151
+ "--force-color-profile=srgb",
2152
+ "--no-pdf-header-footer"
2153
+ ];
1450
2154
  try {
1451
- fs3.writeFileSync(tmpHtml, pagedHtml, "utf-8");
1452
- const fileUrl = (0, import_node_url.pathToFileURL)(tmpHtml).href;
2155
+ fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
2156
+ const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
1453
2157
  let res = (0, import_node_child_process.spawnSync)(
1454
2158
  chromePath,
1455
2159
  [
1456
2160
  "--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",
2161
+ ...isolatedFlags,
1463
2162
  "--run-all-compositor-stages-before-draw",
1464
2163
  "--virtual-time-budget=8000",
1465
- "--no-pdf-header-footer",
1466
2164
  `--print-to-pdf=${tmpPdf}`,
1467
2165
  fileUrl
1468
2166
  ],
1469
2167
  { timeout: 3e4 }
1470
2168
  );
1471
- if ((res.status !== 0 || !fs3.existsSync(tmpPdf)) && chromePath) {
2169
+ if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
1472
2170
  res = (0, import_node_child_process.spawnSync)(
1473
2171
  chromePath,
1474
2172
  [
1475
2173
  "--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",
2174
+ ...isolatedFlags,
1483
2175
  `--print-to-pdf=${tmpPdf}`,
1484
2176
  fileUrl
1485
2177
  ],
1486
2178
  { timeout: 3e4 }
1487
2179
  );
1488
2180
  }
1489
- if (fs3.existsSync(tmpPdf) && fs3.statSync(tmpPdf).size > 0) {
1490
- const pdfBuffer = fs3.readFileSync(tmpPdf);
2181
+ if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
2182
+ const pdfBuffer = fs4.readFileSync(tmpPdf);
1491
2183
  return pdfBuffer;
1492
2184
  }
1493
2185
  } catch {
1494
2186
  } finally {
1495
2187
  try {
1496
- if (fs3.existsSync(tmpHtml)) fs3.unlinkSync(tmpHtml);
1497
- if (fs3.existsSync(tmpPdf)) fs3.unlinkSync(tmpPdf);
2188
+ if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
2189
+ if (fs4.existsSync(tmpPdf)) fs4.unlinkSync(tmpPdf);
2190
+ if (fs4.existsSync(tmpProfile)) fs4.rmSync(tmpProfile, { recursive: true, force: true });
1498
2191
  } catch {
1499
2192
  }
1500
2193
  }
@@ -1510,8 +2203,8 @@ async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
1510
2203
  }
1511
2204
  const tmpId = Math.random().toString(36).substring(2, 9);
1512
2205
  const tmpDir = os2.tmpdir();
1513
- const tmpHtml = path4.join(tmpDir, `mermaid_${tmpId}.html`);
1514
- const tmpScreenshot = path4.join(tmpDir, `mermaid_${tmpId}.png`);
2206
+ const tmpHtml = path5.join(tmpDir, `mermaid_${tmpId}.html`);
2207
+ const tmpScreenshot = path5.join(tmpDir, `mermaid_${tmpId}.png`);
1515
2208
  const htmlContent = `<!DOCTYPE html>
1516
2209
  <html>
1517
2210
  <head>
@@ -1550,8 +2243,8 @@ ${mermaidCode}
1550
2243
  </body>
1551
2244
  </html>`;
1552
2245
  try {
1553
- fs4.writeFileSync(tmpHtml, htmlContent, "utf-8");
1554
- const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
2246
+ fs5.writeFileSync(tmpHtml, htmlContent, "utf-8");
2247
+ const fileUrl = (0, import_node_url3.pathToFileURL)(tmpHtml).href;
1555
2248
  const res = (0, import_node_child_process2.spawnSync)(
1556
2249
  chromePath,
1557
2250
  [
@@ -1569,15 +2262,15 @@ ${mermaidCode}
1569
2262
  ],
1570
2263
  { timeout: 15e3 }
1571
2264
  );
1572
- if (res.status === 0 && fs4.existsSync(tmpScreenshot)) {
1573
- const buffer = fs4.readFileSync(tmpScreenshot);
2265
+ if (res.status === 0 && fs5.existsSync(tmpScreenshot)) {
2266
+ const buffer = fs5.readFileSync(tmpScreenshot);
1574
2267
  return buffer;
1575
2268
  }
1576
2269
  } catch {
1577
2270
  } finally {
1578
2271
  try {
1579
- if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
1580
- if (fs4.existsSync(tmpScreenshot)) fs4.unlinkSync(tmpScreenshot);
2272
+ if (fs5.existsSync(tmpHtml)) fs5.unlinkSync(tmpHtml);
2273
+ if (fs5.existsSync(tmpScreenshot)) fs5.unlinkSync(tmpScreenshot);
1581
2274
  } catch {
1582
2275
  }
1583
2276
  }
@@ -1585,7 +2278,7 @@ ${mermaidCode}
1585
2278
  }
1586
2279
 
1587
2280
  // src/core/docx/docxBuilder.ts
1588
- function parseMarginToTwip(margin, defaultTwip = 1440) {
2281
+ function parseMarginToTwip2(margin, defaultTwip = 1440) {
1589
2282
  if (typeof margin === "number") return margin;
1590
2283
  if (!margin) return defaultTwip;
1591
2284
  const str = margin.trim().toLowerCase();
@@ -1608,7 +2301,7 @@ function parseMarginToTwip(margin, defaultTwip = 1440) {
1608
2301
  const val = parseFloat(str);
1609
2302
  return isNaN(val) ? defaultTwip : Math.round(val);
1610
2303
  }
1611
- async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
2304
+ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), options = {}) {
1612
2305
  const runs = [];
1613
2306
  for (const span of spans) {
1614
2307
  if (span.type === "image" && span.url) {
@@ -1638,7 +2331,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1638
2331
  new import_docx.TextRun({
1639
2332
  text: span.content,
1640
2333
  style: "Hyperlink",
1641
- color: "0969DA",
2334
+ color: "009DA0",
2335
+ font: options.font,
2336
+ size: options.size,
2337
+ bold: options.bold,
1642
2338
  underline: {}
1643
2339
  })
1644
2340
  ],
@@ -1651,7 +2347,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1651
2347
  runs.push(
1652
2348
  new import_docx.TextRun({
1653
2349
  text: span.content,
1654
- bold: true
2350
+ bold: true,
2351
+ font: options.font,
2352
+ size: options.size,
2353
+ color: options.color,
2354
+ italics: options.italics
1655
2355
  })
1656
2356
  );
1657
2357
  continue;
@@ -1660,7 +2360,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1660
2360
  runs.push(
1661
2361
  new import_docx.TextRun({
1662
2362
  text: span.content,
1663
- italics: true
2363
+ italics: true,
2364
+ font: options.font,
2365
+ size: options.size,
2366
+ color: options.color,
2367
+ bold: options.bold
1664
2368
  })
1665
2369
  );
1666
2370
  continue;
@@ -1669,7 +2373,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1669
2373
  runs.push(
1670
2374
  new import_docx.TextRun({
1671
2375
  text: span.content,
1672
- strike: true
2376
+ strike: true,
2377
+ font: options.font,
2378
+ size: options.size,
2379
+ color: options.color
1673
2380
  })
1674
2381
  );
1675
2382
  continue;
@@ -1677,22 +2384,23 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1677
2384
  if (span.type === "code") {
1678
2385
  runs.push(
1679
2386
  new import_docx.TextRun({
1680
- text: ` ${span.content} `,
2387
+ text: span.content,
1681
2388
  font: "Consolas",
2389
+ size: options.size ? options.size - 2 : 19,
2390
+ color: "0F172A",
1682
2391
  shading: {
1683
2392
  type: import_docx.ShadingType.CLEAR,
1684
- fill: "F1F5F9",
1685
- color: "0F172A"
2393
+ fill: "F1F5F9"
1686
2394
  }
1687
2395
  })
1688
2396
  );
1689
2397
  continue;
1690
2398
  }
1691
2399
  if (span.type === "htmlInline") {
1692
- let colorHex;
2400
+ let colorHex = options.color;
1693
2401
  let bgHex;
1694
- let isBold = false;
1695
- let isItalic = false;
2402
+ let isBold = !!options.bold;
2403
+ let isItalic = !!options.italics;
1696
2404
  if (span.style) {
1697
2405
  if (span.style.color) {
1698
2406
  colorHex = span.style.color.replace("#", "").trim();
@@ -1708,13 +2416,9 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1708
2416
  }
1709
2417
  }
1710
2418
  if (span.children && span.children.length > 0) {
1711
- const childRuns = await convertInlinesToTextRuns(span.children, baseDir);
2419
+ const childRuns = await convertInlinesToTextRuns(span.children, baseDir, options);
1712
2420
  for (const child of childRuns) {
1713
- if (child instanceof import_docx.TextRun) {
1714
- runs.push(child);
1715
- } else {
1716
- runs.push(child);
1717
- }
2421
+ runs.push(child);
1718
2422
  }
1719
2423
  continue;
1720
2424
  }
@@ -1724,6 +2428,8 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1724
2428
  color: colorHex,
1725
2429
  bold: isBold,
1726
2430
  italics: isItalic,
2431
+ font: options.font,
2432
+ size: options.size,
1727
2433
  shading: bgHex ? { type: import_docx.ShadingType.CLEAR, fill: bgHex } : void 0
1728
2434
  })
1729
2435
  );
@@ -1731,91 +2437,239 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1731
2437
  }
1732
2438
  runs.push(
1733
2439
  new import_docx.TextRun({
1734
- text: span.content
2440
+ text: span.content,
2441
+ font: options.font,
2442
+ size: options.size,
2443
+ color: options.color,
2444
+ bold: options.bold,
2445
+ italics: options.italics
1735
2446
  })
1736
2447
  );
1737
2448
  }
1738
2449
  return runs;
1739
2450
  }
1740
2451
  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 };
2452
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2453
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1743
2454
  const docElements = [];
1744
- if (metadata.title) {
2455
+ const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
2456
+ const primaryHex = (themeProps.primaryColor || "#33CDCF").replace("#", "");
2457
+ const primaryDarkHex = (themeProps.primaryDark || "#009DA0").replace("#", "");
2458
+ const textHex = (themeProps.textColor || "#0F172A").replace("#", "");
2459
+ const textMutedHex = (themeProps.textMuted || "#64748B").replace("#", "");
2460
+ const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
2461
+ const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
2462
+ const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
2463
+ if (resolved.title) {
1745
2464
  docElements.push(
1746
2465
  new import_docx.Paragraph({
1747
- text: metadata.title,
1748
- heading: import_docx.HeadingLevel.TITLE,
1749
- spacing: { before: 200, after: 120 }
2466
+ children: [
2467
+ new import_docx.TextRun({
2468
+ text: resolved.title,
2469
+ bold: true,
2470
+ size: 44,
2471
+ // 22pt
2472
+ color: textHex,
2473
+ font: defaultFont
2474
+ })
2475
+ ],
2476
+ spacing: { before: 120, after: 80 }
1750
2477
  })
1751
2478
  );
1752
- if (metadata.subtitle) {
2479
+ if (resolved.subtitle) {
1753
2480
  docElements.push(
1754
2481
  new import_docx.Paragraph({
1755
2482
  children: [
1756
2483
  new import_docx.TextRun({
1757
- text: metadata.subtitle,
1758
- italics: true,
1759
- color: "64748B",
1760
- size: 24
2484
+ text: resolved.subtitle,
2485
+ color: textMutedHex,
2486
+ size: 24,
1761
2487
  // 12pt
2488
+ font: defaultFont
1762
2489
  })
1763
2490
  ],
1764
- spacing: { after: 180 }
2491
+ spacing: { before: 60, after: 120 }
1765
2492
  })
1766
2493
  );
1767
2494
  }
1768
- if (metadata.author || metadata.date) {
2495
+ if (resolved.author || resolved.date || resolved.version || resolved.company) {
1769
2496
  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}`);
2497
+ if (resolved.author) metaParts.push(`Author: ${resolved.author}`);
2498
+ if (resolved.version) metaParts.push(`Version: ${resolved.version}`);
2499
+ if (resolved.date) metaParts.push(`Date: ${resolved.date}`);
1772
2500
  docElements.push(
1773
2501
  new import_docx.Paragraph({
1774
2502
  children: [
1775
2503
  new import_docx.TextRun({
1776
- text: metaParts.join(" | "),
1777
- color: "94A3B8",
1778
- size: 20
1779
- // 10pt
2504
+ text: metaParts.join(" "),
2505
+ color: textMutedHex,
2506
+ size: 18,
2507
+ // 9pt
2508
+ font: defaultFont
1780
2509
  })
1781
2510
  ],
1782
- spacing: { after: 360 },
2511
+ spacing: { before: 40, after: 240 },
1783
2512
  border: {
1784
2513
  bottom: {
1785
- color: "E2E8F0",
1786
- space: 10,
2514
+ color: borderHex,
2515
+ space: 12,
1787
2516
  style: import_docx.BorderStyle.SINGLE,
1788
- size: 6
2517
+ size: 4
1789
2518
  }
1790
2519
  }
1791
2520
  })
1792
2521
  );
1793
2522
  }
1794
2523
  }
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(
2524
+ if (resolved.toc) {
2525
+ const headingNodes = doc.nodes.filter(
2526
+ (n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
2527
+ );
2528
+ if (headingNodes.length > 0) {
2529
+ const tocParagraphs = [
1805
2530
  new import_docx.Paragraph({
1806
- heading: headingLevel,
1807
- children: runs,
1808
- spacing: { before: 240, after: 120 }
2531
+ children: [
2532
+ new import_docx.TextRun({
2533
+ text: "TABLE OF CONTENTS",
2534
+ bold: true,
2535
+ size: 18,
2536
+ // 9pt
2537
+ color: textMutedHex,
2538
+ font: defaultFont
2539
+ })
2540
+ ],
2541
+ spacing: { after: 120 },
2542
+ border: {
2543
+ bottom: {
2544
+ color: borderHex,
2545
+ space: 6,
2546
+ style: import_docx.BorderStyle.SINGLE,
2547
+ size: 4
2548
+ }
2549
+ }
1809
2550
  })
1810
- );
2551
+ ];
2552
+ for (const h of headingNodes) {
2553
+ const indentLeft = (h.level - 1) * 240;
2554
+ tocParagraphs.push(
2555
+ new import_docx.Paragraph({
2556
+ indent: { left: indentLeft },
2557
+ children: [
2558
+ new import_docx.TextRun({
2559
+ text: h.text,
2560
+ bold: h.level === 1,
2561
+ color: primaryDarkHex,
2562
+ size: h.level === 1 ? 21 : 20,
2563
+ font: defaultFont
2564
+ })
2565
+ ],
2566
+ spacing: { before: 20, after: 20 }
2567
+ })
2568
+ );
2569
+ }
2570
+ const tocCard = new import_docx.Table({
2571
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
2572
+ columnWidths: [9e3],
2573
+ rows: [
2574
+ new import_docx.TableRow({
2575
+ children: [
2576
+ new import_docx.TableCell({
2577
+ width: { size: 9e3, type: import_docx.WidthType.DXA },
2578
+ shading: { fill: cardBgHex, type: import_docx.ShadingType.CLEAR },
2579
+ margins: { top: 140, bottom: 140, left: 180, right: 180 },
2580
+ borders: {
2581
+ top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
2582
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
2583
+ left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
2584
+ right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex }
2585
+ },
2586
+ children: tocParagraphs
2587
+ })
2588
+ ]
2589
+ })
2590
+ ]
2591
+ });
2592
+ docElements.push(tocCard);
2593
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 200 } }));
2594
+ }
2595
+ }
2596
+ for (const node of doc.nodes) {
2597
+ if (node.type === "heading") {
2598
+ if (node.level === 1) {
2599
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2600
+ font: defaultFont,
2601
+ size: 34,
2602
+ // 17pt
2603
+ color: textHex,
2604
+ bold: true
2605
+ });
2606
+ docElements.push(
2607
+ new import_docx.Paragraph({
2608
+ heading: import_docx.HeadingLevel.HEADING_1,
2609
+ children: runs,
2610
+ spacing: { before: 360, after: 140 },
2611
+ border: {
2612
+ bottom: {
2613
+ color: primaryHex,
2614
+ space: 6,
2615
+ style: import_docx.BorderStyle.SINGLE,
2616
+ size: 16
2617
+ }
2618
+ }
2619
+ })
2620
+ );
2621
+ } else if (node.level === 2) {
2622
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2623
+ font: defaultFont,
2624
+ size: 26,
2625
+ // 13pt
2626
+ color: primaryDarkHex,
2627
+ bold: true
2628
+ });
2629
+ docElements.push(
2630
+ new import_docx.Paragraph({
2631
+ heading: import_docx.HeadingLevel.HEADING_2,
2632
+ children: runs,
2633
+ spacing: { before: 280, after: 100 },
2634
+ border: {
2635
+ bottom: {
2636
+ color: borderHex,
2637
+ space: 4,
2638
+ style: import_docx.BorderStyle.SINGLE,
2639
+ size: 4
2640
+ }
2641
+ }
2642
+ })
2643
+ );
2644
+ } else {
2645
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2646
+ font: defaultFont,
2647
+ size: 22,
2648
+ // 11pt
2649
+ color: textHex,
2650
+ bold: true
2651
+ });
2652
+ docElements.push(
2653
+ new import_docx.Paragraph({
2654
+ heading: node.level === 3 ? import_docx.HeadingLevel.HEADING_3 : import_docx.HeadingLevel.HEADING_4,
2655
+ children: runs,
2656
+ spacing: { before: 200, after: 80 }
2657
+ })
2658
+ );
2659
+ }
1811
2660
  continue;
1812
2661
  }
1813
2662
  if (node.type === "paragraph") {
1814
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2663
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2664
+ font: defaultFont,
2665
+ size: 22,
2666
+ // 11pt
2667
+ color: "334155"
2668
+ });
1815
2669
  docElements.push(
1816
2670
  new import_docx.Paragraph({
1817
2671
  children: runs,
1818
- spacing: { before: 60, after: 140 }
2672
+ spacing: { before: 40, after: 140, line: 280 }
1819
2673
  })
1820
2674
  );
1821
2675
  continue;
@@ -1887,7 +2741,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1887
2741
  bgFill = "F5F3FF";
1888
2742
  title = "IMPORTANT";
1889
2743
  }
1890
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2744
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2745
+ font: defaultFont,
2746
+ size: 21,
2747
+ color: "334155"
2748
+ });
1891
2749
  const calloutTable = new import_docx.Table({
1892
2750
  width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
1893
2751
  columnWidths: [9e3],
@@ -1902,7 +2760,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1902
2760
  top: { style: import_docx.BorderStyle.NONE },
1903
2761
  bottom: { style: import_docx.BorderStyle.NONE },
1904
2762
  right: { style: import_docx.BorderStyle.NONE },
1905
- left: { style: import_docx.BorderStyle.SINGLE, size: 16, color: borderColor }
2763
+ left: { style: import_docx.BorderStyle.SINGLE, size: 20, color: borderColor }
1906
2764
  },
1907
2765
  children: [
1908
2766
  new import_docx.Paragraph({
@@ -1911,13 +2769,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1911
2769
  text: `[${title}]`,
1912
2770
  bold: true,
1913
2771
  color: borderColor,
2772
+ font: defaultFont,
1914
2773
  size: 20
1915
2774
  })
1916
2775
  ],
1917
- spacing: { after: 60 }
2776
+ spacing: { after: 40 }
1918
2777
  }),
1919
2778
  new import_docx.Paragraph({
1920
- children: runs
2779
+ children: runs,
2780
+ spacing: { line: 270 }
1921
2781
  })
1922
2782
  ]
1923
2783
  })
@@ -1935,17 +2795,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1935
2795
  if (lines.length > 0) {
1936
2796
  for (const lineText of lines) {
1937
2797
  const spans = parseInlineSpans(lineText.replace(/^>\s?/, "").trim());
1938
- const lineRuns = await convertInlinesToTextRuns(spans, baseDir);
2798
+ const lineRuns = await convertInlinesToTextRuns(spans, baseDir, {
2799
+ font: defaultFont,
2800
+ size: 21,
2801
+ color: "475569",
2802
+ italics: true
2803
+ });
1939
2804
  quoteParagraphs.push(
1940
2805
  new import_docx.Paragraph({
1941
2806
  children: lineRuns,
1942
- spacing: { before: 40, after: 40 }
2807
+ spacing: { before: 20, after: 20, line: 260 }
1943
2808
  })
1944
2809
  );
1945
2810
  }
1946
2811
  } else {
1947
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1948
- quoteParagraphs.push(new import_docx.Paragraph({ children: runs }));
2812
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2813
+ font: defaultFont,
2814
+ size: 21,
2815
+ color: "475569",
2816
+ italics: true
2817
+ });
2818
+ quoteParagraphs.push(new import_docx.Paragraph({ children: runs, spacing: { line: 260 } }));
1949
2819
  }
1950
2820
  const quoteTable = new import_docx.Table({
1951
2821
  width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
@@ -1961,7 +2831,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1961
2831
  top: { style: import_docx.BorderStyle.NONE },
1962
2832
  bottom: { style: import_docx.BorderStyle.NONE },
1963
2833
  right: { style: import_docx.BorderStyle.NONE },
1964
- left: { style: import_docx.BorderStyle.SINGLE, size: 12, color: "33CDCF" }
2834
+ left: { style: import_docx.BorderStyle.SINGLE, size: 16, color: primaryHex }
1965
2835
  },
1966
2836
  children: quoteParagraphs
1967
2837
  })
@@ -1996,7 +2866,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1996
2866
  docElements.push(
1997
2867
  new import_docx.Paragraph({
1998
2868
  children: [
1999
- new import_docx.TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: "33CDCF" })
2869
+ new import_docx.TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: primaryHex })
2000
2870
  ],
2001
2871
  spacing: { before: 60, after: 60 }
2002
2872
  })
@@ -2008,8 +2878,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2008
2878
  const tableRows = [];
2009
2879
  const numCols = ((_b = (_a = node.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) || 1;
2010
2880
  const colWidth = Math.floor(9e3 / numCols);
2011
- for (const rowNode of node.children) {
2881
+ for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
2882
+ const rowNode = node.children[rowIdx];
2012
2883
  const cells = [];
2884
+ const isHeader = rowNode.isHeader;
2885
+ const rowBg = isHeader ? "F1F5F9" : rowIdx % 2 === 0 ? "FFFFFF" : "F8FAFC";
2013
2886
  if (rowNode.children) {
2014
2887
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
2015
2888
  const cellNode = rowNode.children[colIdx];
@@ -2017,22 +2890,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2017
2890
  let alignment = import_docx.AlignmentType.LEFT;
2018
2891
  if (align === "center") alignment = import_docx.AlignmentType.CENTER;
2019
2892
  if (align === "right") alignment = import_docx.AlignmentType.RIGHT;
2020
- const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir);
2893
+ const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir, {
2894
+ font: defaultFont,
2895
+ size: 20,
2896
+ color: isHeader ? "0F172A" : "334155",
2897
+ bold: isHeader
2898
+ });
2021
2899
  cells.push(
2022
2900
  new import_docx.TableCell({
2023
2901
  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 },
2902
+ shading: { fill: rowBg, type: import_docx.ShadingType.CLEAR },
2903
+ margins: { top: 100, bottom: 100, left: 140, right: 140 },
2026
2904
  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" }
2905
+ top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2906
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: isHeader ? 8 : 4, color: isHeader ? "CBD5E1" : "E2E8F0" },
2907
+ left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2908
+ right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" }
2031
2909
  },
2032
2910
  children: [
2033
2911
  new import_docx.Paragraph({
2034
2912
  alignment,
2035
- children: rowNode.isHeader ? runs.map((r) => r instanceof import_docx.TextRun ? new import_docx.TextRun({ ...r, bold: true, color: "0F172A" }) : r) : runs
2913
+ children: runs
2036
2914
  })
2037
2915
  ]
2038
2916
  })
@@ -2041,7 +2919,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2041
2919
  }
2042
2920
  tableRows.push(
2043
2921
  new import_docx.TableRow({
2044
- tableHeader: rowNode.isHeader,
2922
+ tableHeader: isHeader,
2045
2923
  children: cells
2046
2924
  })
2047
2925
  );
@@ -2057,7 +2935,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2057
2935
  }
2058
2936
  if (node.type === "list" && node.children) {
2059
2937
  for (const item of node.children) {
2060
- const runs = await convertInlinesToTextRuns(item.inlines, baseDir);
2938
+ const runs = await convertInlinesToTextRuns(item.inlines, baseDir, {
2939
+ font: defaultFont,
2940
+ size: 21,
2941
+ color: "334155"
2942
+ });
2061
2943
  let prefix = "";
2062
2944
  if (item.checked !== void 0) {
2063
2945
  prefix = item.checked ? "[X] " : "[ ] ";
@@ -2069,7 +2951,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2069
2951
  ...prefix ? [new import_docx.TextRun({ text: prefix, bold: true, font: "Consolas" })] : [],
2070
2952
  ...runs
2071
2953
  ],
2072
- spacing: { before: 40, after: 40 }
2954
+ spacing: { before: 20, after: 20, line: 260 }
2073
2955
  })
2074
2956
  );
2075
2957
  }
@@ -2144,88 +3026,258 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2144
3026
  continue;
2145
3027
  }
2146
3028
  }
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({
3029
+ if (resolved.signatures && resolved.signatures.items.length > 0) {
3030
+ const sig = resolved.signatures;
3031
+ const numItems = sig.items.length;
3032
+ const contentWidth = Math.max(
3033
+ 1e3,
3034
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
3035
+ );
3036
+ docElements.push(new import_docx.Paragraph({ spacing: { before: sig.spacingBeforeTwip } }));
3037
+ const sigCells = [];
3038
+ const colWidths = [];
3039
+ if (numItems === 1) {
3040
+ const cardWidth = Math.min(3400, Math.floor(contentWidth * 0.42));
3041
+ const spacerWidth = contentWidth - cardWidth;
3042
+ const cardCell = await buildDocxSignatureCell(sig.items[0], sig, cardWidth, defaultFont, baseDir);
3043
+ if (sig.align === "left") {
3044
+ colWidths.push(cardWidth, spacerWidth);
3045
+ sigCells.push(cardCell, createEmptyDocxCell(spacerWidth));
3046
+ } else if (sig.align === "center") {
3047
+ const sideWidth = Math.floor(spacerWidth / 2);
3048
+ colWidths.push(sideWidth, cardWidth, sideWidth);
3049
+ sigCells.push(createEmptyDocxCell(sideWidth), cardCell, createEmptyDocxCell(sideWidth));
3050
+ } else {
3051
+ colWidths.push(spacerWidth, cardWidth);
3052
+ sigCells.push(createEmptyDocxCell(spacerWidth), cardCell);
3053
+ }
3054
+ } else {
3055
+ const colWidth = Math.floor(contentWidth / numItems);
3056
+ for (let i = 0; i < numItems; i++) {
3057
+ colWidths.push(colWidth);
3058
+ const cell = await buildDocxSignatureCell(sig.items[i], sig, colWidth, defaultFont, baseDir);
3059
+ sigCells.push(cell);
3060
+ }
3061
+ }
3062
+ const sigTable = new import_docx.Table({
3063
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
3064
+ columnWidths: colWidths,
3065
+ borders: {
3066
+ top: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
3067
+ bottom: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
3068
+ left: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
3069
+ right: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
3070
+ insideHorizontal: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
3071
+ insideVertical: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" }
3072
+ },
3073
+ rows: [
3074
+ new import_docx.TableRow({
3075
+ cantSplit: true,
3076
+ children: sigCells
3077
+ })
3078
+ ]
3079
+ });
3080
+ docElements.push(sigTable);
3081
+ }
3082
+ const contentWidthTwip = Math.max(
3083
+ 1e3,
3084
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
3085
+ );
3086
+ const centerPos = Math.round(contentWidthTwip / 2);
3087
+ const rightPos = contentWidthTwip;
3088
+ const headerRuns = [];
3089
+ if ((_d = resolved.header) == null ? void 0 : _d.left) {
3090
+ headerRuns.push(
3091
+ new import_docx.TextRun({
3092
+ text: resolved.header.left.text,
3093
+ color: resolved.header.left.color.replace("#", ""),
3094
+ size: (resolved.header.left.fontSize || 9) * 2,
3095
+ font: resolved.header.left.fontFamily || defaultFont,
3096
+ bold: resolved.header.left.bold,
3097
+ italics: resolved.header.left.italic
3098
+ })
3099
+ );
3100
+ }
3101
+ headerRuns.push(new import_docx.TextRun({ text: " " }));
3102
+ if ((_e = resolved.header) == null ? void 0 : _e.center) {
3103
+ headerRuns.push(
3104
+ new import_docx.TextRun({
3105
+ text: resolved.header.center.text,
3106
+ color: resolved.header.center.color.replace("#", ""),
3107
+ size: (resolved.header.center.fontSize || 9) * 2,
3108
+ font: resolved.header.center.fontFamily || defaultFont,
3109
+ bold: resolved.header.center.bold,
3110
+ italics: resolved.header.center.italic
3111
+ })
3112
+ );
3113
+ }
3114
+ headerRuns.push(new import_docx.TextRun({ text: " " }));
3115
+ if ((_f = resolved.header) == null ? void 0 : _f.right) {
3116
+ headerRuns.push(
3117
+ new import_docx.TextRun({
3118
+ text: resolved.header.right.text,
3119
+ color: resolved.header.right.color.replace("#", ""),
3120
+ size: (resolved.header.right.fontSize || 9) * 2,
3121
+ font: resolved.header.right.fontFamily || defaultFont,
3122
+ bold: resolved.header.right.bold,
3123
+ italics: resolved.header.right.italic
3124
+ })
3125
+ );
3126
+ }
3127
+ const docHeader = resolved.header ? new import_docx.Header({
2154
3128
  children: [
2155
3129
  new import_docx.Paragraph({
2156
- tabStops: headerTabStops,
2157
- children: [
2158
- // Left zone
2159
- ...headerObj.left ? [
3130
+ tabStops: [
3131
+ {
3132
+ type: import_docx.TabStopType.CENTER,
3133
+ position: centerPos
3134
+ },
3135
+ {
3136
+ type: import_docx.TabStopType.RIGHT,
3137
+ position: rightPos
3138
+ }
3139
+ ],
3140
+ border: resolved.header.divider ? {
3141
+ bottom: {
3142
+ style: import_docx.BorderStyle.SINGLE,
3143
+ size: 4,
3144
+ space: 6,
3145
+ color: (resolved.header.dividerColor || "#CBD5E1").replace("#", "")
3146
+ }
3147
+ } : void 0,
3148
+ children: headerRuns,
3149
+ spacing: { after: 120 }
3150
+ })
3151
+ ]
3152
+ }) : void 0;
3153
+ const footerRuns = [];
3154
+ if ((_g = resolved.footer) == null ? void 0 : _g.left) {
3155
+ footerRuns.push(
3156
+ new import_docx.TextRun({
3157
+ text: resolved.footer.left.text,
3158
+ color: resolved.footer.left.color.replace("#", ""),
3159
+ size: (resolved.footer.left.fontSize || 9) * 2,
3160
+ font: resolved.footer.left.fontFamily || defaultFont,
3161
+ bold: resolved.footer.left.bold,
3162
+ italics: resolved.footer.left.italic
3163
+ })
3164
+ );
3165
+ }
3166
+ footerRuns.push(new import_docx.TextRun({ text: " " }));
3167
+ if ((_h = resolved.footer) == null ? void 0 : _h.center) {
3168
+ footerRuns.push(
3169
+ new import_docx.TextRun({
3170
+ text: resolved.footer.center.text,
3171
+ color: resolved.footer.center.color.replace("#", ""),
3172
+ size: (resolved.footer.center.fontSize || 9) * 2,
3173
+ font: resolved.footer.center.fontFamily || defaultFont,
3174
+ bold: resolved.footer.center.bold,
3175
+ italics: resolved.footer.center.italic
3176
+ })
3177
+ );
3178
+ }
3179
+ footerRuns.push(new import_docx.TextRun({ text: " " }));
3180
+ if ((_i = resolved.footer) == null ? void 0 : _i.right) {
3181
+ const rZone = resolved.footer.right;
3182
+ const rColor = rZone.color.replace("#", "");
3183
+ const rSize = (rZone.fontSize || 9) * 2;
3184
+ const rFont = rZone.fontFamily || defaultFont;
3185
+ const rBold = rZone.bold;
3186
+ const rItalics = rZone.italic;
3187
+ if (rZone.text.includes("{page}") || rZone.text.includes("{pages}")) {
3188
+ const parts = rZone.text.split(/(\{page\}|\{pages\})/gi);
3189
+ for (const part of parts) {
3190
+ if (part.toLowerCase() === "{page}") {
3191
+ footerRuns.push(
2160
3192
  new import_docx.TextRun({
2161
- text: headerObj.left.replace("{title}", metadata.title || ""),
2162
- color: "94A3B8",
2163
- size: 18
3193
+ children: [import_docx.PageNumber.CURRENT],
3194
+ color: rColor,
3195
+ size: rSize,
3196
+ font: rFont,
3197
+ bold: rBold,
3198
+ italics: rItalics
2164
3199
  })
2165
- ] : [],
2166
- // Center zone (tab + text)
2167
- ...headerObj.center ? [
2168
- new import_docx.TextRun({ text: " ", color: "94A3B8", size: 18 }),
3200
+ );
3201
+ } else if (part.toLowerCase() === "{pages}") {
3202
+ footerRuns.push(
2169
3203
  new import_docx.TextRun({
2170
- text: headerObj.center.replace("{title}", metadata.title || ""),
2171
- color: "94A3B8",
2172
- size: 18
3204
+ children: [import_docx.PageNumber.TOTAL_PAGES],
3205
+ color: rColor,
3206
+ size: rSize,
3207
+ font: rFont,
3208
+ bold: rBold,
3209
+ italics: rItalics
2173
3210
  })
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
- }),
3211
+ );
3212
+ } else if (part) {
3213
+ footerRuns.push(
2182
3214
  new import_docx.TextRun({
2183
- text: headerObj.right.replace("{title}", metadata.title || ""),
2184
- color: "94A3B8",
2185
- size: 18
3215
+ text: part,
3216
+ color: rColor,
3217
+ size: rSize,
3218
+ font: rFont,
3219
+ bold: rBold,
3220
+ italics: rItalics
2186
3221
  })
2187
- ] : []
2188
- ]
2189
- })
2190
- ]
2191
- }) : void 0;
2192
- const docFooter = footerObj ? new import_docx.Footer({
3222
+ );
3223
+ }
3224
+ }
3225
+ } else {
3226
+ footerRuns.push(
3227
+ new import_docx.TextRun({
3228
+ text: rZone.text,
3229
+ color: rColor,
3230
+ size: rSize,
3231
+ font: rFont,
3232
+ bold: rBold,
3233
+ italics: rItalics
3234
+ })
3235
+ );
3236
+ }
3237
+ }
3238
+ const docFooter = resolved.footer ? new import_docx.Footer({
2193
3239
  children: [
2194
3240
  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
- ]
3241
+ tabStops: [
3242
+ {
3243
+ type: import_docx.TabStopType.CENTER,
3244
+ position: centerPos
3245
+ },
3246
+ {
3247
+ type: import_docx.TabStopType.RIGHT,
3248
+ position: rightPos
3249
+ }
3250
+ ],
3251
+ border: resolved.footer.divider ? {
3252
+ top: {
3253
+ style: import_docx.BorderStyle.SINGLE,
3254
+ size: 4,
3255
+ space: 6,
3256
+ color: (resolved.footer.dividerColor || "#CBD5E1").replace("#", "")
3257
+ }
3258
+ } : void 0,
3259
+ children: footerRuns,
3260
+ spacing: { before: 120 }
2212
3261
  })
2213
3262
  ]
2214
3263
  }) : 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";
3264
+ const isLandscape = resolved.orientation === "landscape";
2220
3265
  const document = new import_docx.Document({
2221
3266
  styles: {
2222
3267
  default: {
2223
3268
  document: {
2224
3269
  run: {
2225
- font: "Segoe UI",
2226
- size: 22,
2227
- // 11pt
2228
- color: "0F172A"
3270
+ font: defaultFont,
3271
+ size: 21,
3272
+ // 10.5pt
3273
+ color: textHex
3274
+ },
3275
+ paragraph: {
3276
+ spacing: {
3277
+ line: 276,
3278
+ // 1.15 line spacing
3279
+ after: 140
3280
+ }
2229
3281
  }
2230
3282
  }
2231
3283
  }
@@ -2235,13 +3287,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2235
3287
  properties: {
2236
3288
  page: {
2237
3289
  size: {
3290
+ width: resolved.paperDimensions.widthTwip,
3291
+ height: resolved.paperDimensions.heightTwip,
2238
3292
  orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
2239
3293
  },
2240
3294
  margin: {
2241
- top: topMargin,
2242
- bottom: bottomMargin,
2243
- left: leftMargin,
2244
- right: rightMargin,
3295
+ top: resolved.margins.topTwip,
3296
+ bottom: resolved.margins.bottomTwip,
3297
+ left: resolved.margins.leftTwip,
3298
+ right: resolved.margins.rightTwip,
2245
3299
  header: 720,
2246
3300
  footer: 720
2247
3301
  }
@@ -2255,153 +3309,139 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2255
3309
  });
2256
3310
  return await import_docx.Packer.toBuffer(document);
2257
3311
  }
2258
-
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
- function discoverConfigFile(startDir) {
2306
- const dirsToCheck = [];
2307
- let curr = path5.resolve(startDir);
2308
- while (curr) {
2309
- dirsToCheck.push(curr);
2310
- const parent = path5.dirname(curr);
2311
- if (parent === curr) break;
2312
- curr = parent;
2313
- }
2314
- const cwd = path5.resolve(process.cwd());
2315
- if (!dirsToCheck.includes(cwd)) {
2316
- dirsToCheck.push(cwd);
2317
- }
2318
- for (const dir of dirsToCheck) {
2319
- for (const filename of DEFAULT_CONFIG_FILENAMES) {
2320
- const candidate = path5.join(dir, filename);
2321
- if (fs5.existsSync(candidate) && fs5.statSync(candidate).isFile()) {
2322
- return candidate;
2323
- }
2324
- }
3312
+ async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
3313
+ const cellParagraphs = [];
3314
+ if (item.title) {
3315
+ cellParagraphs.push(
3316
+ new import_docx.Paragraph({
3317
+ children: [
3318
+ new import_docx.TextRun({
3319
+ text: item.title,
3320
+ font: defaultFont,
3321
+ size: 18,
3322
+ // 9pt
3323
+ color: sig.titleColor.replace("#", ""),
3324
+ bold: true
3325
+ })
3326
+ ],
3327
+ spacing: { after: 60 }
3328
+ })
3329
+ );
2325
3330
  }
2326
- return null;
2327
- }
2328
- async function loadConfig(customPath, startDir = process.cwd()) {
2329
- let resolvedPath = null;
2330
- if (customPath) {
2331
- resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(process.cwd(), customPath);
2332
- if (!fs5.existsSync(resolvedPath)) {
2333
- const altPath = path5.resolve(startDir, customPath);
2334
- if (fs5.existsSync(altPath)) {
2335
- resolvedPath = altPath;
2336
- } else {
2337
- throw new Error(`Configuration file not found: ${resolvedPath}`);
2338
- }
3331
+ if (item.image) {
3332
+ const resolvedImg = await resolveImage(item.image, baseDir);
3333
+ if (resolvedImg) {
3334
+ cellParagraphs.push(
3335
+ new import_docx.Paragraph({
3336
+ children: [
3337
+ new import_docx.ImageRun({
3338
+ data: resolvedImg.buffer,
3339
+ transformation: {
3340
+ width: 140,
3341
+ height: 60
3342
+ },
3343
+ type: "png"
3344
+ })
3345
+ ],
3346
+ spacing: { before: 40, after: 40 }
3347
+ })
3348
+ );
3349
+ } else {
3350
+ cellParagraphs.push(new import_docx.Paragraph({ spacing: { before: 240, after: 240 } }));
2339
3351
  }
2340
3352
  } else {
2341
- resolvedPath = discoverConfigFile(startDir);
3353
+ cellParagraphs.push(new import_docx.Paragraph({ spacing: { before: 240, after: 240 } }));
2342
3354
  }
2343
- if (!resolvedPath) {
2344
- return {
2345
- config: { ...DEFAULT_CONFIG },
2346
- configPath: null
2347
- };
3355
+ if (sig.style === "line") {
3356
+ cellParagraphs.push(
3357
+ new import_docx.Paragraph({
3358
+ border: {
3359
+ bottom: {
3360
+ style: import_docx.BorderStyle.SINGLE,
3361
+ size: 6,
3362
+ space: 2,
3363
+ color: sig.borderColor.replace("#", "")
3364
+ }
3365
+ },
3366
+ spacing: { after: 60 }
3367
+ })
3368
+ );
2348
3369
  }
2349
- const ext = path5.extname(resolvedPath).toLowerCase();
2350
- let userConfig = {};
2351
- try {
2352
- if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
2353
- const raw = fs5.readFileSync(resolvedPath, "utf-8").trim();
2354
- try {
2355
- userConfig = JSON.parse(raw);
2356
- } catch {
2357
- userConfig = YAML.parse(raw) || {};
2358
- }
2359
- } else if (ext === ".yaml" || ext === ".yml") {
2360
- const raw = fs5.readFileSync(resolvedPath, "utf-8");
2361
- userConfig = YAML.parse(raw) || {};
2362
- } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
2363
- try {
2364
- const fileUrl = `${(0, import_node_url3.pathToFileURL)(resolvedPath).href}?t=${Date.now()}`;
2365
- const mod = await import(fileUrl);
2366
- const rawExport = mod.default ?? mod.config ?? mod;
2367
- userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
2368
- } catch (importErr) {
2369
- try {
2370
- const mod = require(resolvedPath);
2371
- const rawExport = mod.default ?? mod.config ?? mod;
2372
- userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
2373
- } catch {
2374
- throw importErr;
2375
- }
2376
- }
2377
- }
2378
- } catch (err) {
2379
- throw new Error(
2380
- `Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
3370
+ cellParagraphs.push(
3371
+ new import_docx.Paragraph({
3372
+ children: [
3373
+ new import_docx.TextRun({
3374
+ text: item.name,
3375
+ font: defaultFont,
3376
+ size: 21,
3377
+ // 10.5pt
3378
+ bold: true,
3379
+ color: sig.nameColor.replace("#", "")
3380
+ })
3381
+ ],
3382
+ spacing: { before: sig.style === "line" ? 40 : 20, after: 20 }
3383
+ })
3384
+ );
3385
+ if (item.role) {
3386
+ cellParagraphs.push(
3387
+ new import_docx.Paragraph({
3388
+ children: [
3389
+ new import_docx.TextRun({
3390
+ text: item.role,
3391
+ font: defaultFont,
3392
+ size: 18,
3393
+ // 9pt
3394
+ color: sig.roleColor.replace("#", "")
3395
+ })
3396
+ ],
3397
+ spacing: { after: 20 }
3398
+ })
2381
3399
  );
2382
3400
  }
2383
- if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
2384
- delete userConfig.$schema;
3401
+ if (item.date) {
3402
+ cellParagraphs.push(
3403
+ new import_docx.Paragraph({
3404
+ children: [
3405
+ new import_docx.TextRun({
3406
+ text: `Date: ${item.date}`,
3407
+ font: defaultFont,
3408
+ size: 17,
3409
+ // 8.5pt
3410
+ color: sig.roleColor.replace("#", "")
3411
+ })
3412
+ ],
3413
+ spacing: { after: 20 }
3414
+ })
3415
+ );
2385
3416
  }
2386
- const parsedConfig = userConfig;
2387
- const mergedConfig = {
2388
- ...DEFAULT_CONFIG,
2389
- ...parsedConfig,
2390
- margins: {
2391
- ...DEFAULT_CONFIG.margins,
2392
- ...parsedConfig.margins || {}
3417
+ const isBox = sig.style === "box";
3418
+ const boxBorder = { style: import_docx.BorderStyle.SINGLE, size: 4, color: sig.borderColor.replace("#", "") };
3419
+ const noneBorder = { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" };
3420
+ return new import_docx.TableCell({
3421
+ width: { size: widthDxa, type: import_docx.WidthType.DXA },
3422
+ shading: isBox ? { fill: "F8FAFC", type: import_docx.ShadingType.CLEAR } : void 0,
3423
+ margins: isBox ? { top: 140, bottom: 140, left: 160, right: 160 } : { top: 60, bottom: 60, left: 60, right: 60 },
3424
+ borders: {
3425
+ top: isBox ? boxBorder : noneBorder,
3426
+ bottom: isBox ? boxBorder : noneBorder,
3427
+ left: isBox ? boxBorder : noneBorder,
3428
+ right: isBox ? boxBorder : noneBorder
2393
3429
  },
2394
- header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
2395
- footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
2396
- metadata: {
2397
- ...DEFAULT_CONFIG.metadata || {},
2398
- ...parsedConfig.metadata || {}
2399
- }
2400
- };
2401
- return {
2402
- config: mergedConfig,
2403
- configPath: resolvedPath
2404
- };
3430
+ children: cellParagraphs
3431
+ });
3432
+ }
3433
+ function createEmptyDocxCell(widthDxa) {
3434
+ const noneBorder = { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" };
3435
+ return new import_docx.TableCell({
3436
+ width: { size: widthDxa, type: import_docx.WidthType.DXA },
3437
+ borders: {
3438
+ top: noneBorder,
3439
+ bottom: noneBorder,
3440
+ left: noneBorder,
3441
+ right: noneBorder
3442
+ },
3443
+ children: [new import_docx.Paragraph({})]
3444
+ });
2405
3445
  }
2406
3446
 
2407
3447
  // src/core/engine.ts
@@ -2526,7 +3566,7 @@ try {
2526
3566
  }
2527
3567
  } catch {
2528
3568
  }
2529
- var FALLBACK_VERSION = "0.2.2";
3569
+ var FALLBACK_VERSION = "0.3.0";
2530
3570
  function readVersionFromPackageJson(fromDir) {
2531
3571
  let currentDir = fromDir;
2532
3572
  for (let i = 0; i < 6; i++) {
@@ -2564,10 +3604,17 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
2564
3604
  0 && (module.exports = {
2565
3605
  DEFAULT_CONFIG,
2566
3606
  MARKFORGE_VERSION,
3607
+ Orientation,
3608
+ OutputFormat,
3609
+ PAPER_DIMENSIONS_TWIP,
3610
+ PaperSizeEnum,
2567
3611
  SYNTAX_COLORS,
3612
+ SyntaxTheme,
2568
3613
  THEMES,
2569
- THEME_ACADEMIC,
3614
+ THEME_CORPORATE,
2570
3615
  THEME_DEFAULT,
3616
+ Theme,
3617
+ WatermarkPosition,
2571
3618
  buildDocxDocument,
2572
3619
  buildHtmlDocument,
2573
3620
  buildPdfDocument,
@@ -2576,6 +3623,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
2576
3623
  escapeHtml,
2577
3624
  findChromeExecutable,
2578
3625
  formatServerTimestamp,
3626
+ generateThemeCss,
2579
3627
  getMarkforgeVersion,
2580
3628
  getMimeType,
2581
3629
  highlightCodeToHtml,
@@ -2583,11 +3631,17 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
2583
3631
  inlineHtmlImages,
2584
3632
  loadConfig,
2585
3633
  markforge,
3634
+ normalizeHeaderFooter,
3635
+ normalizeHeaderFooterSlot,
3636
+ normalizeSignatures,
3637
+ normalizeWatermark,
2586
3638
  parseInlineSpans,
2587
3639
  parseMarginToTwip,
2588
3640
  parseMarkdownDocument,
2589
3641
  renderInlinesToHtml,
2590
3642
  renderMermaidToPng,
3643
+ replaceDocumentTokens,
3644
+ resolveDocumentConfig,
2591
3645
  resolveImage,
2592
3646
  slugify,
2593
3647
  tokenizeCodeLine