@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.mjs CHANGED
@@ -550,6 +550,51 @@ var SYNTAX_COLORS_LIGHT = {
550
550
  plain: "24292E"
551
551
  // Near-black — identifiers
552
552
  };
553
+ var SYNTAX_THEMES = {
554
+ "github-dark": SYNTAX_COLORS,
555
+ "dark": SYNTAX_COLORS,
556
+ "github-light": SYNTAX_COLORS_LIGHT,
557
+ "light": SYNTAX_COLORS_LIGHT,
558
+ "dracula": {
559
+ keyword: "FF79C6",
560
+ string: "F1FA8C",
561
+ comment: "6272A4",
562
+ number: "BD93F9",
563
+ boolean: "BD93F9",
564
+ function: "50FA7B",
565
+ type: "8BE9FD",
566
+ operator: "FF79C6",
567
+ punctuation: "F8F8F2",
568
+ plain: "F8F8F2"
569
+ },
570
+ "monokai": {
571
+ keyword: "F92672",
572
+ string: "E6DB74",
573
+ comment: "75715E",
574
+ number: "AE81FF",
575
+ boolean: "AE81FF",
576
+ function: "A6E22E",
577
+ type: "66D9EF",
578
+ operator: "F92672",
579
+ punctuation: "F8F8F2",
580
+ plain: "F8F8F2"
581
+ },
582
+ "nord": {
583
+ keyword: "81A1C1",
584
+ string: "A3BE8C",
585
+ comment: "616E88",
586
+ number: "B48EAD",
587
+ boolean: "81A1C1",
588
+ function: "88C0D0",
589
+ type: "8FBCBB",
590
+ operator: "81A1C1",
591
+ punctuation: "ECEFF4",
592
+ plain: "D8DEE9"
593
+ }
594
+ };
595
+ function getSyntaxPalette(themeName = "github-dark") {
596
+ return SYNTAX_THEMES[themeName.toLowerCase()] || SYNTAX_THEMES["github-dark"];
597
+ }
553
598
  var JS_KEYWORDS = /* @__PURE__ */ new Set([
554
599
  "import",
555
600
  "from",
@@ -693,9 +738,9 @@ var SQL_KEYWORDS = /* @__PURE__ */ new Set([
693
738
  "foreign",
694
739
  "references"
695
740
  ]);
696
- function tokenizeCodeLine(line, lang = "", theme = "dark") {
741
+ function tokenizeCodeLine(line, lang = "", theme = "github-dark") {
697
742
  var _a;
698
- const COLORS = theme === "light" ? SYNTAX_COLORS_LIGHT : SYNTAX_COLORS;
743
+ const COLORS = getSyntaxPalette(theme);
699
744
  if (!line) {
700
745
  return [{ text: " ", type: "plain", colorHex: COLORS.plain }];
701
746
  }
@@ -778,6 +823,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
778
823
  tokens.push({ text: word, type: "keyword", colorHex: COLORS.keyword, bold: true });
779
824
  continue;
780
825
  }
826
+ if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
827
+ tokens.push({ text: word, type: "type", colorHex: COLORS.type });
828
+ continue;
829
+ }
781
830
  let nextNonWs = pos;
782
831
  while (nextNonWs < line.length && /\s/.test(line[nextNonWs])) {
783
832
  nextNonWs++;
@@ -786,10 +835,6 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
786
835
  tokens.push({ text: word, type: "function", colorHex: COLORS.function });
787
836
  continue;
788
837
  }
789
- if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
790
- tokens.push({ text: word, type: "type", colorHex: COLORS.type });
791
- continue;
792
- }
793
838
  tokens.push({ text: word, type: "plain", colorHex: COLORS.plain });
794
839
  continue;
795
840
  }
@@ -802,10 +847,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
802
847
  }
803
848
  return tokens;
804
849
  }
805
- function highlightCodeToHtml(code, lang = "") {
850
+ function highlightCodeToHtml(code, lang = "", theme = "github-dark") {
806
851
  const lines = (code || "").split("\n");
807
852
  const htmlLines = lines.map((line) => {
808
- const tokens = tokenizeCodeLine(line, lang);
853
+ const tokens = tokenizeCodeLine(line, lang, theme);
809
854
  return tokens.map((t) => {
810
855
  const escaped = t.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
811
856
  if (t.type === "plain") return escaped;
@@ -817,21 +862,62 @@ function highlightCodeToHtml(code, lang = "") {
817
862
 
818
863
  // src/core/mermaid/mermaidRenderer.ts
819
864
  import { spawnSync as spawnSync2 } from "child_process";
820
- import * as fs4 from "fs";
865
+ import * as fs5 from "fs";
821
866
  import * as os2 from "os";
822
- import * as path4 from "path";
823
- import { pathToFileURL as pathToFileURL2 } from "url";
867
+ import * as path5 from "path";
868
+ import { pathToFileURL as pathToFileURL3 } from "url";
824
869
 
825
870
  // src/core/pdf/pdfBuilder.ts
826
- import * as fs3 from "fs";
827
- import * as path3 from "path";
871
+ import * as fs4 from "fs";
872
+ import * as path4 from "path";
828
873
  import * as os from "os";
829
- import { pathToFileURL } from "url";
874
+ import { pathToFileURL as pathToFileURL2 } from "url";
830
875
  import { spawnSync } from "child_process";
831
876
 
832
877
  // src/core/html/htmlBuilder.ts
833
- import * as fs2 from "fs";
834
- import * as path2 from "path";
878
+ import * as fs3 from "fs";
879
+ import * as path3 from "path";
880
+
881
+ // src/config/types.ts
882
+ var OutputFormat = /* @__PURE__ */ ((OutputFormat2) => {
883
+ OutputFormat2["DOCX"] = "docx";
884
+ OutputFormat2["PDF"] = "pdf";
885
+ OutputFormat2["HTML"] = "html";
886
+ OutputFormat2["PNG"] = "png";
887
+ return OutputFormat2;
888
+ })(OutputFormat || {});
889
+ var Theme = /* @__PURE__ */ ((Theme2) => {
890
+ Theme2["CORPORATE"] = "corporate";
891
+ return Theme2;
892
+ })(Theme || {});
893
+ var Orientation = /* @__PURE__ */ ((Orientation2) => {
894
+ Orientation2["PORTRAIT"] = "portrait";
895
+ Orientation2["LANDSCAPE"] = "landscape";
896
+ return Orientation2;
897
+ })(Orientation || {});
898
+ var PaperSizeEnum = /* @__PURE__ */ ((PaperSizeEnum2) => {
899
+ PaperSizeEnum2["A4"] = "A4";
900
+ PaperSizeEnum2["LETTER"] = "Letter";
901
+ PaperSizeEnum2["LEGAL"] = "Legal";
902
+ PaperSizeEnum2["A3"] = "A3";
903
+ PaperSizeEnum2["A5"] = "A5";
904
+ return PaperSizeEnum2;
905
+ })(PaperSizeEnum || {});
906
+ var SyntaxTheme = /* @__PURE__ */ ((SyntaxTheme2) => {
907
+ SyntaxTheme2["GITHUB_DARK"] = "github-dark";
908
+ SyntaxTheme2["GITHUB_LIGHT"] = "github-light";
909
+ SyntaxTheme2["DRACULA"] = "dracula";
910
+ SyntaxTheme2["MONOKAI"] = "monokai";
911
+ SyntaxTheme2["NORD"] = "nord";
912
+ return SyntaxTheme2;
913
+ })(SyntaxTheme || {});
914
+ var WatermarkPosition = /* @__PURE__ */ ((WatermarkPosition2) => {
915
+ WatermarkPosition2["DIAGONAL"] = "diagonal";
916
+ WatermarkPosition2["CENTER"] = "center";
917
+ WatermarkPosition2["TOP_RIGHT"] = "top-right";
918
+ WatermarkPosition2["BOTTOM_RIGHT"] = "bottom-right";
919
+ return WatermarkPosition2;
920
+ })(WatermarkPosition || {});
835
921
 
836
922
  // src/core/html/htmlThemes.ts
837
923
  var THEME_COMPONENTS = `
@@ -914,7 +1000,7 @@ hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
914
1000
  pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
915
1001
  }
916
1002
  `;
917
- var THEME_DEFAULT = `
1003
+ var THEME_CORPORATE = `
918
1004
  :root {
919
1005
  --mf-bg: #ffffff;
920
1006
  --mf-text: #0f172a;
@@ -926,49 +1012,479 @@ var THEME_DEFAULT = `
926
1012
  --mf-card-bg: #f8fafc;
927
1013
  --mf-code-bg: #0f172a;
928
1014
  --mf-code-text: #f8fafc;
929
- --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
1015
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
930
1016
  --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
931
1017
  }
932
1018
  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; }
933
- .document-container { max-width: 860px; margin: 0 auto; }
1019
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
934
1020
  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; }
935
- h1 { font-size: 2.2rem; border-bottom: 2px solid #33CDCF; padding-bottom: 0.5rem; }
936
- h2 { font-size: 1.6rem; color: #009DA0; border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
1021
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1022
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
937
1023
  h3 { font-size: 1.3rem; }
938
1024
  h4 { font-size: 1.1rem; }
939
1025
  p { margin: 0.8rem 0; }
940
1026
  `;
941
- var THEME_ACADEMIC = `
1027
+ var THEME_DEFAULT = THEME_CORPORATE;
1028
+ var THEMES = {
1029
+ corporate: THEME_CORPORATE,
1030
+ default: THEME_CORPORATE
1031
+ };
1032
+ function generateThemeCss(theme) {
1033
+ if (!theme || theme === "corporate" || theme === "default" || theme === "corporate" /* CORPORATE */) {
1034
+ return THEME_CORPORATE;
1035
+ }
1036
+ if (typeof theme === "object") {
1037
+ const bg = theme.backgroundColor || "#ffffff";
1038
+ const text = theme.textColor || "#0f172a";
1039
+ const textMuted = theme.textMuted || "#64748b";
1040
+ const primary = theme.primaryColor || "#33CDCF";
1041
+ const primaryDark = theme.primaryDark || primary;
1042
+ const primaryLight = theme.primaryLight || "#ECFDFD";
1043
+ const border = theme.borderColor || "#e2e8f0";
1044
+ const cardBg = theme.cardBackground || "#f8fafc";
1045
+ const codeBg = theme.codeBackground || "#0f172a";
1046
+ const codeText = theme.codeText || "#f8fafc";
1047
+ const font = theme.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
1048
+ const fontMono = theme.fontMono || "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
1049
+ return `
942
1050
  :root {
943
- --mf-bg: #ffffff;
944
- --mf-text: #1a1a1a;
945
- --mf-text-muted: #555;
946
- --mf-primary: #33CDCF;
947
- --mf-primary-dark: #009DA0;
948
- --mf-primary-light: #ECFDFD;
949
- --mf-border: #ccc;
950
- --mf-card-bg: #f9f9f9;
951
- --mf-code-bg: #1e1e1e;
952
- --mf-code-text: #d4d4d4;
953
- --mf-font-family: "Merriweather", "Georgia", "Times New Roman", serif;
954
- --mf-font-mono: "Courier New", Courier, monospace;
1051
+ --mf-bg: ${bg};
1052
+ --mf-text: ${text};
1053
+ --mf-text-muted: ${textMuted};
1054
+ --mf-primary: ${primary};
1055
+ --mf-primary-dark: ${primaryDark};
1056
+ --mf-primary-light: ${primaryLight};
1057
+ --mf-border: ${border};
1058
+ --mf-card-bg: ${cardBg};
1059
+ --mf-code-bg: ${codeBg};
1060
+ --mf-code-text: ${codeText};
1061
+ --mf-font-family: ${font};
1062
+ --mf-font-mono: ${fontMono};
955
1063
  }
956
- body { font-family: var(--mf-font-family); font-size: 16px; line-height: 1.8; padding: 3rem; color: var(--mf-text); }
957
- .document-container { max-width: 780px; margin: 0 auto; text-align: justify; }
958
- h1, h2, h3 { font-family: "Times New Roman", Times, serif; font-weight: bold; text-align: left; }
959
- h1 { font-size: 2rem; border-bottom: 1px solid #000; padding-bottom: 0.3rem; }
960
- h2 { font-size: 1.4rem; border-bottom: 1px solid #ccc; padding-bottom: 0.2rem; }
961
- h3 { font-size: 1.2rem; }
962
- p { margin: 0.9rem 0; }
1064
+ 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; }
1065
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1066
+ 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; }
1067
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1068
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
1069
+ h3 { font-size: 1.3rem; }
1070
+ h4 { font-size: 1.1rem; }
1071
+ p { margin: 0.8rem 0; }
1072
+ ${theme.customCss || ""}
963
1073
  `;
964
- var THEMES = {
965
- default: THEME_DEFAULT,
966
- academic: THEME_ACADEMIC,
967
- github: THEME_DEFAULT,
968
- corporate: THEME_DEFAULT,
969
- minimal: THEME_DEFAULT,
970
- dracula: THEME_DEFAULT
1074
+ }
1075
+ if (typeof theme === "string" && THEMES[theme]) {
1076
+ return THEMES[theme];
1077
+ }
1078
+ return THEME_CORPORATE;
1079
+ }
1080
+
1081
+ // src/config/loadConfig.ts
1082
+ import * as fs2 from "fs";
1083
+ import * as path2 from "path";
1084
+ import { pathToFileURL } from "url";
1085
+ import * as YAML from "yaml";
1086
+ var DEFAULT_CONFIG_FILENAMES = [
1087
+ "markforge.config.json",
1088
+ ".markforgerc.json",
1089
+ "markforge.config.yaml",
1090
+ "markforge.config.yml",
1091
+ ".markforgerc.yaml",
1092
+ ".markforgerc.yml",
1093
+ ".markforgerc",
1094
+ "markforge.config.ts",
1095
+ "markforge.config.js",
1096
+ "markforge.config.mjs",
1097
+ "markforge.config.cjs"
1098
+ ];
1099
+ var DEFAULT_CONFIG = {
1100
+ to: ["docx", "pdf"],
1101
+ outputDir: void 0,
1102
+ theme: "default",
1103
+ css: void 0,
1104
+ orientation: "portrait",
1105
+ paperSize: "A4",
1106
+ margins: {
1107
+ top: "2.5cm",
1108
+ bottom: "2.5cm",
1109
+ left: "2.5cm",
1110
+ right: "2.5cm"
1111
+ },
1112
+ header: void 0,
1113
+ footer: {
1114
+ right: "Page {page} of {pages}"
1115
+ },
1116
+ toc: false,
1117
+ watermark: void 0,
1118
+ embedImages: true,
1119
+ metadata: void 0,
1120
+ watch: false,
1121
+ serve: false,
1122
+ port: 4e3,
1123
+ open: false,
1124
+ bundleHtml: true,
1125
+ syntaxTheme: "github-dark"
1126
+ };
1127
+ function discoverConfigFile(startDir) {
1128
+ const dirsToCheck = [];
1129
+ let curr = path2.resolve(startDir);
1130
+ while (curr) {
1131
+ dirsToCheck.push(curr);
1132
+ const parent = path2.dirname(curr);
1133
+ if (parent === curr) break;
1134
+ curr = parent;
1135
+ }
1136
+ const cwd = path2.resolve(process.cwd());
1137
+ if (!dirsToCheck.includes(cwd)) {
1138
+ dirsToCheck.push(cwd);
1139
+ }
1140
+ for (const dir of dirsToCheck) {
1141
+ for (const filename of DEFAULT_CONFIG_FILENAMES) {
1142
+ const candidate = path2.join(dir, filename);
1143
+ if (fs2.existsSync(candidate) && fs2.statSync(candidate).isFile()) {
1144
+ return candidate;
1145
+ }
1146
+ }
1147
+ }
1148
+ return null;
1149
+ }
1150
+ async function loadConfig(customPath, startDir = process.cwd()) {
1151
+ let resolvedPath = null;
1152
+ if (customPath) {
1153
+ resolvedPath = path2.isAbsolute(customPath) ? customPath : path2.resolve(process.cwd(), customPath);
1154
+ if (!fs2.existsSync(resolvedPath)) {
1155
+ const altPath = path2.resolve(startDir, customPath);
1156
+ if (fs2.existsSync(altPath)) {
1157
+ resolvedPath = altPath;
1158
+ } else {
1159
+ throw new Error(`Configuration file not found: ${resolvedPath}`);
1160
+ }
1161
+ }
1162
+ } else {
1163
+ resolvedPath = discoverConfigFile(startDir);
1164
+ }
1165
+ if (!resolvedPath) {
1166
+ return {
1167
+ config: { ...DEFAULT_CONFIG },
1168
+ configPath: null
1169
+ };
1170
+ }
1171
+ const ext = path2.extname(resolvedPath).toLowerCase();
1172
+ let userConfig = {};
1173
+ try {
1174
+ if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
1175
+ const raw = fs2.readFileSync(resolvedPath, "utf-8").trim();
1176
+ try {
1177
+ userConfig = JSON.parse(raw);
1178
+ } catch {
1179
+ userConfig = YAML.parse(raw) || {};
1180
+ }
1181
+ } else if (ext === ".yaml" || ext === ".yml") {
1182
+ const raw = fs2.readFileSync(resolvedPath, "utf-8");
1183
+ userConfig = YAML.parse(raw) || {};
1184
+ } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
1185
+ try {
1186
+ const fileUrl = `${pathToFileURL(resolvedPath).href}?t=${Date.now()}`;
1187
+ const mod = await import(fileUrl);
1188
+ const rawExport = mod.default ?? mod.config ?? mod;
1189
+ userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
1190
+ } catch (importErr) {
1191
+ try {
1192
+ const mod = __require(resolvedPath);
1193
+ const rawExport = mod.default ?? mod.config ?? mod;
1194
+ userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
1195
+ } catch {
1196
+ throw importErr;
1197
+ }
1198
+ }
1199
+ }
1200
+ } catch (err) {
1201
+ throw new Error(
1202
+ `Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
1203
+ );
1204
+ }
1205
+ if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
1206
+ delete userConfig.$schema;
1207
+ }
1208
+ const parsedConfig = userConfig;
1209
+ const mergedConfig = {
1210
+ ...DEFAULT_CONFIG,
1211
+ ...parsedConfig,
1212
+ margins: {
1213
+ ...DEFAULT_CONFIG.margins,
1214
+ ...parsedConfig.margins || {}
1215
+ },
1216
+ header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
1217
+ footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
1218
+ metadata: {
1219
+ ...DEFAULT_CONFIG.metadata || {},
1220
+ ...parsedConfig.metadata || {}
1221
+ }
1222
+ };
1223
+ return {
1224
+ config: mergedConfig,
1225
+ configPath: resolvedPath
1226
+ };
1227
+ }
1228
+
1229
+ // src/config/resolveConfig.ts
1230
+ var PAPER_DIMENSIONS_TWIP = {
1231
+ A4: { width: 11906, height: 16838 },
1232
+ // 210mm x 297mm
1233
+ Letter: { width: 12240, height: 15840 },
1234
+ // 8.5in x 11in
1235
+ Legal: { width: 12240, height: 20160 },
1236
+ // 8.5in x 14in
1237
+ A3: { width: 16838, height: 23811 },
1238
+ // 297mm x 420mm
1239
+ A5: { width: 8390, height: 11906 }
1240
+ // 148mm x 210mm
971
1241
  };
1242
+ function parseMarginToTwip(margin, defaultTwip = 1440) {
1243
+ if (typeof margin === "number") return margin;
1244
+ if (!margin) return defaultTwip;
1245
+ const str = margin.trim().toLowerCase();
1246
+ if (str.endsWith("cm")) {
1247
+ const cm = parseFloat(str);
1248
+ return isNaN(cm) ? defaultTwip : Math.round(cm * 566.929);
1249
+ }
1250
+ if (str.endsWith("mm")) {
1251
+ const mm = parseFloat(str);
1252
+ return isNaN(mm) ? defaultTwip : Math.round(mm * 56.6929);
1253
+ }
1254
+ if (str.endsWith("in") || str.endsWith("inch")) {
1255
+ const inch = parseFloat(str);
1256
+ return isNaN(inch) ? defaultTwip : Math.round(inch * 1440);
1257
+ }
1258
+ if (str.endsWith("pt")) {
1259
+ const pt = parseFloat(str);
1260
+ return isNaN(pt) ? defaultTwip : Math.round(pt * 20);
1261
+ }
1262
+ const val = parseFloat(str);
1263
+ return isNaN(val) ? defaultTwip : Math.round(val);
1264
+ }
1265
+ function formatMarginCss(margin, defaultCss = "2.5cm") {
1266
+ if (margin === void 0 || margin === null) return defaultCss;
1267
+ if (typeof margin === "number") return `${margin}pt`;
1268
+ const str = margin.trim();
1269
+ if (!str) return defaultCss;
1270
+ if (/^[0-9.]+$/.test(str)) return `${str}pt`;
1271
+ return str;
1272
+ }
1273
+ function replaceDocumentTokens(template = "", meta) {
1274
+ 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 || "");
1275
+ }
1276
+ function normalizeWatermark(rawWatermark) {
1277
+ if (!rawWatermark) {
1278
+ return void 0;
1279
+ }
1280
+ if (typeof rawWatermark === "string") {
1281
+ const text = rawWatermark.trim();
1282
+ if (!text) return void 0;
1283
+ return {
1284
+ text,
1285
+ color: "#94a3b8",
1286
+ opacity: 0.08,
1287
+ fontSize: 54,
1288
+ rotate: -45,
1289
+ position: "diagonal"
1290
+ };
1291
+ }
1292
+ if (typeof rawWatermark === "object") {
1293
+ if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
1294
+ return {
1295
+ text: rawWatermark.text.trim(),
1296
+ color: rawWatermark.color || "#94a3b8",
1297
+ opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
1298
+ fontSize: rawWatermark.fontSize || 54,
1299
+ rotate: typeof rawWatermark.rotate === "number" ? rawWatermark.rotate : -45,
1300
+ position: rawWatermark.position || "diagonal"
1301
+ };
1302
+ }
1303
+ return void 0;
1304
+ }
1305
+ function normalizeHeaderFooterSlot(rawSlot, parent, meta) {
1306
+ if (!rawSlot) return void 0;
1307
+ if (typeof rawSlot === "string") {
1308
+ const text = replaceDocumentTokens(rawSlot, meta).trim();
1309
+ if (!text) return void 0;
1310
+ return {
1311
+ text,
1312
+ color: (parent == null ? void 0 : parent.color) || "#94A3B8",
1313
+ fontSize: (parent == null ? void 0 : parent.size) || 9,
1314
+ fontFamily: (parent == null ? void 0 : parent.font) || "Segoe UI",
1315
+ bold: false,
1316
+ italic: false
1317
+ };
1318
+ }
1319
+ if (typeof rawSlot === "object") {
1320
+ const text = replaceDocumentTokens(rawSlot.text || "", meta).trim();
1321
+ if (!text) return void 0;
1322
+ return {
1323
+ text,
1324
+ color: rawSlot.color || (parent == null ? void 0 : parent.color) || "#94A3B8",
1325
+ fontSize: rawSlot.fontSize || (parent == null ? void 0 : parent.size) || 9,
1326
+ fontFamily: rawSlot.fontFamily || (parent == null ? void 0 : parent.font) || "Segoe UI",
1327
+ bold: Boolean(rawSlot.bold),
1328
+ italic: Boolean(rawSlot.italic)
1329
+ };
1330
+ }
1331
+ return void 0;
1332
+ }
1333
+ function normalizeHeaderFooter(raw, meta) {
1334
+ if (!raw) return void 0;
1335
+ const left = normalizeHeaderFooterSlot(raw.left, raw, meta);
1336
+ const center = normalizeHeaderFooterSlot(raw.center, raw, meta);
1337
+ const right = normalizeHeaderFooterSlot(raw.right, raw, meta);
1338
+ if (!left && !center && !right) return void 0;
1339
+ return {
1340
+ left,
1341
+ center,
1342
+ right,
1343
+ font: raw.font || "Segoe UI",
1344
+ size: raw.size || 9,
1345
+ color: raw.color || "#94A3B8",
1346
+ divider: Boolean(raw.divider),
1347
+ dividerColor: raw.dividerColor || "#E2E8F0"
1348
+ };
1349
+ }
1350
+ function normalizeSignatures(raw, meta = {}) {
1351
+ if (!raw) return void 0;
1352
+ let rawItems = [];
1353
+ let rawConfig = {};
1354
+ if (Array.isArray(raw)) {
1355
+ rawItems = raw;
1356
+ } else if (typeof raw === "object" && Array.isArray(raw.items)) {
1357
+ rawItems = raw.items;
1358
+ rawConfig = raw;
1359
+ }
1360
+ if (rawItems.length === 0) return void 0;
1361
+ const cappedItems = rawItems.slice(0, 4);
1362
+ const items = cappedItems.map((item) => {
1363
+ const tokenCtx = meta;
1364
+ const rawName = typeof item.name === "string" ? item.name : "";
1365
+ const name = replaceDocumentTokens(rawName, tokenCtx).trim();
1366
+ const title = item.title ? replaceDocumentTokens(item.title, tokenCtx).trim() : void 0;
1367
+ const role = item.role ? replaceDocumentTokens(item.role, tokenCtx).trim() : void 0;
1368
+ let dateStr;
1369
+ if (typeof item.date === "string") {
1370
+ dateStr = replaceDocumentTokens(item.date, tokenCtx).trim();
1371
+ } else if (item.date === true) {
1372
+ dateStr = meta.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1373
+ }
1374
+ let signatureHeight = 60;
1375
+ if (typeof item.signatureHeight === "number") {
1376
+ signatureHeight = item.signatureHeight;
1377
+ } else if (typeof item.signatureHeight === "string") {
1378
+ const parsed = parseFloat(item.signatureHeight);
1379
+ if (!isNaN(parsed)) signatureHeight = parsed;
1380
+ }
1381
+ return {
1382
+ title,
1383
+ name: name || "Authorized Signatory",
1384
+ role,
1385
+ date: dateStr,
1386
+ image: item.image,
1387
+ signatureHeight
1388
+ };
1389
+ });
1390
+ const align = rawConfig.align || (items.length === 1 ? "right" : "space-between");
1391
+ const style = rawConfig.style || "line";
1392
+ const borderColor = rawConfig.borderColor || "#CBD5E1";
1393
+ const titleColor = rawConfig.titleColor || "#64748B";
1394
+ const nameColor = rawConfig.nameColor || "#0F172A";
1395
+ const roleColor = rawConfig.roleColor || "#64748B";
1396
+ const spacingBeforeRaw = rawConfig.spacingBefore ?? "2.5rem";
1397
+ const spacingBefore = formatMarginCss(spacingBeforeRaw, "2.5rem");
1398
+ const spacingBeforeTwip = parseMarginToTwip(spacingBeforeRaw, 600);
1399
+ return {
1400
+ items,
1401
+ align,
1402
+ style,
1403
+ borderColor,
1404
+ titleColor,
1405
+ nameColor,
1406
+ roleColor,
1407
+ spacingBefore,
1408
+ spacingBeforeTwip
1409
+ };
1410
+ }
1411
+ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1412
+ const configMeta = userConfig.metadata || {};
1413
+ const mergedMeta = { ...configMeta, ...frontmatter };
1414
+ const title = mergedMeta.title || "MarkForge Document";
1415
+ const subtitle = mergedMeta.subtitle || void 0;
1416
+ const author = Array.isArray(mergedMeta.author) ? mergedMeta.author.join(", ") : mergedMeta.author || void 0;
1417
+ const date = mergedMeta.date || void 0;
1418
+ const version = mergedMeta.version || void 0;
1419
+ const company = mergedMeta.company || void 0;
1420
+ const lang = mergedMeta.lang || "en";
1421
+ const tokenContext = { title, subtitle, author, version, date, company };
1422
+ const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
1423
+ const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
1424
+ const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
1425
+ const baseDim = PAPER_DIMENSIONS_TWIP[paperSize] || PAPER_DIMENSIONS_TWIP.A4;
1426
+ const paperDimensions = orientation === "landscape" ? { widthTwip: baseDim.height, heightTwip: baseDim.width } : { widthTwip: baseDim.width, heightTwip: baseDim.height };
1427
+ const fmMargin = mergedMeta.margins || {};
1428
+ const cfgMargin = userConfig.margins || {};
1429
+ const defMargin = DEFAULT_CONFIG.margins || {};
1430
+ const topRaw = fmMargin.top ?? cfgMargin.top ?? defMargin.top ?? "2.5cm";
1431
+ const bottomRaw = fmMargin.bottom ?? cfgMargin.bottom ?? defMargin.bottom ?? "2.5cm";
1432
+ const leftRaw = fmMargin.left ?? cfgMargin.left ?? defMargin.left ?? "2.5cm";
1433
+ const rightRaw = fmMargin.right ?? cfgMargin.right ?? defMargin.right ?? "2.5cm";
1434
+ const margins = {
1435
+ top: formatMarginCss(topRaw),
1436
+ bottom: formatMarginCss(bottomRaw),
1437
+ left: formatMarginCss(leftRaw),
1438
+ right: formatMarginCss(rightRaw),
1439
+ topTwip: parseMarginToTwip(topRaw),
1440
+ bottomTwip: parseMarginToTwip(bottomRaw),
1441
+ leftTwip: parseMarginToTwip(leftRaw),
1442
+ rightTwip: parseMarginToTwip(rightRaw)
1443
+ };
1444
+ const rawHeader = mergedMeta.header || userConfig.header || DEFAULT_CONFIG.header;
1445
+ const rawFooter = mergedMeta.footer || userConfig.footer || DEFAULT_CONFIG.footer;
1446
+ const header = normalizeHeaderFooter(rawHeader, tokenContext);
1447
+ const footer = normalizeHeaderFooter(rawFooter, tokenContext);
1448
+ const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
1449
+ const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
1450
+ const watermark = normalizeWatermark(rawWatermark);
1451
+ const rawSignatures = mergedMeta.signatures || userConfig.signatures;
1452
+ const signatures = normalizeSignatures(rawSignatures, tokenContext);
1453
+ const cssList = [];
1454
+ const addCss = (item) => {
1455
+ if (!item) return;
1456
+ if (Array.isArray(item)) cssList.push(...item);
1457
+ else cssList.push(item);
1458
+ };
1459
+ addCss(userConfig.css);
1460
+ addCss(mergedMeta.css);
1461
+ const embedImages = typeof userConfig.embedImages === "boolean" ? userConfig.embedImages : DEFAULT_CONFIG.embedImages;
1462
+ const bundleHtml = typeof userConfig.bundleHtml === "boolean" ? userConfig.bundleHtml : DEFAULT_CONFIG.bundleHtml;
1463
+ const syntaxTheme = userConfig.syntaxTheme || DEFAULT_CONFIG.syntaxTheme || "github-dark";
1464
+ return {
1465
+ title,
1466
+ subtitle,
1467
+ author,
1468
+ date,
1469
+ version,
1470
+ company,
1471
+ lang,
1472
+ theme,
1473
+ orientation,
1474
+ paperSize,
1475
+ paperDimensions,
1476
+ margins,
1477
+ header,
1478
+ footer,
1479
+ toc,
1480
+ signatures,
1481
+ watermark,
1482
+ css: cssList,
1483
+ embedImages,
1484
+ bundleHtml,
1485
+ syntaxTheme
1486
+ };
1487
+ }
972
1488
 
973
1489
  // src/core/html/htmlBuilder.ts
974
1490
  function escapeHtml(str) {
@@ -1021,43 +1537,41 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1021
1537
  return result;
1022
1538
  }
1023
1539
  async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1024
- var _a;
1025
- const metadata = { ...config.metadata, ...doc.metadata };
1026
- const themeName = metadata.theme || config.theme || "default";
1027
- const baseThemeCss = THEMES[themeName] || THEMES.default;
1540
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1541
+ const baseThemeCss = generateThemeCss(resolved.theme);
1028
1542
  let customCss = "";
1029
- if (config.css) {
1030
- const cssList = Array.isArray(config.css) ? config.css : [config.css];
1031
- for (const cssPath of cssList) {
1032
- const fullCssPath = path2.isAbsolute(cssPath) ? cssPath : path2.resolve(baseDir, cssPath);
1033
- if (fs2.existsSync(fullCssPath)) {
1034
- customCss += `
1543
+ for (const cssPath of resolved.css) {
1544
+ const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
1545
+ if (fs3.existsSync(fullCssPath)) {
1546
+ customCss += `
1035
1547
  /* Custom CSS: ${cssPath} */
1036
- ` + fs2.readFileSync(fullCssPath, "utf-8");
1037
- }
1548
+ ` + fs3.readFileSync(fullCssPath, "utf-8");
1038
1549
  }
1039
1550
  }
1040
1551
  const inlinedCss = doc.inlinedStyles.join("\n");
1041
1552
  let bodyHtml = "";
1042
- if (metadata.title) {
1553
+ if (resolved.title) {
1043
1554
  bodyHtml += ` <header class="document-header">
1044
1555
  `;
1045
- bodyHtml += ` <h1 class="document-title">${escapeHtml(metadata.title)}</h1>
1556
+ bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
1046
1557
  `;
1047
- if (metadata.subtitle) {
1048
- bodyHtml += ` <div class="document-subtitle">${escapeHtml(metadata.subtitle)}</div>
1558
+ if (resolved.subtitle) {
1559
+ bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
1049
1560
  `;
1050
1561
  }
1051
- if (metadata.author || metadata.date) {
1562
+ if (resolved.author || resolved.date || resolved.version) {
1052
1563
  bodyHtml += ` <div class="document-meta">
1053
1564
  `;
1054
- if (metadata.author) {
1055
- const authors = Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author;
1056
- bodyHtml += ` <span>Author: ${escapeHtml(authors)}</span>
1565
+ if (resolved.author) {
1566
+ bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
1567
+ `;
1568
+ }
1569
+ if (resolved.version) {
1570
+ bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
1057
1571
  `;
1058
1572
  }
1059
- if (metadata.date) {
1060
- bodyHtml += ` <span>Date: ${escapeHtml(metadata.date)}</span>
1573
+ if (resolved.date) {
1574
+ bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
1061
1575
  `;
1062
1576
  }
1063
1577
  bodyHtml += ` </div>
@@ -1066,22 +1580,20 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1066
1580
  bodyHtml += ` </header>
1067
1581
  `;
1068
1582
  }
1069
- if (config.toc || metadata.toc) {
1070
- if (doc.tocEntries.length > 0) {
1071
- bodyHtml += ` <nav class="table-of-contents">
1583
+ if (resolved.toc && doc.tocEntries.length > 0) {
1584
+ bodyHtml += ` <nav class="table-of-contents">
1072
1585
  `;
1073
- bodyHtml += ` <h2>Table of Contents</h2>
1586
+ bodyHtml += ` <h2>Table of Contents</h2>
1074
1587
  <ul>
1075
1588
  `;
1076
- for (const entry of doc.tocEntries) {
1077
- const indent = " ".repeat(entry.level);
1078
- bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1589
+ for (const entry of doc.tocEntries) {
1590
+ const indent = " ".repeat(entry.level);
1591
+ bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1079
1592
  `;
1080
- }
1081
- bodyHtml += ` </ul>
1593
+ }
1594
+ bodyHtml += ` </ul>
1082
1595
  </nav>
1083
1596
  `;
1084
- }
1085
1597
  }
1086
1598
  for (const node of doc.nodes) {
1087
1599
  if (node.type === "heading") {
@@ -1099,7 +1611,7 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1099
1611
  if (node.type === "codeBlock") {
1100
1612
  const lang = node.language || "";
1101
1613
  const langClass = lang ? ` class="language-${escapeHtml(lang)}"` : "";
1102
- const highlighted = highlightCodeToHtml(node.text || "", lang);
1614
+ const highlighted = highlightCodeToHtml(node.text || "", lang, resolved.syntaxTheme);
1103
1615
  bodyHtml += ` <pre><code${langClass}>${highlighted}</code></pre>
1104
1616
  `;
1105
1617
  continue;
@@ -1143,15 +1655,12 @@ ${escapeHtml(node.text || "")}
1143
1655
  for (const row of node.children) {
1144
1656
  bodyHtml += ` <tr>
1145
1657
  `;
1146
- if (row.children) {
1147
- for (let colIdx = 0; colIdx < row.children.length; colIdx++) {
1148
- const cell = row.children[colIdx];
1149
- const tag = row.isHeader ? "th" : "td";
1150
- const align = ((_a = node.align) == null ? void 0 : _a[colIdx]) ? ` style="text-align: ${node.align[colIdx]}"` : "";
1151
- const cellInner = await renderInlinesToHtml(cell.inlines, baseDir);
1152
- bodyHtml += ` <${tag}${align}>${cellInner}</${tag}>
1658
+ for (const cell of row.children || []) {
1659
+ const tag = cell.isHeader ? "th" : "td";
1660
+ const align = cell.align ? ` align="${cell.align}"` : "";
1661
+ const inner = await renderInlinesToHtml(cell.inlines, baseDir);
1662
+ bodyHtml += ` <${tag}${align}>${inner}</${tag}>
1153
1663
  `;
1154
- }
1155
1664
  }
1156
1665
  bodyHtml += ` </tr>
1157
1666
  `;
@@ -1165,58 +1674,187 @@ ${escapeHtml(node.text || "")}
1165
1674
  bodyHtml += ` <${tag}>
1166
1675
  `;
1167
1676
  for (const item of node.children) {
1168
- const itemInner = await renderInlinesToHtml(item.inlines, baseDir);
1169
- let prefix = "";
1170
- if (item.checked !== void 0) {
1171
- prefix = `<input type="checkbox" disabled ${item.checked ? "checked" : ""}/> `;
1172
- }
1173
- bodyHtml += ` <li>${prefix}${itemInner}</li>
1677
+ const inner = await renderInlinesToHtml(item.inlines, baseDir);
1678
+ bodyHtml += ` <li>${inner}</li>
1174
1679
  `;
1175
1680
  }
1176
1681
  bodyHtml += ` </${tag}>
1177
1682
  `;
1178
1683
  continue;
1179
1684
  }
1180
- if (node.type === "htmlBlock") {
1181
- bodyHtml += ` ${node.rawHtml}
1685
+ if (node.type === "thematicBreak") {
1686
+ bodyHtml += ` <hr />
1182
1687
  `;
1183
1688
  continue;
1184
1689
  }
1185
- if (node.type === "thematicBreak") {
1186
- bodyHtml += ` <hr />
1690
+ if (node.type === "htmlBlock") {
1691
+ bodyHtml += ` ${node.text}
1187
1692
  `;
1188
1693
  continue;
1189
1694
  }
1190
1695
  }
1191
- const wmConfig = config.watermark ?? metadata.watermark;
1192
- let watermarkHtml = "";
1193
1696
  let watermarkCss = "";
1194
- if (wmConfig) {
1195
- const wmText = typeof wmConfig === "string" ? wmConfig : wmConfig.text;
1196
- const opacity = typeof wmConfig === "object" && wmConfig.opacity !== void 0 ? wmConfig.opacity : 0.12;
1197
- const rotate = typeof wmConfig === "object" && wmConfig.rotate !== void 0 ? wmConfig.rotate : -45;
1198
- const color = typeof wmConfig === "object" && wmConfig.color ? wmConfig.color : "#94A3B8";
1199
- const fontSize = typeof wmConfig === "object" && wmConfig.fontSize ? `${wmConfig.fontSize}pt` : "52pt";
1697
+ let watermarkHtml = "";
1698
+ if (resolved.watermark) {
1699
+ const wm = resolved.watermark;
1200
1700
  watermarkCss = `
1201
1701
  .document-watermark {
1202
1702
  position: fixed;
1203
- top: 50%;
1204
- left: 50%;
1205
- transform: translate(-50%, -50%) rotate(${rotate}deg);
1206
- font-size: ${fontSize};
1207
- font-weight: 800;
1208
- color: ${color};
1209
- opacity: ${opacity};
1703
+ top: 0;
1704
+ left: 0;
1705
+ width: 100vw;
1706
+ height: 100vh;
1210
1707
  pointer-events: none;
1708
+ z-index: 0;
1211
1709
  user-select: none;
1212
- z-index: 9999;
1213
- text-transform: uppercase;
1214
- letter-spacing: 0.15em;
1215
- white-space: nowrap;
1710
+ -webkit-user-select: none;
1216
1711
  }
1217
- `;
1218
- watermarkHtml = ` <div class="document-watermark">${escapeHtml(wmText)}</div>
1219
- `;
1712
+ .document-container {
1713
+ position: relative;
1714
+ z-index: 1;
1715
+ }
1716
+ @media print {
1717
+ .document-watermark {
1718
+ position: fixed;
1719
+ top: 0;
1720
+ left: 0;
1721
+ width: 100vw;
1722
+ height: 100vh;
1723
+ -webkit-print-color-adjust: exact;
1724
+ print-color-adjust: exact;
1725
+ }
1726
+ }
1727
+ `;
1728
+ watermarkHtml = ` <div id="markforge-watermark" class="document-watermark" aria-hidden="true"></div>
1729
+ <script>
1730
+ (function() {
1731
+ try {
1732
+ var canvas = document.createElement('canvas');
1733
+ var dpr = 3;
1734
+ var width = 800;
1735
+ var height = 1100;
1736
+ canvas.width = Math.round(width * dpr);
1737
+ canvas.height = Math.round(height * dpr);
1738
+ var ctx = canvas.getContext('2d');
1739
+ if (ctx) {
1740
+ ctx.scale(dpr, dpr);
1741
+ ctx.translate(width / 2, height / 2);
1742
+ ctx.rotate((${wm.rotate} * Math.PI) / 180);
1743
+ ctx.textAlign = 'center';
1744
+ ctx.textBaseline = 'middle';
1745
+ ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
1746
+ ctx.fillStyle = '${wm.color}';
1747
+ ctx.globalAlpha = ${wm.opacity};
1748
+ try { ctx.letterSpacing = '0.15em'; } catch(e) {}
1749
+ ctx.fillText(${JSON.stringify(wm.text.toUpperCase())}, 0, 0);
1750
+ var dataUrl = canvas.toDataURL('image/png');
1751
+ var wmEl = document.getElementById('markforge-watermark');
1752
+ if (wmEl) {
1753
+ wmEl.style.backgroundImage = 'url("' + dataUrl + '")';
1754
+ wmEl.style.backgroundRepeat = 'no-repeat';
1755
+ wmEl.style.backgroundPosition = 'center center';
1756
+ wmEl.style.backgroundSize = 'contain';
1757
+ }
1758
+ }
1759
+ } catch(err) {}
1760
+ })();
1761
+ </script>
1762
+ `;
1763
+ }
1764
+ let signaturesHtml = "";
1765
+ let signaturesCss = "";
1766
+ if (resolved.signatures && resolved.signatures.items.length > 0) {
1767
+ const sig = resolved.signatures;
1768
+ const numItems = sig.items.length;
1769
+ let justifyCss = "flex-end";
1770
+ if (sig.align === "left") justifyCss = "flex-start";
1771
+ else if (sig.align === "center") justifyCss = "center";
1772
+ else if (sig.align === "space-between") justifyCss = "space-between";
1773
+ signaturesCss = `
1774
+ .markforge-signatures {
1775
+ margin-top: ${sig.spacingBefore};
1776
+ display: grid;
1777
+ 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))`};
1778
+ gap: 2rem;
1779
+ page-break-inside: avoid;
1780
+ break-inside: avoid;
1781
+ }
1782
+ .markforge-signature-card {
1783
+ ${numItems === 1 && sig.align === "center" ? "grid-column: 2;" : ""}
1784
+ ${numItems === 1 && sig.align === "right" ? "grid-column: 2;" : ""}
1785
+ display: flex;
1786
+ flex-direction: column;
1787
+ ${sig.style === "box" ? `border: 1px solid ${sig.borderColor}; border-radius: 6px; padding: 14px 18px; background-color: var(--mf-card-bg, #F8FAFC);` : ""}
1788
+ }
1789
+ .markforge-sig-title {
1790
+ font-size: 0.85rem;
1791
+ color: ${sig.titleColor};
1792
+ font-weight: 600;
1793
+ margin-bottom: 6px;
1794
+ }
1795
+ .markforge-sig-space {
1796
+ height: var(--sig-height, 60px);
1797
+ display: flex;
1798
+ align-items: center;
1799
+ justify-content: center;
1800
+ margin-bottom: 6px;
1801
+ }
1802
+ .markforge-sig-space img {
1803
+ max-height: 100%;
1804
+ max-width: 100%;
1805
+ object-fit: contain;
1806
+ }
1807
+ .markforge-sig-line {
1808
+ ${sig.style === "line" ? `border-bottom: 1.5px solid ${sig.borderColor}; margin-bottom: 8px;` : ""}
1809
+ }
1810
+ .markforge-sig-name {
1811
+ font-size: 0.95rem;
1812
+ font-weight: 700;
1813
+ color: ${sig.nameColor};
1814
+ }
1815
+ .markforge-sig-role {
1816
+ font-size: 0.82rem;
1817
+ color: ${sig.roleColor};
1818
+ margin-top: 2px;
1819
+ }
1820
+ .markforge-sig-date {
1821
+ font-size: 0.78rem;
1822
+ color: ${sig.roleColor};
1823
+ margin-top: 2px;
1824
+ }
1825
+ @media print {
1826
+ .markforge-signatures {
1827
+ page-break-inside: avoid;
1828
+ break-inside: avoid;
1829
+ }
1830
+ }
1831
+ `;
1832
+ const itemCards = sig.items.map((item) => {
1833
+ const titleHtml = item.title ? `<div class="markforge-sig-title">${escapeHtml(item.title)}</div>` : "";
1834
+ let signSpaceHtml = "";
1835
+ if (item.image) {
1836
+ signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"><img src="${escapeHtml(item.image)}" alt="Signature" /></div>`;
1837
+ } else {
1838
+ signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"></div>`;
1839
+ }
1840
+ const lineHtml = sig.style === "line" ? `<div class="markforge-sig-line"></div>` : "";
1841
+ const nameHtml = `<div class="markforge-sig-name">${escapeHtml(item.name)}</div>`;
1842
+ const roleHtml = item.role ? `<div class="markforge-sig-role">${escapeHtml(item.role)}</div>` : "";
1843
+ const dateHtml = item.date ? `<div class="markforge-sig-date">Date: ${escapeHtml(item.date)}</div>` : "";
1844
+ return ` <div class="markforge-signature-card">
1845
+ ${titleHtml}
1846
+ ${signSpaceHtml}
1847
+ ${lineHtml}
1848
+ ${nameHtml}
1849
+ ${roleHtml}
1850
+ ${dateHtml}
1851
+ </div>`;
1852
+ }).join("\n");
1853
+ signaturesHtml = `
1854
+ <div class="markforge-signatures">
1855
+ ${itemCards}
1856
+ </div>
1857
+ `;
1220
1858
  }
1221
1859
  const hasMermaid = doc.nodes.some((n) => n.type === "mermaid");
1222
1860
  const mermaidScript = hasMermaid ? `<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
@@ -1234,24 +1872,24 @@ ${escapeHtml(node.text || "")}
1234
1872
  }
1235
1873
  });
1236
1874
  </script>` : "";
1237
- const documentTitle = metadata.title || "MarkForge Document";
1238
1875
  return `<!DOCTYPE html>
1239
- <html lang="${metadata.lang || "en"}">
1876
+ <html lang="${resolved.lang}">
1240
1877
  <head>
1241
1878
  <meta charset="UTF-8">
1242
1879
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1243
- <title>${escapeHtml(documentTitle)}</title>
1880
+ <title>${escapeHtml(resolved.title)}</title>
1244
1881
  <style>
1245
1882
  ${THEME_COMPONENTS}
1246
1883
  ${baseThemeCss}
1247
1884
  ${customCss}
1248
1885
  ${inlinedCss}
1249
1886
  ${watermarkCss}
1887
+ ${signaturesCss}
1250
1888
  </style>
1251
1889
  </head>
1252
1890
  <body>
1253
1891
  ${watermarkHtml} <div class="document-container">
1254
- ${bodyHtml} </div>
1892
+ ${bodyHtml}${signaturesHtml} </div>
1255
1893
  ${mermaidScript}
1256
1894
  </body>
1257
1895
  </html>`;
@@ -1259,10 +1897,10 @@ ${bodyHtml} </div>
1259
1897
 
1260
1898
  // src/core/pdf/pdfBuilder.ts
1261
1899
  function findChromeExecutable() {
1262
- if (process.env.CHROME_PATH && fs3.existsSync(process.env.CHROME_PATH)) {
1900
+ if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
1263
1901
  return process.env.CHROME_PATH;
1264
1902
  }
1265
- if (process.env.PUPPETEER_EXECUTABLE_PATH && fs3.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
1903
+ if (process.env.PUPPETEER_EXECUTABLE_PATH && fs4.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
1266
1904
  return process.env.PUPPETEER_EXECUTABLE_PATH;
1267
1905
  }
1268
1906
  const isWin = process.platform === "win32";
@@ -1284,14 +1922,14 @@ function findChromeExecutable() {
1284
1922
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
1285
1923
  "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
1286
1924
  "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
1925
+ // Windows — Microsoft Edge (Native Windows 10/11 browser, enterprise whitelist friendly)
1926
+ `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
1927
+ `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
1928
+ `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
1287
1929
  // Windows — Google Chrome
1288
1930
  `${winProgramFiles}\\Google\\Chrome\\Application\\chrome.exe`,
1289
1931
  `${winProgramFilesX86}\\Google\\Chrome\\Application\\chrome.exe`,
1290
1932
  `${winLocalAppData}\\Google\\Chrome\\Application\\chrome.exe`,
1291
- // Windows — Microsoft Edge (ships with Windows 10/11)
1292
- `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
1293
- `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
1294
- `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
1295
1933
  // Windows — Brave Browser
1296
1934
  `${winProgramFiles}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
1297
1935
  `${winProgramFilesX86}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
@@ -1301,7 +1939,7 @@ function findChromeExecutable() {
1301
1939
  ].filter(Boolean);
1302
1940
  for (const candidate of candidates) {
1303
1941
  try {
1304
- if (fs3.existsSync(candidate)) {
1942
+ if (fs4.existsSync(candidate)) {
1305
1943
  return candidate;
1306
1944
  }
1307
1945
  } catch {
@@ -1314,7 +1952,7 @@ function findChromeExecutable() {
1314
1952
  const res = spawnSync(cmd, [name], { encoding: "utf-8" });
1315
1953
  if (res.status === 0 && res.stdout.trim()) {
1316
1954
  const binPath = res.stdout.split(/\r?\n/)[0].trim();
1317
- if (fs3.existsSync(binPath)) return binPath;
1955
+ if (fs4.existsSync(binPath)) return binPath;
1318
1956
  }
1319
1957
  }
1320
1958
  } catch {
@@ -1322,22 +1960,50 @@ function findChromeExecutable() {
1322
1960
  return null;
1323
1961
  }
1324
1962
  function injectPagedMediaStyles(html, config, metadata) {
1325
- var _a, _b, _c, _d;
1326
- const merged = { ...config.metadata, ...metadata };
1327
- const orientation = merged.orientation || config.orientation || "portrait";
1328
- const size = merged.paperSize || config.paperSize || "A4";
1329
- const margins = merged.margins || config.margins || {};
1330
- const top = margins.top || ((_a = config.margins) == null ? void 0 : _a.top) || "2.5cm";
1331
- const bottom = margins.bottom || ((_b = config.margins) == null ? void 0 : _b.bottom) || "2.5cm";
1332
- const left = margins.left || ((_c = config.margins) == null ? void 0 : _c.left) || "2.5cm";
1333
- const right = margins.right || ((_d = config.margins) == null ? void 0 : _d.right) || "2.5cm";
1334
- const headerCfg = merged.header;
1335
- const footerCfg = merged.footer;
1336
- const headerLeft = (headerCfg == null ? void 0 : headerCfg.left) ?? "";
1337
- const headerCenter = (headerCfg == null ? void 0 : headerCfg.center) ?? "";
1338
- const headerRight = (headerCfg == null ? void 0 : headerCfg.right) ?? "";
1339
- const footerLeft = ((footerCfg == null ? void 0 : footerCfg.left) ?? "").replace("{page}", "").replace("{pages}", "").trim();
1340
- const esc = (s) => s.replace(/"/g, '"').replace(/\\/g, "\\\\");
1963
+ var _a, _b, _c, _d, _e, _f;
1964
+ const resolved = resolveDocumentConfig(metadata || {}, config);
1965
+ const size = resolved.paperSize;
1966
+ const orientation = resolved.orientation;
1967
+ const top = resolved.margins.top;
1968
+ const bottom = resolved.margins.bottom;
1969
+ const left = resolved.margins.left;
1970
+ const right = resolved.margins.right;
1971
+ const esc = (s) => s.replace(/"/g, '\\"').replace(/\\/g, "\\\\");
1972
+ const buildZoneCss = (pos, zone, isPageCounter = false) => {
1973
+ if (!zone && !isPageCounter) return "";
1974
+ const color = (zone == null ? void 0 : zone.color) || "#94a3b8";
1975
+ const fontSize = (zone == null ? void 0 : zone.fontSize) ? `${zone.fontSize}pt` : "9pt";
1976
+ const fontFamily = (zone == null ? void 0 : zone.fontFamily) ? `font-family: ${zone.fontFamily};` : "";
1977
+ const fontWeight = (zone == null ? void 0 : zone.bold) ? "font-weight: bold;" : "";
1978
+ const fontStyle = (zone == null ? void 0 : zone.italic) ? "font-style: italic;" : "";
1979
+ let content = "";
1980
+ if (isPageCounter) {
1981
+ if ((zone == null ? void 0 : zone.text) && (zone.text.includes("{page}") || zone.text.includes("{pages}"))) {
1982
+ const parts = zone.text.split(/(\{page\}|\{pages\})/gi);
1983
+ const cssParts = parts.map((part) => {
1984
+ if (part.toLowerCase() === "{page}") return "counter(page)";
1985
+ if (part.toLowerCase() === "{pages}") return "counter(pages)";
1986
+ return `"${esc(part)}"`;
1987
+ });
1988
+ content = cssParts.join(" ");
1989
+ } else if (zone == null ? void 0 : zone.text) {
1990
+ content = `"${esc(zone.text)}"`;
1991
+ } else {
1992
+ content = `"Page " counter(page) " of " counter(pages)`;
1993
+ }
1994
+ } else if (zone == null ? void 0 : zone.text) {
1995
+ content = `"${esc(zone.text)}"`;
1996
+ }
1997
+ if (!content) return "";
1998
+ return `@${pos} {
1999
+ content: ${content};
2000
+ font-size: ${fontSize};
2001
+ color: ${color};
2002
+ ${fontFamily}
2003
+ ${fontWeight}
2004
+ ${fontStyle}
2005
+ }`;
2006
+ };
1341
2007
  const pagedCss = `
1342
2008
  @page {
1343
2009
  size: ${size} ${orientation};
@@ -1345,15 +2011,12 @@ function injectPagedMediaStyles(html, config, metadata) {
1345
2011
  margin-bottom: ${bottom};
1346
2012
  margin-left: ${left};
1347
2013
  margin-right: ${right};
1348
- ${headerLeft ? `@top-left { content: "${esc(headerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1349
- ${headerCenter ? `@top-center { content: "${esc(headerCenter)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1350
- ${headerRight ? `@top-right { content: "${esc(headerRight)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1351
- ${footerLeft ? `@bottom-left { content: "${esc(footerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1352
- @bottom-right {
1353
- content: "Page " counter(page) " of " counter(pages);
1354
- font-size: 9pt;
1355
- color: #94a3b8;
1356
- }
2014
+ ${buildZoneCss("top-left", (_a = resolved.header) == null ? void 0 : _a.left)}
2015
+ ${buildZoneCss("top-center", (_b = resolved.header) == null ? void 0 : _b.center)}
2016
+ ${buildZoneCss("top-right", (_c = resolved.header) == null ? void 0 : _c.right)}
2017
+ ${buildZoneCss("bottom-left", (_d = resolved.footer) == null ? void 0 : _d.left)}
2018
+ ${buildZoneCss("bottom-center", (_e = resolved.footer) == null ? void 0 : _e.center)}
2019
+ ${buildZoneCss("bottom-right", (_f = resolved.footer) == null ? void 0 : _f.right, true)}
1357
2020
  }
1358
2021
  @media print {
1359
2022
  body { padding: 0; }
@@ -1410,56 +2073,72 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1410
2073
  if (chromePath) {
1411
2074
  const tmpId = Math.random().toString(36).substring(2, 9);
1412
2075
  const tmpDir = os.tmpdir();
1413
- const tmpHtml = path3.join(tmpDir, `markforge_${tmpId}.html`);
1414
- const tmpPdf = path3.join(tmpDir, `markforge_${tmpId}.pdf`);
2076
+ const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
2077
+ const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
2078
+ const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
2079
+ const isolatedFlags = [
2080
+ `--user-data-dir=${tmpProfile}`,
2081
+ "--no-first-run",
2082
+ "--no-default-browser-check",
2083
+ "--disable-sync",
2084
+ "--disable-background-networking",
2085
+ "--disable-component-update",
2086
+ "--disable-default-apps",
2087
+ "--disable-extensions",
2088
+ "--disable-domain-reliability",
2089
+ "--disable-client-side-phishing-detection",
2090
+ "--disable-breakpad",
2091
+ "--disable-component-extensions-with-background-pages",
2092
+ "--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
2093
+ "--password-store=basic",
2094
+ "--use-mock-keychain",
2095
+ "--mute-audio",
2096
+ "--no-service-autorun",
2097
+ "--disable-gpu",
2098
+ "--no-sandbox",
2099
+ "--disable-setuid-sandbox",
2100
+ "--allow-file-access-from-files",
2101
+ "--disable-web-security",
2102
+ "--force-color-profile=srgb",
2103
+ "--no-pdf-header-footer"
2104
+ ];
1415
2105
  try {
1416
- fs3.writeFileSync(tmpHtml, pagedHtml, "utf-8");
1417
- const fileUrl = pathToFileURL(tmpHtml).href;
2106
+ fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
2107
+ const fileUrl = pathToFileURL2(tmpHtml).href;
1418
2108
  let res = spawnSync(
1419
2109
  chromePath,
1420
2110
  [
1421
2111
  "--headless=new",
1422
- "--disable-gpu",
1423
- "--no-sandbox",
1424
- "--disable-setuid-sandbox",
1425
- "--allow-file-access-from-files",
1426
- "--disable-web-security",
1427
- "--force-color-profile=srgb",
2112
+ ...isolatedFlags,
1428
2113
  "--run-all-compositor-stages-before-draw",
1429
2114
  "--virtual-time-budget=8000",
1430
- "--no-pdf-header-footer",
1431
2115
  `--print-to-pdf=${tmpPdf}`,
1432
2116
  fileUrl
1433
2117
  ],
1434
2118
  { timeout: 3e4 }
1435
2119
  );
1436
- if ((res.status !== 0 || !fs3.existsSync(tmpPdf)) && chromePath) {
2120
+ if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
1437
2121
  res = spawnSync(
1438
2122
  chromePath,
1439
2123
  [
1440
2124
  "--headless",
1441
- "--disable-gpu",
1442
- "--no-sandbox",
1443
- "--disable-setuid-sandbox",
1444
- "--allow-file-access-from-files",
1445
- "--disable-web-security",
1446
- "--force-color-profile=srgb",
1447
- "--no-pdf-header-footer",
2125
+ ...isolatedFlags,
1448
2126
  `--print-to-pdf=${tmpPdf}`,
1449
2127
  fileUrl
1450
2128
  ],
1451
2129
  { timeout: 3e4 }
1452
2130
  );
1453
2131
  }
1454
- if (fs3.existsSync(tmpPdf) && fs3.statSync(tmpPdf).size > 0) {
1455
- const pdfBuffer = fs3.readFileSync(tmpPdf);
2132
+ if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
2133
+ const pdfBuffer = fs4.readFileSync(tmpPdf);
1456
2134
  return pdfBuffer;
1457
2135
  }
1458
2136
  } catch {
1459
2137
  } finally {
1460
2138
  try {
1461
- if (fs3.existsSync(tmpHtml)) fs3.unlinkSync(tmpHtml);
1462
- if (fs3.existsSync(tmpPdf)) fs3.unlinkSync(tmpPdf);
2139
+ if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
2140
+ if (fs4.existsSync(tmpPdf)) fs4.unlinkSync(tmpPdf);
2141
+ if (fs4.existsSync(tmpProfile)) fs4.rmSync(tmpProfile, { recursive: true, force: true });
1463
2142
  } catch {
1464
2143
  }
1465
2144
  }
@@ -1475,8 +2154,8 @@ async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
1475
2154
  }
1476
2155
  const tmpId = Math.random().toString(36).substring(2, 9);
1477
2156
  const tmpDir = os2.tmpdir();
1478
- const tmpHtml = path4.join(tmpDir, `mermaid_${tmpId}.html`);
1479
- const tmpScreenshot = path4.join(tmpDir, `mermaid_${tmpId}.png`);
2157
+ const tmpHtml = path5.join(tmpDir, `mermaid_${tmpId}.html`);
2158
+ const tmpScreenshot = path5.join(tmpDir, `mermaid_${tmpId}.png`);
1480
2159
  const htmlContent = `<!DOCTYPE html>
1481
2160
  <html>
1482
2161
  <head>
@@ -1515,8 +2194,8 @@ ${mermaidCode}
1515
2194
  </body>
1516
2195
  </html>`;
1517
2196
  try {
1518
- fs4.writeFileSync(tmpHtml, htmlContent, "utf-8");
1519
- const fileUrl = pathToFileURL2(tmpHtml).href;
2197
+ fs5.writeFileSync(tmpHtml, htmlContent, "utf-8");
2198
+ const fileUrl = pathToFileURL3(tmpHtml).href;
1520
2199
  const res = spawnSync2(
1521
2200
  chromePath,
1522
2201
  [
@@ -1534,15 +2213,15 @@ ${mermaidCode}
1534
2213
  ],
1535
2214
  { timeout: 15e3 }
1536
2215
  );
1537
- if (res.status === 0 && fs4.existsSync(tmpScreenshot)) {
1538
- const buffer = fs4.readFileSync(tmpScreenshot);
2216
+ if (res.status === 0 && fs5.existsSync(tmpScreenshot)) {
2217
+ const buffer = fs5.readFileSync(tmpScreenshot);
1539
2218
  return buffer;
1540
2219
  }
1541
2220
  } catch {
1542
2221
  } finally {
1543
2222
  try {
1544
- if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
1545
- if (fs4.existsSync(tmpScreenshot)) fs4.unlinkSync(tmpScreenshot);
2223
+ if (fs5.existsSync(tmpHtml)) fs5.unlinkSync(tmpHtml);
2224
+ if (fs5.existsSync(tmpScreenshot)) fs5.unlinkSync(tmpScreenshot);
1546
2225
  } catch {
1547
2226
  }
1548
2227
  }
@@ -1550,7 +2229,7 @@ ${mermaidCode}
1550
2229
  }
1551
2230
 
1552
2231
  // src/core/docx/docxBuilder.ts
1553
- function parseMarginToTwip(margin, defaultTwip = 1440) {
2232
+ function parseMarginToTwip2(margin, defaultTwip = 1440) {
1554
2233
  if (typeof margin === "number") return margin;
1555
2234
  if (!margin) return defaultTwip;
1556
2235
  const str = margin.trim().toLowerCase();
@@ -1573,7 +2252,7 @@ function parseMarginToTwip(margin, defaultTwip = 1440) {
1573
2252
  const val = parseFloat(str);
1574
2253
  return isNaN(val) ? defaultTwip : Math.round(val);
1575
2254
  }
1576
- async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
2255
+ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), options = {}) {
1577
2256
  const runs = [];
1578
2257
  for (const span of spans) {
1579
2258
  if (span.type === "image" && span.url) {
@@ -1603,7 +2282,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1603
2282
  new TextRun({
1604
2283
  text: span.content,
1605
2284
  style: "Hyperlink",
1606
- color: "0969DA",
2285
+ color: "009DA0",
2286
+ font: options.font,
2287
+ size: options.size,
2288
+ bold: options.bold,
1607
2289
  underline: {}
1608
2290
  })
1609
2291
  ],
@@ -1616,7 +2298,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1616
2298
  runs.push(
1617
2299
  new TextRun({
1618
2300
  text: span.content,
1619
- bold: true
2301
+ bold: true,
2302
+ font: options.font,
2303
+ size: options.size,
2304
+ color: options.color,
2305
+ italics: options.italics
1620
2306
  })
1621
2307
  );
1622
2308
  continue;
@@ -1625,7 +2311,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1625
2311
  runs.push(
1626
2312
  new TextRun({
1627
2313
  text: span.content,
1628
- italics: true
2314
+ italics: true,
2315
+ font: options.font,
2316
+ size: options.size,
2317
+ color: options.color,
2318
+ bold: options.bold
1629
2319
  })
1630
2320
  );
1631
2321
  continue;
@@ -1634,7 +2324,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1634
2324
  runs.push(
1635
2325
  new TextRun({
1636
2326
  text: span.content,
1637
- strike: true
2327
+ strike: true,
2328
+ font: options.font,
2329
+ size: options.size,
2330
+ color: options.color
1638
2331
  })
1639
2332
  );
1640
2333
  continue;
@@ -1642,22 +2335,23 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1642
2335
  if (span.type === "code") {
1643
2336
  runs.push(
1644
2337
  new TextRun({
1645
- text: ` ${span.content} `,
2338
+ text: span.content,
1646
2339
  font: "Consolas",
2340
+ size: options.size ? options.size - 2 : 19,
2341
+ color: "0F172A",
1647
2342
  shading: {
1648
2343
  type: ShadingType.CLEAR,
1649
- fill: "F1F5F9",
1650
- color: "0F172A"
2344
+ fill: "F1F5F9"
1651
2345
  }
1652
2346
  })
1653
2347
  );
1654
2348
  continue;
1655
2349
  }
1656
2350
  if (span.type === "htmlInline") {
1657
- let colorHex;
2351
+ let colorHex = options.color;
1658
2352
  let bgHex;
1659
- let isBold = false;
1660
- let isItalic = false;
2353
+ let isBold = !!options.bold;
2354
+ let isItalic = !!options.italics;
1661
2355
  if (span.style) {
1662
2356
  if (span.style.color) {
1663
2357
  colorHex = span.style.color.replace("#", "").trim();
@@ -1673,13 +2367,9 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1673
2367
  }
1674
2368
  }
1675
2369
  if (span.children && span.children.length > 0) {
1676
- const childRuns = await convertInlinesToTextRuns(span.children, baseDir);
2370
+ const childRuns = await convertInlinesToTextRuns(span.children, baseDir, options);
1677
2371
  for (const child of childRuns) {
1678
- if (child instanceof TextRun) {
1679
- runs.push(child);
1680
- } else {
1681
- runs.push(child);
1682
- }
2372
+ runs.push(child);
1683
2373
  }
1684
2374
  continue;
1685
2375
  }
@@ -1689,6 +2379,8 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1689
2379
  color: colorHex,
1690
2380
  bold: isBold,
1691
2381
  italics: isItalic,
2382
+ font: options.font,
2383
+ size: options.size,
1692
2384
  shading: bgHex ? { type: ShadingType.CLEAR, fill: bgHex } : void 0
1693
2385
  })
1694
2386
  );
@@ -1696,91 +2388,239 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1696
2388
  }
1697
2389
  runs.push(
1698
2390
  new TextRun({
1699
- text: span.content
2391
+ text: span.content,
2392
+ font: options.font,
2393
+ size: options.size,
2394
+ color: options.color,
2395
+ bold: options.bold,
2396
+ italics: options.italics
1700
2397
  })
1701
2398
  );
1702
2399
  }
1703
2400
  return runs;
1704
2401
  }
1705
2402
  async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1706
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1707
- const metadata = { ...config.metadata, ...doc.metadata };
2403
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2404
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1708
2405
  const docElements = [];
1709
- if (metadata.title) {
2406
+ const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
2407
+ const primaryHex = (themeProps.primaryColor || "#33CDCF").replace("#", "");
2408
+ const primaryDarkHex = (themeProps.primaryDark || "#009DA0").replace("#", "");
2409
+ const textHex = (themeProps.textColor || "#0F172A").replace("#", "");
2410
+ const textMutedHex = (themeProps.textMuted || "#64748B").replace("#", "");
2411
+ const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
2412
+ const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
2413
+ const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
2414
+ if (resolved.title) {
1710
2415
  docElements.push(
1711
2416
  new Paragraph({
1712
- text: metadata.title,
1713
- heading: HeadingLevel.TITLE,
1714
- spacing: { before: 200, after: 120 }
2417
+ children: [
2418
+ new TextRun({
2419
+ text: resolved.title,
2420
+ bold: true,
2421
+ size: 44,
2422
+ // 22pt
2423
+ color: textHex,
2424
+ font: defaultFont
2425
+ })
2426
+ ],
2427
+ spacing: { before: 120, after: 80 }
1715
2428
  })
1716
2429
  );
1717
- if (metadata.subtitle) {
2430
+ if (resolved.subtitle) {
1718
2431
  docElements.push(
1719
2432
  new Paragraph({
1720
2433
  children: [
1721
2434
  new TextRun({
1722
- text: metadata.subtitle,
1723
- italics: true,
1724
- color: "64748B",
1725
- size: 24
2435
+ text: resolved.subtitle,
2436
+ color: textMutedHex,
2437
+ size: 24,
1726
2438
  // 12pt
2439
+ font: defaultFont
1727
2440
  })
1728
2441
  ],
1729
- spacing: { after: 180 }
2442
+ spacing: { before: 60, after: 120 }
1730
2443
  })
1731
2444
  );
1732
2445
  }
1733
- if (metadata.author || metadata.date) {
2446
+ if (resolved.author || resolved.date || resolved.version || resolved.company) {
1734
2447
  const metaParts = [];
1735
- if (metadata.author) metaParts.push(`Author: ${Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author}`);
1736
- if (metadata.date) metaParts.push(`Date: ${metadata.date}`);
2448
+ if (resolved.author) metaParts.push(`Author: ${resolved.author}`);
2449
+ if (resolved.version) metaParts.push(`Version: ${resolved.version}`);
2450
+ if (resolved.date) metaParts.push(`Date: ${resolved.date}`);
1737
2451
  docElements.push(
1738
2452
  new Paragraph({
1739
2453
  children: [
1740
2454
  new TextRun({
1741
- text: metaParts.join(" | "),
1742
- color: "94A3B8",
1743
- size: 20
1744
- // 10pt
2455
+ text: metaParts.join(" "),
2456
+ color: textMutedHex,
2457
+ size: 18,
2458
+ // 9pt
2459
+ font: defaultFont
1745
2460
  })
1746
2461
  ],
1747
- spacing: { after: 360 },
2462
+ spacing: { before: 40, after: 240 },
1748
2463
  border: {
1749
2464
  bottom: {
1750
- color: "E2E8F0",
1751
- space: 10,
2465
+ color: borderHex,
2466
+ space: 12,
1752
2467
  style: BorderStyle.SINGLE,
1753
- size: 6
2468
+ size: 4
1754
2469
  }
1755
2470
  }
1756
2471
  })
1757
2472
  );
1758
2473
  }
1759
2474
  }
1760
- for (const node of doc.nodes) {
1761
- if (node.type === "heading") {
1762
- let headingLevel = HeadingLevel.HEADING_1;
1763
- if (node.level === 2) headingLevel = HeadingLevel.HEADING_2;
1764
- if (node.level === 3) headingLevel = HeadingLevel.HEADING_3;
1765
- if (node.level === 4) headingLevel = HeadingLevel.HEADING_4;
1766
- if (node.level === 5) headingLevel = HeadingLevel.HEADING_5;
1767
- if (node.level === 6) headingLevel = HeadingLevel.HEADING_6;
1768
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1769
- docElements.push(
2475
+ if (resolved.toc) {
2476
+ const headingNodes = doc.nodes.filter(
2477
+ (n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
2478
+ );
2479
+ if (headingNodes.length > 0) {
2480
+ const tocParagraphs = [
1770
2481
  new Paragraph({
1771
- heading: headingLevel,
1772
- children: runs,
1773
- spacing: { before: 240, after: 120 }
2482
+ children: [
2483
+ new TextRun({
2484
+ text: "TABLE OF CONTENTS",
2485
+ bold: true,
2486
+ size: 18,
2487
+ // 9pt
2488
+ color: textMutedHex,
2489
+ font: defaultFont
2490
+ })
2491
+ ],
2492
+ spacing: { after: 120 },
2493
+ border: {
2494
+ bottom: {
2495
+ color: borderHex,
2496
+ space: 6,
2497
+ style: BorderStyle.SINGLE,
2498
+ size: 4
2499
+ }
2500
+ }
1774
2501
  })
1775
- );
2502
+ ];
2503
+ for (const h of headingNodes) {
2504
+ const indentLeft = (h.level - 1) * 240;
2505
+ tocParagraphs.push(
2506
+ new Paragraph({
2507
+ indent: { left: indentLeft },
2508
+ children: [
2509
+ new TextRun({
2510
+ text: h.text,
2511
+ bold: h.level === 1,
2512
+ color: primaryDarkHex,
2513
+ size: h.level === 1 ? 21 : 20,
2514
+ font: defaultFont
2515
+ })
2516
+ ],
2517
+ spacing: { before: 20, after: 20 }
2518
+ })
2519
+ );
2520
+ }
2521
+ const tocCard = new Table({
2522
+ width: { size: 100, type: WidthType.PERCENTAGE },
2523
+ columnWidths: [9e3],
2524
+ rows: [
2525
+ new TableRow({
2526
+ children: [
2527
+ new TableCell({
2528
+ width: { size: 9e3, type: WidthType.DXA },
2529
+ shading: { fill: cardBgHex, type: ShadingType.CLEAR },
2530
+ margins: { top: 140, bottom: 140, left: 180, right: 180 },
2531
+ borders: {
2532
+ top: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
2533
+ bottom: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
2534
+ left: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
2535
+ right: { style: BorderStyle.SINGLE, size: 4, color: borderHex }
2536
+ },
2537
+ children: tocParagraphs
2538
+ })
2539
+ ]
2540
+ })
2541
+ ]
2542
+ });
2543
+ docElements.push(tocCard);
2544
+ docElements.push(new Paragraph({ spacing: { after: 200 } }));
2545
+ }
2546
+ }
2547
+ for (const node of doc.nodes) {
2548
+ if (node.type === "heading") {
2549
+ if (node.level === 1) {
2550
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2551
+ font: defaultFont,
2552
+ size: 34,
2553
+ // 17pt
2554
+ color: textHex,
2555
+ bold: true
2556
+ });
2557
+ docElements.push(
2558
+ new Paragraph({
2559
+ heading: HeadingLevel.HEADING_1,
2560
+ children: runs,
2561
+ spacing: { before: 360, after: 140 },
2562
+ border: {
2563
+ bottom: {
2564
+ color: primaryHex,
2565
+ space: 6,
2566
+ style: BorderStyle.SINGLE,
2567
+ size: 16
2568
+ }
2569
+ }
2570
+ })
2571
+ );
2572
+ } else if (node.level === 2) {
2573
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2574
+ font: defaultFont,
2575
+ size: 26,
2576
+ // 13pt
2577
+ color: primaryDarkHex,
2578
+ bold: true
2579
+ });
2580
+ docElements.push(
2581
+ new Paragraph({
2582
+ heading: HeadingLevel.HEADING_2,
2583
+ children: runs,
2584
+ spacing: { before: 280, after: 100 },
2585
+ border: {
2586
+ bottom: {
2587
+ color: borderHex,
2588
+ space: 4,
2589
+ style: BorderStyle.SINGLE,
2590
+ size: 4
2591
+ }
2592
+ }
2593
+ })
2594
+ );
2595
+ } else {
2596
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2597
+ font: defaultFont,
2598
+ size: 22,
2599
+ // 11pt
2600
+ color: textHex,
2601
+ bold: true
2602
+ });
2603
+ docElements.push(
2604
+ new Paragraph({
2605
+ heading: node.level === 3 ? HeadingLevel.HEADING_3 : HeadingLevel.HEADING_4,
2606
+ children: runs,
2607
+ spacing: { before: 200, after: 80 }
2608
+ })
2609
+ );
2610
+ }
1776
2611
  continue;
1777
2612
  }
1778
2613
  if (node.type === "paragraph") {
1779
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2614
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2615
+ font: defaultFont,
2616
+ size: 22,
2617
+ // 11pt
2618
+ color: "334155"
2619
+ });
1780
2620
  docElements.push(
1781
2621
  new Paragraph({
1782
2622
  children: runs,
1783
- spacing: { before: 60, after: 140 }
2623
+ spacing: { before: 40, after: 140, line: 280 }
1784
2624
  })
1785
2625
  );
1786
2626
  continue;
@@ -1852,7 +2692,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1852
2692
  bgFill = "F5F3FF";
1853
2693
  title = "IMPORTANT";
1854
2694
  }
1855
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2695
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2696
+ font: defaultFont,
2697
+ size: 21,
2698
+ color: "334155"
2699
+ });
1856
2700
  const calloutTable = new Table({
1857
2701
  width: { size: 100, type: WidthType.PERCENTAGE },
1858
2702
  columnWidths: [9e3],
@@ -1867,7 +2711,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1867
2711
  top: { style: BorderStyle.NONE },
1868
2712
  bottom: { style: BorderStyle.NONE },
1869
2713
  right: { style: BorderStyle.NONE },
1870
- left: { style: BorderStyle.SINGLE, size: 16, color: borderColor }
2714
+ left: { style: BorderStyle.SINGLE, size: 20, color: borderColor }
1871
2715
  },
1872
2716
  children: [
1873
2717
  new Paragraph({
@@ -1876,13 +2720,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1876
2720
  text: `[${title}]`,
1877
2721
  bold: true,
1878
2722
  color: borderColor,
2723
+ font: defaultFont,
1879
2724
  size: 20
1880
2725
  })
1881
2726
  ],
1882
- spacing: { after: 60 }
2727
+ spacing: { after: 40 }
1883
2728
  }),
1884
2729
  new Paragraph({
1885
- children: runs
2730
+ children: runs,
2731
+ spacing: { line: 270 }
1886
2732
  })
1887
2733
  ]
1888
2734
  })
@@ -1900,17 +2746,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1900
2746
  if (lines.length > 0) {
1901
2747
  for (const lineText of lines) {
1902
2748
  const spans = parseInlineSpans(lineText.replace(/^>\s?/, "").trim());
1903
- const lineRuns = await convertInlinesToTextRuns(spans, baseDir);
2749
+ const lineRuns = await convertInlinesToTextRuns(spans, baseDir, {
2750
+ font: defaultFont,
2751
+ size: 21,
2752
+ color: "475569",
2753
+ italics: true
2754
+ });
1904
2755
  quoteParagraphs.push(
1905
2756
  new Paragraph({
1906
2757
  children: lineRuns,
1907
- spacing: { before: 40, after: 40 }
2758
+ spacing: { before: 20, after: 20, line: 260 }
1908
2759
  })
1909
2760
  );
1910
2761
  }
1911
2762
  } else {
1912
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1913
- quoteParagraphs.push(new Paragraph({ children: runs }));
2763
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2764
+ font: defaultFont,
2765
+ size: 21,
2766
+ color: "475569",
2767
+ italics: true
2768
+ });
2769
+ quoteParagraphs.push(new Paragraph({ children: runs, spacing: { line: 260 } }));
1914
2770
  }
1915
2771
  const quoteTable = new Table({
1916
2772
  width: { size: 100, type: WidthType.PERCENTAGE },
@@ -1926,7 +2782,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1926
2782
  top: { style: BorderStyle.NONE },
1927
2783
  bottom: { style: BorderStyle.NONE },
1928
2784
  right: { style: BorderStyle.NONE },
1929
- left: { style: BorderStyle.SINGLE, size: 12, color: "33CDCF" }
2785
+ left: { style: BorderStyle.SINGLE, size: 16, color: primaryHex }
1930
2786
  },
1931
2787
  children: quoteParagraphs
1932
2788
  })
@@ -1961,7 +2817,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1961
2817
  docElements.push(
1962
2818
  new Paragraph({
1963
2819
  children: [
1964
- new TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: "33CDCF" })
2820
+ new TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: primaryHex })
1965
2821
  ],
1966
2822
  spacing: { before: 60, after: 60 }
1967
2823
  })
@@ -1973,8 +2829,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1973
2829
  const tableRows = [];
1974
2830
  const numCols = ((_b = (_a = node.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) || 1;
1975
2831
  const colWidth = Math.floor(9e3 / numCols);
1976
- for (const rowNode of node.children) {
2832
+ for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
2833
+ const rowNode = node.children[rowIdx];
1977
2834
  const cells = [];
2835
+ const isHeader = rowNode.isHeader;
2836
+ const rowBg = isHeader ? "F1F5F9" : rowIdx % 2 === 0 ? "FFFFFF" : "F8FAFC";
1978
2837
  if (rowNode.children) {
1979
2838
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
1980
2839
  const cellNode = rowNode.children[colIdx];
@@ -1982,22 +2841,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1982
2841
  let alignment = AlignmentType.LEFT;
1983
2842
  if (align === "center") alignment = AlignmentType.CENTER;
1984
2843
  if (align === "right") alignment = AlignmentType.RIGHT;
1985
- const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir);
2844
+ const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir, {
2845
+ font: defaultFont,
2846
+ size: 20,
2847
+ color: isHeader ? "0F172A" : "334155",
2848
+ bold: isHeader
2849
+ });
1986
2850
  cells.push(
1987
2851
  new TableCell({
1988
2852
  width: { size: colWidth, type: WidthType.DXA },
1989
- shading: rowNode.isHeader ? { fill: "F1F5F9", type: ShadingType.CLEAR } : void 0,
1990
- margins: { top: 100, bottom: 100, left: 120, right: 120 },
2853
+ shading: { fill: rowBg, type: ShadingType.CLEAR },
2854
+ margins: { top: 100, bottom: 100, left: 140, right: 140 },
1991
2855
  borders: {
1992
- top: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
1993
- bottom: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
1994
- left: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
1995
- right: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" }
2856
+ top: { style: BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2857
+ bottom: { style: BorderStyle.SINGLE, size: isHeader ? 8 : 4, color: isHeader ? "CBD5E1" : "E2E8F0" },
2858
+ left: { style: BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2859
+ right: { style: BorderStyle.SINGLE, size: 4, color: "E2E8F0" }
1996
2860
  },
1997
2861
  children: [
1998
2862
  new Paragraph({
1999
2863
  alignment,
2000
- children: rowNode.isHeader ? runs.map((r) => r instanceof TextRun ? new TextRun({ ...r, bold: true, color: "0F172A" }) : r) : runs
2864
+ children: runs
2001
2865
  })
2002
2866
  ]
2003
2867
  })
@@ -2006,7 +2870,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2006
2870
  }
2007
2871
  tableRows.push(
2008
2872
  new TableRow({
2009
- tableHeader: rowNode.isHeader,
2873
+ tableHeader: isHeader,
2010
2874
  children: cells
2011
2875
  })
2012
2876
  );
@@ -2022,7 +2886,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2022
2886
  }
2023
2887
  if (node.type === "list" && node.children) {
2024
2888
  for (const item of node.children) {
2025
- const runs = await convertInlinesToTextRuns(item.inlines, baseDir);
2889
+ const runs = await convertInlinesToTextRuns(item.inlines, baseDir, {
2890
+ font: defaultFont,
2891
+ size: 21,
2892
+ color: "334155"
2893
+ });
2026
2894
  let prefix = "";
2027
2895
  if (item.checked !== void 0) {
2028
2896
  prefix = item.checked ? "[X] " : "[ ] ";
@@ -2034,7 +2902,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2034
2902
  ...prefix ? [new TextRun({ text: prefix, bold: true, font: "Consolas" })] : [],
2035
2903
  ...runs
2036
2904
  ],
2037
- spacing: { before: 40, after: 40 }
2905
+ spacing: { before: 20, after: 20, line: 260 }
2038
2906
  })
2039
2907
  );
2040
2908
  }
@@ -2109,88 +2977,258 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2109
2977
  continue;
2110
2978
  }
2111
2979
  }
2112
- const headerObj = metadata.header || config.header;
2113
- const footerObj = metadata.footer || config.footer;
2114
- const headerTabStops = [
2115
- { type: TabStopType.CENTER, position: 4513 },
2116
- { type: TabStopType.RIGHT, position: 9026 }
2117
- ];
2118
- const docHeader = headerObj ? new Header({
2980
+ if (resolved.signatures && resolved.signatures.items.length > 0) {
2981
+ const sig = resolved.signatures;
2982
+ const numItems = sig.items.length;
2983
+ const contentWidth = Math.max(
2984
+ 1e3,
2985
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
2986
+ );
2987
+ docElements.push(new Paragraph({ spacing: { before: sig.spacingBeforeTwip } }));
2988
+ const sigCells = [];
2989
+ const colWidths = [];
2990
+ if (numItems === 1) {
2991
+ const cardWidth = Math.min(3400, Math.floor(contentWidth * 0.42));
2992
+ const spacerWidth = contentWidth - cardWidth;
2993
+ const cardCell = await buildDocxSignatureCell(sig.items[0], sig, cardWidth, defaultFont, baseDir);
2994
+ if (sig.align === "left") {
2995
+ colWidths.push(cardWidth, spacerWidth);
2996
+ sigCells.push(cardCell, createEmptyDocxCell(spacerWidth));
2997
+ } else if (sig.align === "center") {
2998
+ const sideWidth = Math.floor(spacerWidth / 2);
2999
+ colWidths.push(sideWidth, cardWidth, sideWidth);
3000
+ sigCells.push(createEmptyDocxCell(sideWidth), cardCell, createEmptyDocxCell(sideWidth));
3001
+ } else {
3002
+ colWidths.push(spacerWidth, cardWidth);
3003
+ sigCells.push(createEmptyDocxCell(spacerWidth), cardCell);
3004
+ }
3005
+ } else {
3006
+ const colWidth = Math.floor(contentWidth / numItems);
3007
+ for (let i = 0; i < numItems; i++) {
3008
+ colWidths.push(colWidth);
3009
+ const cell = await buildDocxSignatureCell(sig.items[i], sig, colWidth, defaultFont, baseDir);
3010
+ sigCells.push(cell);
3011
+ }
3012
+ }
3013
+ const sigTable = new Table({
3014
+ width: { size: 100, type: WidthType.PERCENTAGE },
3015
+ columnWidths: colWidths,
3016
+ borders: {
3017
+ top: { style: BorderStyle.NONE, size: 0, color: "auto" },
3018
+ bottom: { style: BorderStyle.NONE, size: 0, color: "auto" },
3019
+ left: { style: BorderStyle.NONE, size: 0, color: "auto" },
3020
+ right: { style: BorderStyle.NONE, size: 0, color: "auto" },
3021
+ insideHorizontal: { style: BorderStyle.NONE, size: 0, color: "auto" },
3022
+ insideVertical: { style: BorderStyle.NONE, size: 0, color: "auto" }
3023
+ },
3024
+ rows: [
3025
+ new TableRow({
3026
+ cantSplit: true,
3027
+ children: sigCells
3028
+ })
3029
+ ]
3030
+ });
3031
+ docElements.push(sigTable);
3032
+ }
3033
+ const contentWidthTwip = Math.max(
3034
+ 1e3,
3035
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
3036
+ );
3037
+ const centerPos = Math.round(contentWidthTwip / 2);
3038
+ const rightPos = contentWidthTwip;
3039
+ const headerRuns = [];
3040
+ if ((_d = resolved.header) == null ? void 0 : _d.left) {
3041
+ headerRuns.push(
3042
+ new TextRun({
3043
+ text: resolved.header.left.text,
3044
+ color: resolved.header.left.color.replace("#", ""),
3045
+ size: (resolved.header.left.fontSize || 9) * 2,
3046
+ font: resolved.header.left.fontFamily || defaultFont,
3047
+ bold: resolved.header.left.bold,
3048
+ italics: resolved.header.left.italic
3049
+ })
3050
+ );
3051
+ }
3052
+ headerRuns.push(new TextRun({ text: " " }));
3053
+ if ((_e = resolved.header) == null ? void 0 : _e.center) {
3054
+ headerRuns.push(
3055
+ new TextRun({
3056
+ text: resolved.header.center.text,
3057
+ color: resolved.header.center.color.replace("#", ""),
3058
+ size: (resolved.header.center.fontSize || 9) * 2,
3059
+ font: resolved.header.center.fontFamily || defaultFont,
3060
+ bold: resolved.header.center.bold,
3061
+ italics: resolved.header.center.italic
3062
+ })
3063
+ );
3064
+ }
3065
+ headerRuns.push(new TextRun({ text: " " }));
3066
+ if ((_f = resolved.header) == null ? void 0 : _f.right) {
3067
+ headerRuns.push(
3068
+ new TextRun({
3069
+ text: resolved.header.right.text,
3070
+ color: resolved.header.right.color.replace("#", ""),
3071
+ size: (resolved.header.right.fontSize || 9) * 2,
3072
+ font: resolved.header.right.fontFamily || defaultFont,
3073
+ bold: resolved.header.right.bold,
3074
+ italics: resolved.header.right.italic
3075
+ })
3076
+ );
3077
+ }
3078
+ const docHeader = resolved.header ? new Header({
2119
3079
  children: [
2120
3080
  new Paragraph({
2121
- tabStops: headerTabStops,
2122
- children: [
2123
- // Left zone
2124
- ...headerObj.left ? [
3081
+ tabStops: [
3082
+ {
3083
+ type: TabStopType.CENTER,
3084
+ position: centerPos
3085
+ },
3086
+ {
3087
+ type: TabStopType.RIGHT,
3088
+ position: rightPos
3089
+ }
3090
+ ],
3091
+ border: resolved.header.divider ? {
3092
+ bottom: {
3093
+ style: BorderStyle.SINGLE,
3094
+ size: 4,
3095
+ space: 6,
3096
+ color: (resolved.header.dividerColor || "#CBD5E1").replace("#", "")
3097
+ }
3098
+ } : void 0,
3099
+ children: headerRuns,
3100
+ spacing: { after: 120 }
3101
+ })
3102
+ ]
3103
+ }) : void 0;
3104
+ const footerRuns = [];
3105
+ if ((_g = resolved.footer) == null ? void 0 : _g.left) {
3106
+ footerRuns.push(
3107
+ new TextRun({
3108
+ text: resolved.footer.left.text,
3109
+ color: resolved.footer.left.color.replace("#", ""),
3110
+ size: (resolved.footer.left.fontSize || 9) * 2,
3111
+ font: resolved.footer.left.fontFamily || defaultFont,
3112
+ bold: resolved.footer.left.bold,
3113
+ italics: resolved.footer.left.italic
3114
+ })
3115
+ );
3116
+ }
3117
+ footerRuns.push(new TextRun({ text: " " }));
3118
+ if ((_h = resolved.footer) == null ? void 0 : _h.center) {
3119
+ footerRuns.push(
3120
+ new TextRun({
3121
+ text: resolved.footer.center.text,
3122
+ color: resolved.footer.center.color.replace("#", ""),
3123
+ size: (resolved.footer.center.fontSize || 9) * 2,
3124
+ font: resolved.footer.center.fontFamily || defaultFont,
3125
+ bold: resolved.footer.center.bold,
3126
+ italics: resolved.footer.center.italic
3127
+ })
3128
+ );
3129
+ }
3130
+ footerRuns.push(new TextRun({ text: " " }));
3131
+ if ((_i = resolved.footer) == null ? void 0 : _i.right) {
3132
+ const rZone = resolved.footer.right;
3133
+ const rColor = rZone.color.replace("#", "");
3134
+ const rSize = (rZone.fontSize || 9) * 2;
3135
+ const rFont = rZone.fontFamily || defaultFont;
3136
+ const rBold = rZone.bold;
3137
+ const rItalics = rZone.italic;
3138
+ if (rZone.text.includes("{page}") || rZone.text.includes("{pages}")) {
3139
+ const parts = rZone.text.split(/(\{page\}|\{pages\})/gi);
3140
+ for (const part of parts) {
3141
+ if (part.toLowerCase() === "{page}") {
3142
+ footerRuns.push(
2125
3143
  new TextRun({
2126
- text: headerObj.left.replace("{title}", metadata.title || ""),
2127
- color: "94A3B8",
2128
- size: 18
3144
+ children: [PageNumber.CURRENT],
3145
+ color: rColor,
3146
+ size: rSize,
3147
+ font: rFont,
3148
+ bold: rBold,
3149
+ italics: rItalics
2129
3150
  })
2130
- ] : [],
2131
- // Center zone (tab + text)
2132
- ...headerObj.center ? [
2133
- new TextRun({ text: " ", color: "94A3B8", size: 18 }),
3151
+ );
3152
+ } else if (part.toLowerCase() === "{pages}") {
3153
+ footerRuns.push(
2134
3154
  new TextRun({
2135
- text: headerObj.center.replace("{title}", metadata.title || ""),
2136
- color: "94A3B8",
2137
- size: 18
3155
+ children: [PageNumber.TOTAL_PAGES],
3156
+ color: rColor,
3157
+ size: rSize,
3158
+ font: rFont,
3159
+ bold: rBold,
3160
+ italics: rItalics
2138
3161
  })
2139
- ] : [],
2140
- // Right zone (tab + text) — skip extra tab if center already used one
2141
- ...headerObj.right ? [
2142
- new TextRun({
2143
- text: headerObj.left || headerObj.center ? " " : "",
2144
- color: "94A3B8",
2145
- size: 18
2146
- }),
3162
+ );
3163
+ } else if (part) {
3164
+ footerRuns.push(
2147
3165
  new TextRun({
2148
- text: headerObj.right.replace("{title}", metadata.title || ""),
2149
- color: "94A3B8",
2150
- size: 18
3166
+ text: part,
3167
+ color: rColor,
3168
+ size: rSize,
3169
+ font: rFont,
3170
+ bold: rBold,
3171
+ italics: rItalics
2151
3172
  })
2152
- ] : []
2153
- ]
2154
- })
2155
- ]
2156
- }) : void 0;
2157
- const docFooter = footerObj ? new Footer({
3173
+ );
3174
+ }
3175
+ }
3176
+ } else {
3177
+ footerRuns.push(
3178
+ new TextRun({
3179
+ text: rZone.text,
3180
+ color: rColor,
3181
+ size: rSize,
3182
+ font: rFont,
3183
+ bold: rBold,
3184
+ italics: rItalics
3185
+ })
3186
+ );
3187
+ }
3188
+ }
3189
+ const docFooter = resolved.footer ? new Footer({
2158
3190
  children: [
2159
3191
  new Paragraph({
2160
- tabStops: headerTabStops,
2161
- children: [
2162
- // Left zone
2163
- ...footerObj.left ? [
2164
- new TextRun({
2165
- text: footerObj.left.replace("{page}", "").replace("{pages}", "").trim(),
2166
- color: "94A3B8",
2167
- size: 18
2168
- })
2169
- ] : [],
2170
- // Right zone: always includes page number if right is configured or footer exists
2171
- new TextRun({ text: " ", color: "94A3B8", size: 18 }),
2172
- new TextRun({ text: "Page ", color: "94A3B8", size: 18 }),
2173
- new TextRun({ children: [PageNumber.CURRENT], color: "94A3B8", size: 18 }),
2174
- new TextRun({ text: " of ", color: "94A3B8", size: 18 }),
2175
- new TextRun({ children: [PageNumber.TOTAL_PAGES], color: "94A3B8", size: 18 })
2176
- ]
3192
+ tabStops: [
3193
+ {
3194
+ type: TabStopType.CENTER,
3195
+ position: centerPos
3196
+ },
3197
+ {
3198
+ type: TabStopType.RIGHT,
3199
+ position: rightPos
3200
+ }
3201
+ ],
3202
+ border: resolved.footer.divider ? {
3203
+ top: {
3204
+ style: BorderStyle.SINGLE,
3205
+ size: 4,
3206
+ space: 6,
3207
+ color: (resolved.footer.dividerColor || "#CBD5E1").replace("#", "")
3208
+ }
3209
+ } : void 0,
3210
+ children: footerRuns,
3211
+ spacing: { before: 120 }
2177
3212
  })
2178
3213
  ]
2179
3214
  }) : void 0;
2180
- const topMargin = parseMarginToTwip(((_d = metadata.margins) == null ? void 0 : _d.top) || ((_e = config.margins) == null ? void 0 : _e.top), 1440);
2181
- const bottomMargin = parseMarginToTwip(((_f = metadata.margins) == null ? void 0 : _f.bottom) || ((_g = config.margins) == null ? void 0 : _g.bottom), 1440);
2182
- const leftMargin = parseMarginToTwip(((_h = metadata.margins) == null ? void 0 : _h.left) || ((_i = config.margins) == null ? void 0 : _i.left), 1440);
2183
- const rightMargin = parseMarginToTwip(((_j = metadata.margins) == null ? void 0 : _j.right) || ((_k = config.margins) == null ? void 0 : _k.right), 1440);
2184
- const isLandscape = (metadata.orientation || config.orientation) === "landscape";
3215
+ const isLandscape = resolved.orientation === "landscape";
2185
3216
  const document = new Document({
2186
3217
  styles: {
2187
3218
  default: {
2188
3219
  document: {
2189
3220
  run: {
2190
- font: "Segoe UI",
2191
- size: 22,
2192
- // 11pt
2193
- color: "0F172A"
3221
+ font: defaultFont,
3222
+ size: 21,
3223
+ // 10.5pt
3224
+ color: textHex
3225
+ },
3226
+ paragraph: {
3227
+ spacing: {
3228
+ line: 276,
3229
+ // 1.15 line spacing
3230
+ after: 140
3231
+ }
2194
3232
  }
2195
3233
  }
2196
3234
  }
@@ -2200,13 +3238,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2200
3238
  properties: {
2201
3239
  page: {
2202
3240
  size: {
3241
+ width: resolved.paperDimensions.widthTwip,
3242
+ height: resolved.paperDimensions.heightTwip,
2203
3243
  orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
2204
3244
  },
2205
3245
  margin: {
2206
- top: topMargin,
2207
- bottom: bottomMargin,
2208
- left: leftMargin,
2209
- right: rightMargin,
3246
+ top: resolved.margins.topTwip,
3247
+ bottom: resolved.margins.bottomTwip,
3248
+ left: resolved.margins.leftTwip,
3249
+ right: resolved.margins.rightTwip,
2210
3250
  header: 720,
2211
3251
  footer: 720
2212
3252
  }
@@ -2220,153 +3260,139 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2220
3260
  });
2221
3261
  return await Packer.toBuffer(document);
2222
3262
  }
2223
-
2224
- // src/config/loadConfig.ts
2225
- import * as fs5 from "fs";
2226
- import * as path5 from "path";
2227
- import { pathToFileURL as pathToFileURL3 } from "url";
2228
- import * as YAML from "yaml";
2229
- var DEFAULT_CONFIG_FILENAMES = [
2230
- "markforge.config.json",
2231
- ".markforgerc.json",
2232
- "markforge.config.yaml",
2233
- "markforge.config.yml",
2234
- ".markforgerc.yaml",
2235
- ".markforgerc.yml",
2236
- ".markforgerc",
2237
- "markforge.config.ts",
2238
- "markforge.config.js",
2239
- "markforge.config.mjs",
2240
- "markforge.config.cjs"
2241
- ];
2242
- var DEFAULT_CONFIG = {
2243
- to: ["docx", "pdf"],
2244
- outputDir: void 0,
2245
- theme: "default",
2246
- css: void 0,
2247
- orientation: "portrait",
2248
- paperSize: "A4",
2249
- margins: {
2250
- top: "2.5cm",
2251
- bottom: "2.5cm",
2252
- left: "2.5cm",
2253
- right: "2.5cm"
2254
- },
2255
- header: void 0,
2256
- footer: {
2257
- right: "Page {page} of {pages}"
2258
- },
2259
- toc: false,
2260
- watermark: void 0,
2261
- embedImages: true,
2262
- metadata: void 0,
2263
- watch: false,
2264
- serve: false,
2265
- port: 4e3,
2266
- open: false,
2267
- bundleHtml: true,
2268
- syntaxTheme: "github-dark"
2269
- };
2270
- function discoverConfigFile(startDir) {
2271
- const dirsToCheck = [];
2272
- let curr = path5.resolve(startDir);
2273
- while (curr) {
2274
- dirsToCheck.push(curr);
2275
- const parent = path5.dirname(curr);
2276
- if (parent === curr) break;
2277
- curr = parent;
2278
- }
2279
- const cwd = path5.resolve(process.cwd());
2280
- if (!dirsToCheck.includes(cwd)) {
2281
- dirsToCheck.push(cwd);
2282
- }
2283
- for (const dir of dirsToCheck) {
2284
- for (const filename of DEFAULT_CONFIG_FILENAMES) {
2285
- const candidate = path5.join(dir, filename);
2286
- if (fs5.existsSync(candidate) && fs5.statSync(candidate).isFile()) {
2287
- return candidate;
2288
- }
2289
- }
3263
+ async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
3264
+ const cellParagraphs = [];
3265
+ if (item.title) {
3266
+ cellParagraphs.push(
3267
+ new Paragraph({
3268
+ children: [
3269
+ new TextRun({
3270
+ text: item.title,
3271
+ font: defaultFont,
3272
+ size: 18,
3273
+ // 9pt
3274
+ color: sig.titleColor.replace("#", ""),
3275
+ bold: true
3276
+ })
3277
+ ],
3278
+ spacing: { after: 60 }
3279
+ })
3280
+ );
2290
3281
  }
2291
- return null;
2292
- }
2293
- async function loadConfig(customPath, startDir = process.cwd()) {
2294
- let resolvedPath = null;
2295
- if (customPath) {
2296
- resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(process.cwd(), customPath);
2297
- if (!fs5.existsSync(resolvedPath)) {
2298
- const altPath = path5.resolve(startDir, customPath);
2299
- if (fs5.existsSync(altPath)) {
2300
- resolvedPath = altPath;
2301
- } else {
2302
- throw new Error(`Configuration file not found: ${resolvedPath}`);
2303
- }
3282
+ if (item.image) {
3283
+ const resolvedImg = await resolveImage(item.image, baseDir);
3284
+ if (resolvedImg) {
3285
+ cellParagraphs.push(
3286
+ new Paragraph({
3287
+ children: [
3288
+ new ImageRun({
3289
+ data: resolvedImg.buffer,
3290
+ transformation: {
3291
+ width: 140,
3292
+ height: 60
3293
+ },
3294
+ type: "png"
3295
+ })
3296
+ ],
3297
+ spacing: { before: 40, after: 40 }
3298
+ })
3299
+ );
3300
+ } else {
3301
+ cellParagraphs.push(new Paragraph({ spacing: { before: 240, after: 240 } }));
2304
3302
  }
2305
3303
  } else {
2306
- resolvedPath = discoverConfigFile(startDir);
3304
+ cellParagraphs.push(new Paragraph({ spacing: { before: 240, after: 240 } }));
2307
3305
  }
2308
- if (!resolvedPath) {
2309
- return {
2310
- config: { ...DEFAULT_CONFIG },
2311
- configPath: null
2312
- };
3306
+ if (sig.style === "line") {
3307
+ cellParagraphs.push(
3308
+ new Paragraph({
3309
+ border: {
3310
+ bottom: {
3311
+ style: BorderStyle.SINGLE,
3312
+ size: 6,
3313
+ space: 2,
3314
+ color: sig.borderColor.replace("#", "")
3315
+ }
3316
+ },
3317
+ spacing: { after: 60 }
3318
+ })
3319
+ );
2313
3320
  }
2314
- const ext = path5.extname(resolvedPath).toLowerCase();
2315
- let userConfig = {};
2316
- try {
2317
- if (ext === ".json" || ext === "" || resolvedPath.endsWith(".markforgerc")) {
2318
- const raw = fs5.readFileSync(resolvedPath, "utf-8").trim();
2319
- try {
2320
- userConfig = JSON.parse(raw);
2321
- } catch {
2322
- userConfig = YAML.parse(raw) || {};
2323
- }
2324
- } else if (ext === ".yaml" || ext === ".yml") {
2325
- const raw = fs5.readFileSync(resolvedPath, "utf-8");
2326
- userConfig = YAML.parse(raw) || {};
2327
- } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
2328
- try {
2329
- const fileUrl = `${pathToFileURL3(resolvedPath).href}?t=${Date.now()}`;
2330
- const mod = await import(fileUrl);
2331
- const rawExport = mod.default ?? mod.config ?? mod;
2332
- userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
2333
- } catch (importErr) {
2334
- try {
2335
- const mod = __require(resolvedPath);
2336
- const rawExport = mod.default ?? mod.config ?? mod;
2337
- userConfig = (typeof rawExport === "function" ? await rawExport() : rawExport) || {};
2338
- } catch {
2339
- throw importErr;
2340
- }
2341
- }
2342
- }
2343
- } catch (err) {
2344
- throw new Error(
2345
- `Failed to parse configuration file at ${resolvedPath}: ${err instanceof Error ? err.message : String(err)}`
3321
+ cellParagraphs.push(
3322
+ new Paragraph({
3323
+ children: [
3324
+ new TextRun({
3325
+ text: item.name,
3326
+ font: defaultFont,
3327
+ size: 21,
3328
+ // 10.5pt
3329
+ bold: true,
3330
+ color: sig.nameColor.replace("#", "")
3331
+ })
3332
+ ],
3333
+ spacing: { before: sig.style === "line" ? 40 : 20, after: 20 }
3334
+ })
3335
+ );
3336
+ if (item.role) {
3337
+ cellParagraphs.push(
3338
+ new Paragraph({
3339
+ children: [
3340
+ new TextRun({
3341
+ text: item.role,
3342
+ font: defaultFont,
3343
+ size: 18,
3344
+ // 9pt
3345
+ color: sig.roleColor.replace("#", "")
3346
+ })
3347
+ ],
3348
+ spacing: { after: 20 }
3349
+ })
2346
3350
  );
2347
3351
  }
2348
- if (userConfig && typeof userConfig === "object" && "$schema" in userConfig) {
2349
- delete userConfig.$schema;
3352
+ if (item.date) {
3353
+ cellParagraphs.push(
3354
+ new Paragraph({
3355
+ children: [
3356
+ new TextRun({
3357
+ text: `Date: ${item.date}`,
3358
+ font: defaultFont,
3359
+ size: 17,
3360
+ // 8.5pt
3361
+ color: sig.roleColor.replace("#", "")
3362
+ })
3363
+ ],
3364
+ spacing: { after: 20 }
3365
+ })
3366
+ );
2350
3367
  }
2351
- const parsedConfig = userConfig;
2352
- const mergedConfig = {
2353
- ...DEFAULT_CONFIG,
2354
- ...parsedConfig,
2355
- margins: {
2356
- ...DEFAULT_CONFIG.margins,
2357
- ...parsedConfig.margins || {}
3368
+ const isBox = sig.style === "box";
3369
+ const boxBorder = { style: BorderStyle.SINGLE, size: 4, color: sig.borderColor.replace("#", "") };
3370
+ const noneBorder = { style: BorderStyle.NONE, size: 0, color: "auto" };
3371
+ return new TableCell({
3372
+ width: { size: widthDxa, type: WidthType.DXA },
3373
+ shading: isBox ? { fill: "F8FAFC", type: ShadingType.CLEAR } : void 0,
3374
+ margins: isBox ? { top: 140, bottom: 140, left: 160, right: 160 } : { top: 60, bottom: 60, left: 60, right: 60 },
3375
+ borders: {
3376
+ top: isBox ? boxBorder : noneBorder,
3377
+ bottom: isBox ? boxBorder : noneBorder,
3378
+ left: isBox ? boxBorder : noneBorder,
3379
+ right: isBox ? boxBorder : noneBorder
2358
3380
  },
2359
- header: parsedConfig.header !== void 0 ? parsedConfig.header : DEFAULT_CONFIG.header,
2360
- footer: parsedConfig.footer !== void 0 ? parsedConfig.footer : DEFAULT_CONFIG.footer,
2361
- metadata: {
2362
- ...DEFAULT_CONFIG.metadata || {},
2363
- ...parsedConfig.metadata || {}
2364
- }
2365
- };
2366
- return {
2367
- config: mergedConfig,
2368
- configPath: resolvedPath
2369
- };
3381
+ children: cellParagraphs
3382
+ });
3383
+ }
3384
+ function createEmptyDocxCell(widthDxa) {
3385
+ const noneBorder = { style: BorderStyle.NONE, size: 0, color: "auto" };
3386
+ return new TableCell({
3387
+ width: { size: widthDxa, type: WidthType.DXA },
3388
+ borders: {
3389
+ top: noneBorder,
3390
+ bottom: noneBorder,
3391
+ left: noneBorder,
3392
+ right: noneBorder
3393
+ },
3394
+ children: [new Paragraph({})]
3395
+ });
2370
3396
  }
2371
3397
 
2372
3398
  // src/core/engine.ts
@@ -2469,7 +3495,7 @@ function defineConfig(config) {
2469
3495
  // src/version.ts
2470
3496
  import * as fs7 from "fs";
2471
3497
  import * as path7 from "path";
2472
- import { fileURLToPath as fileURLToPath2 } from "url";
3498
+ import { fileURLToPath } from "url";
2473
3499
  try {
2474
3500
  if (typeof globalThis !== "undefined" && (!globalThis.localStorage || typeof globalThis.localStorage.getItem !== "function")) {
2475
3501
  Object.defineProperty(globalThis, "localStorage", {
@@ -2490,7 +3516,7 @@ try {
2490
3516
  }
2491
3517
  } catch {
2492
3518
  }
2493
- var FALLBACK_VERSION = "0.2.2";
3519
+ var FALLBACK_VERSION = "0.3.0";
2494
3520
  function readVersionFromPackageJson(fromDir) {
2495
3521
  let currentDir = fromDir;
2496
3522
  for (let i = 0; i < 6; i++) {
@@ -2515,7 +3541,7 @@ function getPackageDir() {
2515
3541
  return __dirname;
2516
3542
  }
2517
3543
  try {
2518
- return path7.dirname(fileURLToPath2(import.meta.url));
3544
+ return path7.dirname(fileURLToPath(import.meta.url));
2519
3545
  } catch {
2520
3546
  return process.cwd();
2521
3547
  }
@@ -2527,10 +3553,17 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
2527
3553
  export {
2528
3554
  DEFAULT_CONFIG,
2529
3555
  MARKFORGE_VERSION,
3556
+ Orientation,
3557
+ OutputFormat,
3558
+ PAPER_DIMENSIONS_TWIP,
3559
+ PaperSizeEnum,
2530
3560
  SYNTAX_COLORS,
3561
+ SyntaxTheme,
2531
3562
  THEMES,
2532
- THEME_ACADEMIC,
3563
+ THEME_CORPORATE,
2533
3564
  THEME_DEFAULT,
3565
+ Theme,
3566
+ WatermarkPosition,
2534
3567
  buildDocxDocument,
2535
3568
  buildHtmlDocument,
2536
3569
  buildPdfDocument,
@@ -2539,6 +3572,7 @@ export {
2539
3572
  escapeHtml,
2540
3573
  findChromeExecutable,
2541
3574
  formatServerTimestamp,
3575
+ generateThemeCss,
2542
3576
  getMarkforgeVersion,
2543
3577
  getMimeType,
2544
3578
  highlightCodeToHtml,
@@ -2546,11 +3580,17 @@ export {
2546
3580
  inlineHtmlImages,
2547
3581
  loadConfig,
2548
3582
  compileMarkdown as markforge,
3583
+ normalizeHeaderFooter,
3584
+ normalizeHeaderFooterSlot,
3585
+ normalizeSignatures,
3586
+ normalizeWatermark,
2549
3587
  parseInlineSpans,
2550
- parseMarginToTwip,
3588
+ parseMarginToTwip2 as parseMarginToTwip,
2551
3589
  parseMarkdownDocument,
2552
3590
  renderInlinesToHtml,
2553
3591
  renderMermaidToPng,
3592
+ replaceDocumentTokens,
3593
+ resolveDocumentConfig,
2554
3594
  resolveImage,
2555
3595
  slugify,
2556
3596
  tokenizeCodeLine