@masumdev/markforge 0.2.3 → 0.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@ import {
12
12
  } from "./chunk-NGC2ZAWZ.mjs";
13
13
  import {
14
14
  DEFAULT_CONFIG
15
- } from "./chunk-ASXSHC6H.mjs";
15
+ } from "./chunk-IYGPJIK5.mjs";
16
16
  import "./chunk-YZHGBG4N.mjs";
17
17
 
18
18
  // src/ui/App.tsx
@@ -22,7 +22,8 @@ import { Box as Box4 } from "ink";
22
22
  // src/ui/Header.tsx
23
23
  import { Box, Text } from "ink";
24
24
  import { jsx, jsxs } from "react/jsx-runtime";
25
- var Header = ({ inputFile, theme = "default" }) => {
25
+ var Header = ({ inputFile, theme = "corporate" }) => {
26
+ const themeLabel = typeof theme === "object" ? "custom (ThemeProps)" : String(theme || "corporate");
26
27
  return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: /* @__PURE__ */ jsxs(
27
28
  Box,
28
29
  {
@@ -45,7 +46,7 @@ var Header = ({ inputFile, theme = "default" }) => {
45
46
  ] }),
46
47
  /* @__PURE__ */ jsxs(Box, { children: [
47
48
  /* @__PURE__ */ jsx(Text, { color: "gray", children: "Theme: " }),
48
- /* @__PURE__ */ jsx(Text, { color: "blue", children: theme })
49
+ /* @__PURE__ */ jsx(Text, { color: "blue", children: themeLabel })
49
50
  ] })
50
51
  ]
51
52
  }
@@ -649,6 +650,51 @@ var SYNTAX_COLORS_LIGHT = {
649
650
  plain: "24292E"
650
651
  // Near-black — identifiers
651
652
  };
653
+ var SYNTAX_THEMES = {
654
+ "github-dark": SYNTAX_COLORS,
655
+ "dark": SYNTAX_COLORS,
656
+ "github-light": SYNTAX_COLORS_LIGHT,
657
+ "light": SYNTAX_COLORS_LIGHT,
658
+ "dracula": {
659
+ keyword: "FF79C6",
660
+ string: "F1FA8C",
661
+ comment: "6272A4",
662
+ number: "BD93F9",
663
+ boolean: "BD93F9",
664
+ function: "50FA7B",
665
+ type: "8BE9FD",
666
+ operator: "FF79C6",
667
+ punctuation: "F8F8F2",
668
+ plain: "F8F8F2"
669
+ },
670
+ "monokai": {
671
+ keyword: "F92672",
672
+ string: "E6DB74",
673
+ comment: "75715E",
674
+ number: "AE81FF",
675
+ boolean: "AE81FF",
676
+ function: "A6E22E",
677
+ type: "66D9EF",
678
+ operator: "F92672",
679
+ punctuation: "F8F8F2",
680
+ plain: "F8F8F2"
681
+ },
682
+ "nord": {
683
+ keyword: "81A1C1",
684
+ string: "A3BE8C",
685
+ comment: "616E88",
686
+ number: "B48EAD",
687
+ boolean: "81A1C1",
688
+ function: "88C0D0",
689
+ type: "8FBCBB",
690
+ operator: "81A1C1",
691
+ punctuation: "ECEFF4",
692
+ plain: "D8DEE9"
693
+ }
694
+ };
695
+ function getSyntaxPalette(themeName = "github-dark") {
696
+ return SYNTAX_THEMES[themeName.toLowerCase()] || SYNTAX_THEMES["github-dark"];
697
+ }
652
698
  var JS_KEYWORDS = /* @__PURE__ */ new Set([
653
699
  "import",
654
700
  "from",
@@ -792,9 +838,9 @@ var SQL_KEYWORDS = /* @__PURE__ */ new Set([
792
838
  "foreign",
793
839
  "references"
794
840
  ]);
795
- function tokenizeCodeLine(line, lang = "", theme = "dark") {
841
+ function tokenizeCodeLine(line, lang = "", theme = "github-dark") {
796
842
  var _a;
797
- const COLORS = theme === "light" ? SYNTAX_COLORS_LIGHT : SYNTAX_COLORS;
843
+ const COLORS = getSyntaxPalette(theme);
798
844
  if (!line) {
799
845
  return [{ text: " ", type: "plain", colorHex: COLORS.plain }];
800
846
  }
@@ -877,6 +923,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
877
923
  tokens.push({ text: word, type: "keyword", colorHex: COLORS.keyword, bold: true });
878
924
  continue;
879
925
  }
926
+ if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
927
+ tokens.push({ text: word, type: "type", colorHex: COLORS.type });
928
+ continue;
929
+ }
880
930
  let nextNonWs = pos;
881
931
  while (nextNonWs < line.length && /\s/.test(line[nextNonWs])) {
882
932
  nextNonWs++;
@@ -885,10 +935,6 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
885
935
  tokens.push({ text: word, type: "function", colorHex: COLORS.function });
886
936
  continue;
887
937
  }
888
- if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
889
- tokens.push({ text: word, type: "type", colorHex: COLORS.type });
890
- continue;
891
- }
892
938
  tokens.push({ text: word, type: "plain", colorHex: COLORS.plain });
893
939
  continue;
894
940
  }
@@ -901,10 +947,10 @@ function tokenizeCodeLine(line, lang = "", theme = "dark") {
901
947
  }
902
948
  return tokens;
903
949
  }
904
- function highlightCodeToHtml(code, lang = "") {
950
+ function highlightCodeToHtml(code, lang = "", theme = "github-dark") {
905
951
  const lines = (code || "").split("\n");
906
952
  const htmlLines = lines.map((line) => {
907
- const tokens = tokenizeCodeLine(line, lang);
953
+ const tokens = tokenizeCodeLine(line, lang, theme);
908
954
  return tokens.map((t) => {
909
955
  const escaped = t.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
910
956
  if (t.type === "plain") return escaped;
@@ -1013,7 +1059,7 @@ hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
1013
1059
  pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
1014
1060
  }
1015
1061
  `;
1016
- var THEME_DEFAULT = `
1062
+ var THEME_CORPORATE = `
1017
1063
  :root {
1018
1064
  --mf-bg: #ffffff;
1019
1065
  --mf-text: #0f172a;
@@ -1025,49 +1071,266 @@ var THEME_DEFAULT = `
1025
1071
  --mf-card-bg: #f8fafc;
1026
1072
  --mf-code-bg: #0f172a;
1027
1073
  --mf-code-text: #f8fafc;
1028
- --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
1074
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1029
1075
  --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1030
1076
  }
1031
1077
  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; }
1032
- .document-container { max-width: 860px; margin: 0 auto; }
1078
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1033
1079
  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; }
1034
- h1 { font-size: 2.2rem; border-bottom: 2px solid #33CDCF; padding-bottom: 0.5rem; }
1035
- h2 { font-size: 1.6rem; color: #009DA0; border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
1080
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1081
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
1036
1082
  h3 { font-size: 1.3rem; }
1037
1083
  h4 { font-size: 1.1rem; }
1038
1084
  p { margin: 0.8rem 0; }
1039
1085
  `;
1040
- var THEME_ACADEMIC = `
1086
+ var THEMES = {
1087
+ corporate: THEME_CORPORATE,
1088
+ default: THEME_CORPORATE
1089
+ };
1090
+ function generateThemeCss(theme) {
1091
+ if (!theme || theme === "corporate" || theme === "default" || theme === "corporate" /* CORPORATE */) {
1092
+ return THEME_CORPORATE;
1093
+ }
1094
+ if (typeof theme === "object") {
1095
+ const bg = theme.backgroundColor || "#ffffff";
1096
+ const text = theme.textColor || "#0f172a";
1097
+ const textMuted = theme.textMuted || "#64748b";
1098
+ const primary = theme.primaryColor || "#33CDCF";
1099
+ const primaryDark = theme.primaryDark || primary;
1100
+ const primaryLight = theme.primaryLight || "#ECFDFD";
1101
+ const border = theme.borderColor || "#e2e8f0";
1102
+ const cardBg = theme.cardBackground || "#f8fafc";
1103
+ const codeBg = theme.codeBackground || "#0f172a";
1104
+ const codeText = theme.codeText || "#f8fafc";
1105
+ const font = theme.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
1106
+ const fontMono = theme.fontMono || "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
1107
+ return `
1041
1108
  :root {
1042
- --mf-bg: #ffffff;
1043
- --mf-text: #1a1a1a;
1044
- --mf-text-muted: #555;
1045
- --mf-primary: #33CDCF;
1046
- --mf-primary-dark: #009DA0;
1047
- --mf-primary-light: #ECFDFD;
1048
- --mf-border: #ccc;
1049
- --mf-card-bg: #f9f9f9;
1050
- --mf-code-bg: #1e1e1e;
1051
- --mf-code-text: #d4d4d4;
1052
- --mf-font-family: "Merriweather", "Georgia", "Times New Roman", serif;
1053
- --mf-font-mono: "Courier New", Courier, monospace;
1109
+ --mf-bg: ${bg};
1110
+ --mf-text: ${text};
1111
+ --mf-text-muted: ${textMuted};
1112
+ --mf-primary: ${primary};
1113
+ --mf-primary-dark: ${primaryDark};
1114
+ --mf-primary-light: ${primaryLight};
1115
+ --mf-border: ${border};
1116
+ --mf-card-bg: ${cardBg};
1117
+ --mf-code-bg: ${codeBg};
1118
+ --mf-code-text: ${codeText};
1119
+ --mf-font-family: ${font};
1120
+ --mf-font-mono: ${fontMono};
1054
1121
  }
1055
- body { font-family: var(--mf-font-family); font-size: 16px; line-height: 1.8; padding: 3rem; color: var(--mf-text); }
1056
- .document-container { max-width: 780px; margin: 0 auto; text-align: justify; }
1057
- h1, h2, h3 { font-family: "Times New Roman", Times, serif; font-weight: bold; text-align: left; }
1058
- h1 { font-size: 2rem; border-bottom: 1px solid #000; padding-bottom: 0.3rem; }
1059
- h2 { font-size: 1.4rem; border-bottom: 1px solid #ccc; padding-bottom: 0.2rem; }
1060
- h3 { font-size: 1.2rem; }
1061
- p { margin: 0.9rem 0; }
1122
+ 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; }
1123
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1124
+ 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; }
1125
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1126
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
1127
+ h3 { font-size: 1.3rem; }
1128
+ h4 { font-size: 1.1rem; }
1129
+ p { margin: 0.8rem 0; }
1130
+ ${theme.customCss || ""}
1062
1131
  `;
1063
- var THEMES = {
1064
- default: THEME_DEFAULT,
1065
- academic: THEME_ACADEMIC,
1066
- github: THEME_DEFAULT,
1067
- corporate: THEME_DEFAULT,
1068
- minimal: THEME_DEFAULT,
1069
- dracula: THEME_DEFAULT
1132
+ }
1133
+ if (typeof theme === "string" && THEMES[theme]) {
1134
+ return THEMES[theme];
1135
+ }
1136
+ return THEME_CORPORATE;
1137
+ }
1138
+
1139
+ // src/config/resolveConfig.ts
1140
+ var PAPER_DIMENSIONS_TWIP = {
1141
+ A4: { width: 11906, height: 16838 },
1142
+ // 210mm x 297mm
1143
+ Letter: { width: 12240, height: 15840 },
1144
+ // 8.5in x 11in
1145
+ Legal: { width: 12240, height: 20160 },
1146
+ // 8.5in x 14in
1147
+ A3: { width: 16838, height: 23811 },
1148
+ // 297mm x 420mm
1149
+ A5: { width: 8390, height: 11906 }
1150
+ // 148mm x 210mm
1070
1151
  };
1152
+ function parseMarginToTwip(margin, defaultTwip = 1440) {
1153
+ if (typeof margin === "number") return margin;
1154
+ if (!margin) return defaultTwip;
1155
+ const str = margin.trim().toLowerCase();
1156
+ if (str.endsWith("cm")) {
1157
+ const cm = parseFloat(str);
1158
+ return isNaN(cm) ? defaultTwip : Math.round(cm * 566.929);
1159
+ }
1160
+ if (str.endsWith("mm")) {
1161
+ const mm = parseFloat(str);
1162
+ return isNaN(mm) ? defaultTwip : Math.round(mm * 56.6929);
1163
+ }
1164
+ if (str.endsWith("in") || str.endsWith("inch")) {
1165
+ const inch = parseFloat(str);
1166
+ return isNaN(inch) ? defaultTwip : Math.round(inch * 1440);
1167
+ }
1168
+ if (str.endsWith("pt")) {
1169
+ const pt = parseFloat(str);
1170
+ return isNaN(pt) ? defaultTwip : Math.round(pt * 20);
1171
+ }
1172
+ const val = parseFloat(str);
1173
+ return isNaN(val) ? defaultTwip : Math.round(val);
1174
+ }
1175
+ function formatMarginCss(margin, defaultCss = "2.5cm") {
1176
+ if (margin === void 0 || margin === null) return defaultCss;
1177
+ if (typeof margin === "number") return `${margin}pt`;
1178
+ const str = margin.trim();
1179
+ if (!str) return defaultCss;
1180
+ if (/^[0-9.]+$/.test(str)) return `${str}pt`;
1181
+ return str;
1182
+ }
1183
+ function replaceDocumentTokens(template = "", meta) {
1184
+ 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 || "");
1185
+ }
1186
+ function normalizeWatermark(rawWatermark) {
1187
+ if (!rawWatermark) {
1188
+ return void 0;
1189
+ }
1190
+ if (typeof rawWatermark === "string") {
1191
+ const text = rawWatermark.trim();
1192
+ if (!text) return void 0;
1193
+ return {
1194
+ text,
1195
+ color: "#94a3b8",
1196
+ opacity: 0.08,
1197
+ fontSize: 54,
1198
+ rotate: -45,
1199
+ position: "diagonal"
1200
+ };
1201
+ }
1202
+ if (typeof rawWatermark === "object") {
1203
+ if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
1204
+ return {
1205
+ text: rawWatermark.text.trim(),
1206
+ color: rawWatermark.color || "#94a3b8",
1207
+ opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
1208
+ fontSize: rawWatermark.fontSize || 54,
1209
+ rotate: typeof rawWatermark.rotate === "number" ? rawWatermark.rotate : -45,
1210
+ position: rawWatermark.position || "diagonal"
1211
+ };
1212
+ }
1213
+ return void 0;
1214
+ }
1215
+ function normalizeHeaderFooterSlot(rawSlot, parent, meta) {
1216
+ if (!rawSlot) return void 0;
1217
+ if (typeof rawSlot === "string") {
1218
+ const text = replaceDocumentTokens(rawSlot, meta).trim();
1219
+ if (!text) return void 0;
1220
+ return {
1221
+ text,
1222
+ color: (parent == null ? void 0 : parent.color) || "#94A3B8",
1223
+ fontSize: (parent == null ? void 0 : parent.size) || 9,
1224
+ fontFamily: (parent == null ? void 0 : parent.font) || "Segoe UI",
1225
+ bold: false,
1226
+ italic: false
1227
+ };
1228
+ }
1229
+ if (typeof rawSlot === "object") {
1230
+ const text = replaceDocumentTokens(rawSlot.text || "", meta).trim();
1231
+ if (!text) return void 0;
1232
+ return {
1233
+ text,
1234
+ color: rawSlot.color || (parent == null ? void 0 : parent.color) || "#94A3B8",
1235
+ fontSize: rawSlot.fontSize || (parent == null ? void 0 : parent.size) || 9,
1236
+ fontFamily: rawSlot.fontFamily || (parent == null ? void 0 : parent.font) || "Segoe UI",
1237
+ bold: Boolean(rawSlot.bold),
1238
+ italic: Boolean(rawSlot.italic)
1239
+ };
1240
+ }
1241
+ return void 0;
1242
+ }
1243
+ function normalizeHeaderFooter(raw, meta) {
1244
+ if (!raw) return void 0;
1245
+ const left = normalizeHeaderFooterSlot(raw.left, raw, meta);
1246
+ const center = normalizeHeaderFooterSlot(raw.center, raw, meta);
1247
+ const right = normalizeHeaderFooterSlot(raw.right, raw, meta);
1248
+ if (!left && !center && !right) return void 0;
1249
+ return {
1250
+ left,
1251
+ center,
1252
+ right,
1253
+ font: raw.font || "Segoe UI",
1254
+ size: raw.size || 9,
1255
+ color: raw.color || "#94A3B8",
1256
+ divider: Boolean(raw.divider),
1257
+ dividerColor: raw.dividerColor || "#E2E8F0"
1258
+ };
1259
+ }
1260
+ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1261
+ const configMeta = userConfig.metadata || {};
1262
+ const mergedMeta = { ...configMeta, ...frontmatter };
1263
+ const title = mergedMeta.title || "MarkForge Document";
1264
+ const subtitle = mergedMeta.subtitle || void 0;
1265
+ const author = Array.isArray(mergedMeta.author) ? mergedMeta.author.join(", ") : mergedMeta.author || void 0;
1266
+ const date = mergedMeta.date || void 0;
1267
+ const version = mergedMeta.version || void 0;
1268
+ const company = mergedMeta.company || void 0;
1269
+ const lang = mergedMeta.lang || "en";
1270
+ const tokenContext = { title, subtitle, author, version, date, company };
1271
+ const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
1272
+ const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
1273
+ const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
1274
+ const baseDim = PAPER_DIMENSIONS_TWIP[paperSize] || PAPER_DIMENSIONS_TWIP.A4;
1275
+ const paperDimensions = orientation === "landscape" ? { widthTwip: baseDim.height, heightTwip: baseDim.width } : { widthTwip: baseDim.width, heightTwip: baseDim.height };
1276
+ const fmMargin = mergedMeta.margins || {};
1277
+ const cfgMargin = userConfig.margins || {};
1278
+ const defMargin = DEFAULT_CONFIG.margins || {};
1279
+ const topRaw = fmMargin.top ?? cfgMargin.top ?? defMargin.top ?? "2.5cm";
1280
+ const bottomRaw = fmMargin.bottom ?? cfgMargin.bottom ?? defMargin.bottom ?? "2.5cm";
1281
+ const leftRaw = fmMargin.left ?? cfgMargin.left ?? defMargin.left ?? "2.5cm";
1282
+ const rightRaw = fmMargin.right ?? cfgMargin.right ?? defMargin.right ?? "2.5cm";
1283
+ const margins = {
1284
+ top: formatMarginCss(topRaw),
1285
+ bottom: formatMarginCss(bottomRaw),
1286
+ left: formatMarginCss(leftRaw),
1287
+ right: formatMarginCss(rightRaw),
1288
+ topTwip: parseMarginToTwip(topRaw),
1289
+ bottomTwip: parseMarginToTwip(bottomRaw),
1290
+ leftTwip: parseMarginToTwip(leftRaw),
1291
+ rightTwip: parseMarginToTwip(rightRaw)
1292
+ };
1293
+ const rawHeader = mergedMeta.header || userConfig.header || DEFAULT_CONFIG.header;
1294
+ const rawFooter = mergedMeta.footer || userConfig.footer || DEFAULT_CONFIG.footer;
1295
+ const header = normalizeHeaderFooter(rawHeader, tokenContext);
1296
+ const footer = normalizeHeaderFooter(rawFooter, tokenContext);
1297
+ const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
1298
+ const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
1299
+ const watermark = normalizeWatermark(rawWatermark);
1300
+ const cssList = [];
1301
+ const addCss = (item) => {
1302
+ if (!item) return;
1303
+ if (Array.isArray(item)) cssList.push(...item);
1304
+ else cssList.push(item);
1305
+ };
1306
+ addCss(userConfig.css);
1307
+ addCss(mergedMeta.css);
1308
+ const embedImages = typeof userConfig.embedImages === "boolean" ? userConfig.embedImages : DEFAULT_CONFIG.embedImages;
1309
+ const bundleHtml = typeof userConfig.bundleHtml === "boolean" ? userConfig.bundleHtml : DEFAULT_CONFIG.bundleHtml;
1310
+ const syntaxTheme = userConfig.syntaxTheme || DEFAULT_CONFIG.syntaxTheme || "github-dark";
1311
+ return {
1312
+ title,
1313
+ subtitle,
1314
+ author,
1315
+ date,
1316
+ version,
1317
+ company,
1318
+ lang,
1319
+ theme,
1320
+ orientation,
1321
+ paperSize,
1322
+ paperDimensions,
1323
+ margins,
1324
+ header,
1325
+ footer,
1326
+ toc,
1327
+ watermark,
1328
+ css: cssList,
1329
+ embedImages,
1330
+ bundleHtml,
1331
+ syntaxTheme
1332
+ };
1333
+ }
1071
1334
 
1072
1335
  // src/core/html/htmlBuilder.ts
1073
1336
  function escapeHtml(str) {
@@ -1120,43 +1383,41 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1120
1383
  return result;
1121
1384
  }
1122
1385
  async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1123
- var _a;
1124
- const metadata = { ...config.metadata, ...doc.metadata };
1125
- const themeName = metadata.theme || config.theme || "default";
1126
- const baseThemeCss = THEMES[themeName] || THEMES.default;
1386
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1387
+ const baseThemeCss = generateThemeCss(resolved.theme);
1127
1388
  let customCss = "";
1128
- if (config.css) {
1129
- const cssList = Array.isArray(config.css) ? config.css : [config.css];
1130
- for (const cssPath of cssList) {
1131
- const fullCssPath = path2.isAbsolute(cssPath) ? cssPath : path2.resolve(baseDir, cssPath);
1132
- if (fs2.existsSync(fullCssPath)) {
1133
- customCss += `
1389
+ for (const cssPath of resolved.css) {
1390
+ const fullCssPath = path2.isAbsolute(cssPath) ? cssPath : path2.resolve(baseDir, cssPath);
1391
+ if (fs2.existsSync(fullCssPath)) {
1392
+ customCss += `
1134
1393
  /* Custom CSS: ${cssPath} */
1135
1394
  ` + fs2.readFileSync(fullCssPath, "utf-8");
1136
- }
1137
1395
  }
1138
1396
  }
1139
1397
  const inlinedCss = doc.inlinedStyles.join("\n");
1140
1398
  let bodyHtml = "";
1141
- if (metadata.title) {
1399
+ if (resolved.title) {
1142
1400
  bodyHtml += ` <header class="document-header">
1143
1401
  `;
1144
- bodyHtml += ` <h1 class="document-title">${escapeHtml(metadata.title)}</h1>
1402
+ bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
1145
1403
  `;
1146
- if (metadata.subtitle) {
1147
- bodyHtml += ` <div class="document-subtitle">${escapeHtml(metadata.subtitle)}</div>
1404
+ if (resolved.subtitle) {
1405
+ bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
1148
1406
  `;
1149
1407
  }
1150
- if (metadata.author || metadata.date) {
1408
+ if (resolved.author || resolved.date || resolved.version) {
1151
1409
  bodyHtml += ` <div class="document-meta">
1152
1410
  `;
1153
- if (metadata.author) {
1154
- const authors = Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author;
1155
- bodyHtml += ` <span>Author: ${escapeHtml(authors)}</span>
1411
+ if (resolved.author) {
1412
+ bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
1413
+ `;
1414
+ }
1415
+ if (resolved.version) {
1416
+ bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
1156
1417
  `;
1157
1418
  }
1158
- if (metadata.date) {
1159
- bodyHtml += ` <span>Date: ${escapeHtml(metadata.date)}</span>
1419
+ if (resolved.date) {
1420
+ bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
1160
1421
  `;
1161
1422
  }
1162
1423
  bodyHtml += ` </div>
@@ -1165,22 +1426,20 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1165
1426
  bodyHtml += ` </header>
1166
1427
  `;
1167
1428
  }
1168
- if (config.toc || metadata.toc) {
1169
- if (doc.tocEntries.length > 0) {
1170
- bodyHtml += ` <nav class="table-of-contents">
1429
+ if (resolved.toc && doc.tocEntries.length > 0) {
1430
+ bodyHtml += ` <nav class="table-of-contents">
1171
1431
  `;
1172
- bodyHtml += ` <h2>Table of Contents</h2>
1432
+ bodyHtml += ` <h2>Table of Contents</h2>
1173
1433
  <ul>
1174
1434
  `;
1175
- for (const entry of doc.tocEntries) {
1176
- const indent = " ".repeat(entry.level);
1177
- bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1435
+ for (const entry of doc.tocEntries) {
1436
+ const indent = " ".repeat(entry.level);
1437
+ bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1178
1438
  `;
1179
- }
1180
- bodyHtml += ` </ul>
1439
+ }
1440
+ bodyHtml += ` </ul>
1181
1441
  </nav>
1182
1442
  `;
1183
- }
1184
1443
  }
1185
1444
  for (const node of doc.nodes) {
1186
1445
  if (node.type === "heading") {
@@ -1198,7 +1457,7 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1198
1457
  if (node.type === "codeBlock") {
1199
1458
  const lang = node.language || "";
1200
1459
  const langClass = lang ? ` class="language-${escapeHtml(lang)}"` : "";
1201
- const highlighted = highlightCodeToHtml(node.text || "", lang);
1460
+ const highlighted = highlightCodeToHtml(node.text || "", lang, resolved.syntaxTheme);
1202
1461
  bodyHtml += ` <pre><code${langClass}>${highlighted}</code></pre>
1203
1462
  `;
1204
1463
  continue;
@@ -1242,15 +1501,12 @@ ${escapeHtml(node.text || "")}
1242
1501
  for (const row of node.children) {
1243
1502
  bodyHtml += ` <tr>
1244
1503
  `;
1245
- if (row.children) {
1246
- for (let colIdx = 0; colIdx < row.children.length; colIdx++) {
1247
- const cell = row.children[colIdx];
1248
- const tag = row.isHeader ? "th" : "td";
1249
- const align = ((_a = node.align) == null ? void 0 : _a[colIdx]) ? ` style="text-align: ${node.align[colIdx]}"` : "";
1250
- const cellInner = await renderInlinesToHtml(cell.inlines, baseDir);
1251
- bodyHtml += ` <${tag}${align}>${cellInner}</${tag}>
1504
+ for (const cell of row.children || []) {
1505
+ const tag = cell.isHeader ? "th" : "td";
1506
+ const align = cell.align ? ` align="${cell.align}"` : "";
1507
+ const inner = await renderInlinesToHtml(cell.inlines, baseDir);
1508
+ bodyHtml += ` <${tag}${align}>${inner}</${tag}>
1252
1509
  `;
1253
- }
1254
1510
  }
1255
1511
  bodyHtml += ` </tr>
1256
1512
  `;
@@ -1264,57 +1520,52 @@ ${escapeHtml(node.text || "")}
1264
1520
  bodyHtml += ` <${tag}>
1265
1521
  `;
1266
1522
  for (const item of node.children) {
1267
- const itemInner = await renderInlinesToHtml(item.inlines, baseDir);
1268
- let prefix = "";
1269
- if (item.checked !== void 0) {
1270
- prefix = `<input type="checkbox" disabled ${item.checked ? "checked" : ""}/> `;
1271
- }
1272
- bodyHtml += ` <li>${prefix}${itemInner}</li>
1523
+ const inner = await renderInlinesToHtml(item.inlines, baseDir);
1524
+ bodyHtml += ` <li>${inner}</li>
1273
1525
  `;
1274
1526
  }
1275
1527
  bodyHtml += ` </${tag}>
1276
1528
  `;
1277
1529
  continue;
1278
1530
  }
1279
- if (node.type === "htmlBlock") {
1280
- bodyHtml += ` ${node.rawHtml}
1531
+ if (node.type === "thematicBreak") {
1532
+ bodyHtml += ` <hr />
1281
1533
  `;
1282
1534
  continue;
1283
1535
  }
1284
- if (node.type === "thematicBreak") {
1285
- bodyHtml += ` <hr />
1536
+ if (node.type === "htmlBlock") {
1537
+ bodyHtml += ` ${node.text}
1286
1538
  `;
1287
1539
  continue;
1288
1540
  }
1289
1541
  }
1290
- const wmConfig = config.watermark ?? metadata.watermark;
1291
- let watermarkHtml = "";
1292
1542
  let watermarkCss = "";
1293
- if (wmConfig) {
1294
- const wmText = typeof wmConfig === "string" ? wmConfig : wmConfig.text;
1295
- const opacity = typeof wmConfig === "object" && wmConfig.opacity !== void 0 ? wmConfig.opacity : 0.12;
1296
- const rotate = typeof wmConfig === "object" && wmConfig.rotate !== void 0 ? wmConfig.rotate : -45;
1297
- const color = typeof wmConfig === "object" && wmConfig.color ? wmConfig.color : "#94A3B8";
1298
- const fontSize = typeof wmConfig === "object" && wmConfig.fontSize ? `${wmConfig.fontSize}pt` : "52pt";
1543
+ let watermarkHtml = "";
1544
+ if (resolved.watermark) {
1545
+ const wm = resolved.watermark;
1299
1546
  watermarkCss = `
1300
1547
  .document-watermark {
1301
1548
  position: fixed;
1302
1549
  top: 50%;
1303
1550
  left: 50%;
1304
- transform: translate(-50%, -50%) rotate(${rotate}deg);
1305
- font-size: ${fontSize};
1306
- font-weight: 800;
1307
- color: ${color};
1308
- opacity: ${opacity};
1551
+ transform: translate(-50%, -50%) rotate(${wm.rotate}deg);
1552
+ font-size: ${wm.fontSize}pt;
1553
+ font-weight: 900;
1554
+ color: ${wm.color};
1555
+ opacity: ${wm.opacity};
1309
1556
  pointer-events: none;
1557
+ z-index: 0;
1310
1558
  user-select: none;
1311
- z-index: 9999;
1312
1559
  text-transform: uppercase;
1313
1560
  letter-spacing: 0.15em;
1314
1561
  white-space: nowrap;
1315
1562
  }
1563
+ .document-container {
1564
+ position: relative;
1565
+ z-index: 1;
1566
+ }
1316
1567
  `;
1317
- watermarkHtml = ` <div class="document-watermark">${escapeHtml(wmText)}</div>
1568
+ watermarkHtml = ` <div class="document-watermark">${escapeHtml(wm.text)}</div>
1318
1569
  `;
1319
1570
  }
1320
1571
  const hasMermaid = doc.nodes.some((n) => n.type === "mermaid");
@@ -1333,13 +1584,12 @@ ${escapeHtml(node.text || "")}
1333
1584
  }
1334
1585
  });
1335
1586
  </script>` : "";
1336
- const documentTitle = metadata.title || "MarkForge Document";
1337
1587
  return `<!DOCTYPE html>
1338
- <html lang="${metadata.lang || "en"}">
1588
+ <html lang="${resolved.lang}">
1339
1589
  <head>
1340
1590
  <meta charset="UTF-8">
1341
1591
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
1342
- <title>${escapeHtml(documentTitle)}</title>
1592
+ <title>${escapeHtml(resolved.title)}</title>
1343
1593
  <style>
1344
1594
  ${THEME_COMPONENTS}
1345
1595
  ${baseThemeCss}
@@ -1383,14 +1633,14 @@ function findChromeExecutable() {
1383
1633
  "/Applications/Chromium.app/Contents/MacOS/Chromium",
1384
1634
  "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
1385
1635
  "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
1636
+ // Windows — Microsoft Edge (Native Windows 10/11 browser, enterprise whitelist friendly)
1637
+ `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
1638
+ `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
1639
+ `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
1386
1640
  // Windows — Google Chrome
1387
1641
  `${winProgramFiles}\\Google\\Chrome\\Application\\chrome.exe`,
1388
1642
  `${winProgramFilesX86}\\Google\\Chrome\\Application\\chrome.exe`,
1389
1643
  `${winLocalAppData}\\Google\\Chrome\\Application\\chrome.exe`,
1390
- // Windows — Microsoft Edge (ships with Windows 10/11)
1391
- `${winProgramFiles}\\Microsoft\\Edge\\Application\\msedge.exe`,
1392
- `${winProgramFilesX86}\\Microsoft\\Edge\\Application\\msedge.exe`,
1393
- `${winLocalAppData}\\Microsoft\\Edge\\Application\\msedge.exe`,
1394
1644
  // Windows — Brave Browser
1395
1645
  `${winProgramFiles}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
1396
1646
  `${winProgramFilesX86}\\BraveSoftware\\Brave-Browser\\Application\\brave.exe`,
@@ -1421,22 +1671,50 @@ function findChromeExecutable() {
1421
1671
  return null;
1422
1672
  }
1423
1673
  function injectPagedMediaStyles(html, config, metadata) {
1424
- var _a, _b, _c, _d;
1425
- const merged = { ...config.metadata, ...metadata };
1426
- const orientation = merged.orientation || config.orientation || "portrait";
1427
- const size = merged.paperSize || config.paperSize || "A4";
1428
- const margins = merged.margins || config.margins || {};
1429
- const top = margins.top || ((_a = config.margins) == null ? void 0 : _a.top) || "2.5cm";
1430
- const bottom = margins.bottom || ((_b = config.margins) == null ? void 0 : _b.bottom) || "2.5cm";
1431
- const left = margins.left || ((_c = config.margins) == null ? void 0 : _c.left) || "2.5cm";
1432
- const right = margins.right || ((_d = config.margins) == null ? void 0 : _d.right) || "2.5cm";
1433
- const headerCfg = merged.header;
1434
- const footerCfg = merged.footer;
1435
- const headerLeft = (headerCfg == null ? void 0 : headerCfg.left) ?? "";
1436
- const headerCenter = (headerCfg == null ? void 0 : headerCfg.center) ?? "";
1437
- const headerRight = (headerCfg == null ? void 0 : headerCfg.right) ?? "";
1438
- const footerLeft = ((footerCfg == null ? void 0 : footerCfg.left) ?? "").replace("{page}", "").replace("{pages}", "").trim();
1439
- const esc = (s) => s.replace(/"/g, '"').replace(/\\/g, "\\\\");
1674
+ var _a, _b, _c, _d, _e, _f;
1675
+ const resolved = resolveDocumentConfig(metadata || {}, config);
1676
+ const size = resolved.paperSize;
1677
+ const orientation = resolved.orientation;
1678
+ const top = resolved.margins.top;
1679
+ const bottom = resolved.margins.bottom;
1680
+ const left = resolved.margins.left;
1681
+ const right = resolved.margins.right;
1682
+ const esc = (s) => s.replace(/"/g, '\\"').replace(/\\/g, "\\\\");
1683
+ const buildZoneCss = (pos, zone, isPageCounter = false) => {
1684
+ if (!zone && !isPageCounter) return "";
1685
+ const color = (zone == null ? void 0 : zone.color) || "#94a3b8";
1686
+ const fontSize = (zone == null ? void 0 : zone.fontSize) ? `${zone.fontSize}pt` : "9pt";
1687
+ const fontFamily = (zone == null ? void 0 : zone.fontFamily) ? `font-family: ${zone.fontFamily};` : "";
1688
+ const fontWeight = (zone == null ? void 0 : zone.bold) ? "font-weight: bold;" : "";
1689
+ const fontStyle = (zone == null ? void 0 : zone.italic) ? "font-style: italic;" : "";
1690
+ let content = "";
1691
+ if (isPageCounter) {
1692
+ if ((zone == null ? void 0 : zone.text) && (zone.text.includes("{page}") || zone.text.includes("{pages}"))) {
1693
+ const parts = zone.text.split(/(\{page\}|\{pages\})/gi);
1694
+ const cssParts = parts.map((part) => {
1695
+ if (part.toLowerCase() === "{page}") return "counter(page)";
1696
+ if (part.toLowerCase() === "{pages}") return "counter(pages)";
1697
+ return `"${esc(part)}"`;
1698
+ });
1699
+ content = cssParts.join(" ");
1700
+ } else if (zone == null ? void 0 : zone.text) {
1701
+ content = `"${esc(zone.text)}"`;
1702
+ } else {
1703
+ content = `"Page " counter(page) " of " counter(pages)`;
1704
+ }
1705
+ } else if (zone == null ? void 0 : zone.text) {
1706
+ content = `"${esc(zone.text)}"`;
1707
+ }
1708
+ if (!content) return "";
1709
+ return `@${pos} {
1710
+ content: ${content};
1711
+ font-size: ${fontSize};
1712
+ color: ${color};
1713
+ ${fontFamily}
1714
+ ${fontWeight}
1715
+ ${fontStyle}
1716
+ }`;
1717
+ };
1440
1718
  const pagedCss = `
1441
1719
  @page {
1442
1720
  size: ${size} ${orientation};
@@ -1444,15 +1722,12 @@ function injectPagedMediaStyles(html, config, metadata) {
1444
1722
  margin-bottom: ${bottom};
1445
1723
  margin-left: ${left};
1446
1724
  margin-right: ${right};
1447
- ${headerLeft ? `@top-left { content: "${esc(headerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1448
- ${headerCenter ? `@top-center { content: "${esc(headerCenter)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1449
- ${headerRight ? `@top-right { content: "${esc(headerRight)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1450
- ${footerLeft ? `@bottom-left { content: "${esc(footerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1451
- @bottom-right {
1452
- content: "Page " counter(page) " of " counter(pages);
1453
- font-size: 9pt;
1454
- color: #94a3b8;
1455
- }
1725
+ ${buildZoneCss("top-left", (_a = resolved.header) == null ? void 0 : _a.left)}
1726
+ ${buildZoneCss("top-center", (_b = resolved.header) == null ? void 0 : _b.center)}
1727
+ ${buildZoneCss("top-right", (_c = resolved.header) == null ? void 0 : _c.right)}
1728
+ ${buildZoneCss("bottom-left", (_d = resolved.footer) == null ? void 0 : _d.left)}
1729
+ ${buildZoneCss("bottom-center", (_e = resolved.footer) == null ? void 0 : _e.center)}
1730
+ ${buildZoneCss("bottom-right", (_f = resolved.footer) == null ? void 0 : _f.right, true)}
1456
1731
  }
1457
1732
  @media print {
1458
1733
  body { padding: 0; }
@@ -1511,6 +1786,33 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1511
1786
  const tmpDir = os.tmpdir();
1512
1787
  const tmpHtml = path3.join(tmpDir, `markforge_${tmpId}.html`);
1513
1788
  const tmpPdf = path3.join(tmpDir, `markforge_${tmpId}.pdf`);
1789
+ const tmpProfile = path3.join(tmpDir, `markforge_prof_${tmpId}`);
1790
+ const isolatedFlags = [
1791
+ `--user-data-dir=${tmpProfile}`,
1792
+ "--no-first-run",
1793
+ "--no-default-browser-check",
1794
+ "--disable-sync",
1795
+ "--disable-background-networking",
1796
+ "--disable-component-update",
1797
+ "--disable-default-apps",
1798
+ "--disable-extensions",
1799
+ "--disable-domain-reliability",
1800
+ "--disable-client-side-phishing-detection",
1801
+ "--disable-breakpad",
1802
+ "--disable-component-extensions-with-background-pages",
1803
+ "--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
1804
+ "--password-store=basic",
1805
+ "--use-mock-keychain",
1806
+ "--mute-audio",
1807
+ "--no-service-autorun",
1808
+ "--disable-gpu",
1809
+ "--no-sandbox",
1810
+ "--disable-setuid-sandbox",
1811
+ "--allow-file-access-from-files",
1812
+ "--disable-web-security",
1813
+ "--force-color-profile=srgb",
1814
+ "--no-pdf-header-footer"
1815
+ ];
1514
1816
  try {
1515
1817
  fs3.writeFileSync(tmpHtml, pagedHtml, "utf-8");
1516
1818
  const fileUrl = pathToFileURL(tmpHtml).href;
@@ -1518,15 +1820,9 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1518
1820
  chromePath,
1519
1821
  [
1520
1822
  "--headless=new",
1521
- "--disable-gpu",
1522
- "--no-sandbox",
1523
- "--disable-setuid-sandbox",
1524
- "--allow-file-access-from-files",
1525
- "--disable-web-security",
1526
- "--force-color-profile=srgb",
1823
+ ...isolatedFlags,
1527
1824
  "--run-all-compositor-stages-before-draw",
1528
1825
  "--virtual-time-budget=8000",
1529
- "--no-pdf-header-footer",
1530
1826
  `--print-to-pdf=${tmpPdf}`,
1531
1827
  fileUrl
1532
1828
  ],
@@ -1537,13 +1833,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1537
1833
  chromePath,
1538
1834
  [
1539
1835
  "--headless",
1540
- "--disable-gpu",
1541
- "--no-sandbox",
1542
- "--disable-setuid-sandbox",
1543
- "--allow-file-access-from-files",
1544
- "--disable-web-security",
1545
- "--force-color-profile=srgb",
1546
- "--no-pdf-header-footer",
1836
+ ...isolatedFlags,
1547
1837
  `--print-to-pdf=${tmpPdf}`,
1548
1838
  fileUrl
1549
1839
  ],
@@ -1559,6 +1849,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1559
1849
  try {
1560
1850
  if (fs3.existsSync(tmpHtml)) fs3.unlinkSync(tmpHtml);
1561
1851
  if (fs3.existsSync(tmpPdf)) fs3.unlinkSync(tmpPdf);
1852
+ if (fs3.existsSync(tmpProfile)) fs3.rmSync(tmpProfile, { recursive: true, force: true });
1562
1853
  } catch {
1563
1854
  }
1564
1855
  }
@@ -1649,30 +1940,7 @@ ${mermaidCode}
1649
1940
  }
1650
1941
 
1651
1942
  // src/core/docx/docxBuilder.ts
1652
- function parseMarginToTwip(margin, defaultTwip = 1440) {
1653
- if (typeof margin === "number") return margin;
1654
- if (!margin) return defaultTwip;
1655
- const str = margin.trim().toLowerCase();
1656
- if (str.endsWith("cm")) {
1657
- const cm = parseFloat(str);
1658
- return Math.round(convertMillimetersToTwip(cm * 10));
1659
- }
1660
- if (str.endsWith("mm")) {
1661
- const mm = parseFloat(str);
1662
- return Math.round(convertMillimetersToTwip(mm));
1663
- }
1664
- if (str.endsWith("in") || str.endsWith("inch")) {
1665
- const inch = parseFloat(str);
1666
- return Math.round(convertInchesToTwip(inch));
1667
- }
1668
- if (str.endsWith("pt")) {
1669
- const pt = parseFloat(str);
1670
- return Math.round(pt * 20);
1671
- }
1672
- const val = parseFloat(str);
1673
- return isNaN(val) ? defaultTwip : Math.round(val);
1674
- }
1675
- async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1943
+ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), options = {}) {
1676
1944
  const runs = [];
1677
1945
  for (const span of spans) {
1678
1946
  if (span.type === "image" && span.url) {
@@ -1702,7 +1970,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1702
1970
  new TextRun({
1703
1971
  text: span.content,
1704
1972
  style: "Hyperlink",
1705
- color: "0969DA",
1973
+ color: "009DA0",
1974
+ font: options.font,
1975
+ size: options.size,
1976
+ bold: options.bold,
1706
1977
  underline: {}
1707
1978
  })
1708
1979
  ],
@@ -1715,7 +1986,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1715
1986
  runs.push(
1716
1987
  new TextRun({
1717
1988
  text: span.content,
1718
- bold: true
1989
+ bold: true,
1990
+ font: options.font,
1991
+ size: options.size,
1992
+ color: options.color,
1993
+ italics: options.italics
1719
1994
  })
1720
1995
  );
1721
1996
  continue;
@@ -1724,7 +1999,11 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1724
1999
  runs.push(
1725
2000
  new TextRun({
1726
2001
  text: span.content,
1727
- italics: true
2002
+ italics: true,
2003
+ font: options.font,
2004
+ size: options.size,
2005
+ color: options.color,
2006
+ bold: options.bold
1728
2007
  })
1729
2008
  );
1730
2009
  continue;
@@ -1733,7 +2012,10 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1733
2012
  runs.push(
1734
2013
  new TextRun({
1735
2014
  text: span.content,
1736
- strike: true
2015
+ strike: true,
2016
+ font: options.font,
2017
+ size: options.size,
2018
+ color: options.color
1737
2019
  })
1738
2020
  );
1739
2021
  continue;
@@ -1741,22 +2023,23 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1741
2023
  if (span.type === "code") {
1742
2024
  runs.push(
1743
2025
  new TextRun({
1744
- text: ` ${span.content} `,
2026
+ text: span.content,
1745
2027
  font: "Consolas",
2028
+ size: options.size ? options.size - 2 : 19,
2029
+ color: "0F172A",
1746
2030
  shading: {
1747
2031
  type: ShadingType.CLEAR,
1748
- fill: "F1F5F9",
1749
- color: "0F172A"
2032
+ fill: "F1F5F9"
1750
2033
  }
1751
2034
  })
1752
2035
  );
1753
2036
  continue;
1754
2037
  }
1755
2038
  if (span.type === "htmlInline") {
1756
- let colorHex;
2039
+ let colorHex = options.color;
1757
2040
  let bgHex;
1758
- let isBold = false;
1759
- let isItalic = false;
2041
+ let isBold = !!options.bold;
2042
+ let isItalic = !!options.italics;
1760
2043
  if (span.style) {
1761
2044
  if (span.style.color) {
1762
2045
  colorHex = span.style.color.replace("#", "").trim();
@@ -1772,13 +2055,9 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1772
2055
  }
1773
2056
  }
1774
2057
  if (span.children && span.children.length > 0) {
1775
- const childRuns = await convertInlinesToTextRuns(span.children, baseDir);
2058
+ const childRuns = await convertInlinesToTextRuns(span.children, baseDir, options);
1776
2059
  for (const child of childRuns) {
1777
- if (child instanceof TextRun) {
1778
- runs.push(child);
1779
- } else {
1780
- runs.push(child);
1781
- }
2060
+ runs.push(child);
1782
2061
  }
1783
2062
  continue;
1784
2063
  }
@@ -1788,6 +2067,8 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1788
2067
  color: colorHex,
1789
2068
  bold: isBold,
1790
2069
  italics: isItalic,
2070
+ font: options.font,
2071
+ size: options.size,
1791
2072
  shading: bgHex ? { type: ShadingType.CLEAR, fill: bgHex } : void 0
1792
2073
  })
1793
2074
  );
@@ -1795,91 +2076,239 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1795
2076
  }
1796
2077
  runs.push(
1797
2078
  new TextRun({
1798
- text: span.content
2079
+ text: span.content,
2080
+ font: options.font,
2081
+ size: options.size,
2082
+ color: options.color,
2083
+ bold: options.bold,
2084
+ italics: options.italics
1799
2085
  })
1800
2086
  );
1801
2087
  }
1802
2088
  return runs;
1803
2089
  }
1804
2090
  async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1805
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1806
- const metadata = { ...config.metadata, ...doc.metadata };
2091
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2092
+ const resolved = resolveDocumentConfig(doc.metadata, config);
1807
2093
  const docElements = [];
1808
- if (metadata.title) {
2094
+ const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
2095
+ const primaryHex = (themeProps.primaryColor || "#33CDCF").replace("#", "");
2096
+ const primaryDarkHex = (themeProps.primaryDark || "#009DA0").replace("#", "");
2097
+ const textHex = (themeProps.textColor || "#0F172A").replace("#", "");
2098
+ const textMutedHex = (themeProps.textMuted || "#64748B").replace("#", "");
2099
+ const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
2100
+ const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
2101
+ const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
2102
+ if (resolved.title) {
1809
2103
  docElements.push(
1810
2104
  new Paragraph({
1811
- text: metadata.title,
1812
- heading: HeadingLevel.TITLE,
1813
- spacing: { before: 200, after: 120 }
2105
+ children: [
2106
+ new TextRun({
2107
+ text: resolved.title,
2108
+ bold: true,
2109
+ size: 44,
2110
+ // 22pt
2111
+ color: textHex,
2112
+ font: defaultFont
2113
+ })
2114
+ ],
2115
+ spacing: { before: 120, after: 80 }
1814
2116
  })
1815
2117
  );
1816
- if (metadata.subtitle) {
2118
+ if (resolved.subtitle) {
1817
2119
  docElements.push(
1818
2120
  new Paragraph({
1819
2121
  children: [
1820
2122
  new TextRun({
1821
- text: metadata.subtitle,
1822
- italics: true,
1823
- color: "64748B",
1824
- size: 24
2123
+ text: resolved.subtitle,
2124
+ color: textMutedHex,
2125
+ size: 24,
1825
2126
  // 12pt
2127
+ font: defaultFont
1826
2128
  })
1827
2129
  ],
1828
- spacing: { after: 180 }
2130
+ spacing: { before: 60, after: 120 }
1829
2131
  })
1830
2132
  );
1831
2133
  }
1832
- if (metadata.author || metadata.date) {
2134
+ if (resolved.author || resolved.date || resolved.version || resolved.company) {
1833
2135
  const metaParts = [];
1834
- if (metadata.author) metaParts.push(`Author: ${Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author}`);
1835
- if (metadata.date) metaParts.push(`Date: ${metadata.date}`);
2136
+ if (resolved.author) metaParts.push(`Author: ${resolved.author}`);
2137
+ if (resolved.version) metaParts.push(`Version: ${resolved.version}`);
2138
+ if (resolved.date) metaParts.push(`Date: ${resolved.date}`);
1836
2139
  docElements.push(
1837
2140
  new Paragraph({
1838
2141
  children: [
1839
2142
  new TextRun({
1840
- text: metaParts.join(" | "),
1841
- color: "94A3B8",
1842
- size: 20
1843
- // 10pt
2143
+ text: metaParts.join(" "),
2144
+ color: textMutedHex,
2145
+ size: 18,
2146
+ // 9pt
2147
+ font: defaultFont
1844
2148
  })
1845
2149
  ],
1846
- spacing: { after: 360 },
2150
+ spacing: { before: 40, after: 240 },
1847
2151
  border: {
1848
2152
  bottom: {
1849
- color: "E2E8F0",
1850
- space: 10,
2153
+ color: borderHex,
2154
+ space: 12,
1851
2155
  style: BorderStyle.SINGLE,
1852
- size: 6
2156
+ size: 4
1853
2157
  }
1854
2158
  }
1855
2159
  })
1856
2160
  );
1857
2161
  }
1858
2162
  }
1859
- for (const node of doc.nodes) {
1860
- if (node.type === "heading") {
1861
- let headingLevel = HeadingLevel.HEADING_1;
1862
- if (node.level === 2) headingLevel = HeadingLevel.HEADING_2;
1863
- if (node.level === 3) headingLevel = HeadingLevel.HEADING_3;
1864
- if (node.level === 4) headingLevel = HeadingLevel.HEADING_4;
1865
- if (node.level === 5) headingLevel = HeadingLevel.HEADING_5;
1866
- if (node.level === 6) headingLevel = HeadingLevel.HEADING_6;
1867
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1868
- docElements.push(
2163
+ if (resolved.toc) {
2164
+ const headingNodes = doc.nodes.filter(
2165
+ (n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
2166
+ );
2167
+ if (headingNodes.length > 0) {
2168
+ const tocParagraphs = [
1869
2169
  new Paragraph({
1870
- heading: headingLevel,
1871
- children: runs,
1872
- spacing: { before: 240, after: 120 }
2170
+ children: [
2171
+ new TextRun({
2172
+ text: "TABLE OF CONTENTS",
2173
+ bold: true,
2174
+ size: 18,
2175
+ // 9pt
2176
+ color: textMutedHex,
2177
+ font: defaultFont
2178
+ })
2179
+ ],
2180
+ spacing: { after: 120 },
2181
+ border: {
2182
+ bottom: {
2183
+ color: borderHex,
2184
+ space: 6,
2185
+ style: BorderStyle.SINGLE,
2186
+ size: 4
2187
+ }
2188
+ }
1873
2189
  })
1874
- );
2190
+ ];
2191
+ for (const h of headingNodes) {
2192
+ const indentLeft = (h.level - 1) * 240;
2193
+ tocParagraphs.push(
2194
+ new Paragraph({
2195
+ indent: { left: indentLeft },
2196
+ children: [
2197
+ new TextRun({
2198
+ text: h.text,
2199
+ bold: h.level === 1,
2200
+ color: primaryDarkHex,
2201
+ size: h.level === 1 ? 21 : 20,
2202
+ font: defaultFont
2203
+ })
2204
+ ],
2205
+ spacing: { before: 20, after: 20 }
2206
+ })
2207
+ );
2208
+ }
2209
+ const tocCard = new Table({
2210
+ width: { size: 100, type: WidthType.PERCENTAGE },
2211
+ columnWidths: [9e3],
2212
+ rows: [
2213
+ new TableRow({
2214
+ children: [
2215
+ new TableCell({
2216
+ width: { size: 9e3, type: WidthType.DXA },
2217
+ shading: { fill: cardBgHex, type: ShadingType.CLEAR },
2218
+ margins: { top: 140, bottom: 140, left: 180, right: 180 },
2219
+ borders: {
2220
+ top: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
2221
+ bottom: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
2222
+ left: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
2223
+ right: { style: BorderStyle.SINGLE, size: 4, color: borderHex }
2224
+ },
2225
+ children: tocParagraphs
2226
+ })
2227
+ ]
2228
+ })
2229
+ ]
2230
+ });
2231
+ docElements.push(tocCard);
2232
+ docElements.push(new Paragraph({ spacing: { after: 200 } }));
2233
+ }
2234
+ }
2235
+ for (const node of doc.nodes) {
2236
+ if (node.type === "heading") {
2237
+ if (node.level === 1) {
2238
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2239
+ font: defaultFont,
2240
+ size: 34,
2241
+ // 17pt
2242
+ color: textHex,
2243
+ bold: true
2244
+ });
2245
+ docElements.push(
2246
+ new Paragraph({
2247
+ heading: HeadingLevel.HEADING_1,
2248
+ children: runs,
2249
+ spacing: { before: 360, after: 140 },
2250
+ border: {
2251
+ bottom: {
2252
+ color: primaryHex,
2253
+ space: 6,
2254
+ style: BorderStyle.SINGLE,
2255
+ size: 16
2256
+ }
2257
+ }
2258
+ })
2259
+ );
2260
+ } else if (node.level === 2) {
2261
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2262
+ font: defaultFont,
2263
+ size: 26,
2264
+ // 13pt
2265
+ color: primaryDarkHex,
2266
+ bold: true
2267
+ });
2268
+ docElements.push(
2269
+ new Paragraph({
2270
+ heading: HeadingLevel.HEADING_2,
2271
+ children: runs,
2272
+ spacing: { before: 280, after: 100 },
2273
+ border: {
2274
+ bottom: {
2275
+ color: borderHex,
2276
+ space: 4,
2277
+ style: BorderStyle.SINGLE,
2278
+ size: 4
2279
+ }
2280
+ }
2281
+ })
2282
+ );
2283
+ } else {
2284
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2285
+ font: defaultFont,
2286
+ size: 22,
2287
+ // 11pt
2288
+ color: textHex,
2289
+ bold: true
2290
+ });
2291
+ docElements.push(
2292
+ new Paragraph({
2293
+ heading: node.level === 3 ? HeadingLevel.HEADING_3 : HeadingLevel.HEADING_4,
2294
+ children: runs,
2295
+ spacing: { before: 200, after: 80 }
2296
+ })
2297
+ );
2298
+ }
1875
2299
  continue;
1876
2300
  }
1877
2301
  if (node.type === "paragraph") {
1878
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2302
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2303
+ font: defaultFont,
2304
+ size: 22,
2305
+ // 11pt
2306
+ color: "334155"
2307
+ });
1879
2308
  docElements.push(
1880
2309
  new Paragraph({
1881
2310
  children: runs,
1882
- spacing: { before: 60, after: 140 }
2311
+ spacing: { before: 40, after: 140, line: 280 }
1883
2312
  })
1884
2313
  );
1885
2314
  continue;
@@ -1951,7 +2380,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1951
2380
  bgFill = "F5F3FF";
1952
2381
  title = "IMPORTANT";
1953
2382
  }
1954
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2383
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2384
+ font: defaultFont,
2385
+ size: 21,
2386
+ color: "334155"
2387
+ });
1955
2388
  const calloutTable = new Table({
1956
2389
  width: { size: 100, type: WidthType.PERCENTAGE },
1957
2390
  columnWidths: [9e3],
@@ -1966,7 +2399,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1966
2399
  top: { style: BorderStyle.NONE },
1967
2400
  bottom: { style: BorderStyle.NONE },
1968
2401
  right: { style: BorderStyle.NONE },
1969
- left: { style: BorderStyle.SINGLE, size: 16, color: borderColor }
2402
+ left: { style: BorderStyle.SINGLE, size: 20, color: borderColor }
1970
2403
  },
1971
2404
  children: [
1972
2405
  new Paragraph({
@@ -1975,13 +2408,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1975
2408
  text: `[${title}]`,
1976
2409
  bold: true,
1977
2410
  color: borderColor,
2411
+ font: defaultFont,
1978
2412
  size: 20
1979
2413
  })
1980
2414
  ],
1981
- spacing: { after: 60 }
2415
+ spacing: { after: 40 }
1982
2416
  }),
1983
2417
  new Paragraph({
1984
- children: runs
2418
+ children: runs,
2419
+ spacing: { line: 270 }
1985
2420
  })
1986
2421
  ]
1987
2422
  })
@@ -1999,17 +2434,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1999
2434
  if (lines.length > 0) {
2000
2435
  for (const lineText of lines) {
2001
2436
  const spans = parseInlineSpans(lineText.replace(/^>\s?/, "").trim());
2002
- const lineRuns = await convertInlinesToTextRuns(spans, baseDir);
2437
+ const lineRuns = await convertInlinesToTextRuns(spans, baseDir, {
2438
+ font: defaultFont,
2439
+ size: 21,
2440
+ color: "475569",
2441
+ italics: true
2442
+ });
2003
2443
  quoteParagraphs.push(
2004
2444
  new Paragraph({
2005
2445
  children: lineRuns,
2006
- spacing: { before: 40, after: 40 }
2446
+ spacing: { before: 20, after: 20, line: 260 }
2007
2447
  })
2008
2448
  );
2009
2449
  }
2010
2450
  } else {
2011
- const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
2012
- quoteParagraphs.push(new Paragraph({ children: runs }));
2451
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir, {
2452
+ font: defaultFont,
2453
+ size: 21,
2454
+ color: "475569",
2455
+ italics: true
2456
+ });
2457
+ quoteParagraphs.push(new Paragraph({ children: runs, spacing: { line: 260 } }));
2013
2458
  }
2014
2459
  const quoteTable = new Table({
2015
2460
  width: { size: 100, type: WidthType.PERCENTAGE },
@@ -2025,7 +2470,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2025
2470
  top: { style: BorderStyle.NONE },
2026
2471
  bottom: { style: BorderStyle.NONE },
2027
2472
  right: { style: BorderStyle.NONE },
2028
- left: { style: BorderStyle.SINGLE, size: 12, color: "33CDCF" }
2473
+ left: { style: BorderStyle.SINGLE, size: 16, color: primaryHex }
2029
2474
  },
2030
2475
  children: quoteParagraphs
2031
2476
  })
@@ -2060,7 +2505,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2060
2505
  docElements.push(
2061
2506
  new Paragraph({
2062
2507
  children: [
2063
- new TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: "33CDCF" })
2508
+ new TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: primaryHex })
2064
2509
  ],
2065
2510
  spacing: { before: 60, after: 60 }
2066
2511
  })
@@ -2072,8 +2517,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2072
2517
  const tableRows = [];
2073
2518
  const numCols = ((_b = (_a = node.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) || 1;
2074
2519
  const colWidth = Math.floor(9e3 / numCols);
2075
- for (const rowNode of node.children) {
2520
+ for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
2521
+ const rowNode = node.children[rowIdx];
2076
2522
  const cells = [];
2523
+ const isHeader = rowNode.isHeader;
2524
+ const rowBg = isHeader ? "F1F5F9" : rowIdx % 2 === 0 ? "FFFFFF" : "F8FAFC";
2077
2525
  if (rowNode.children) {
2078
2526
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
2079
2527
  const cellNode = rowNode.children[colIdx];
@@ -2081,22 +2529,27 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2081
2529
  let alignment = AlignmentType.LEFT;
2082
2530
  if (align === "center") alignment = AlignmentType.CENTER;
2083
2531
  if (align === "right") alignment = AlignmentType.RIGHT;
2084
- const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir);
2532
+ const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir, {
2533
+ font: defaultFont,
2534
+ size: 20,
2535
+ color: isHeader ? "0F172A" : "334155",
2536
+ bold: isHeader
2537
+ });
2085
2538
  cells.push(
2086
2539
  new TableCell({
2087
2540
  width: { size: colWidth, type: WidthType.DXA },
2088
- shading: rowNode.isHeader ? { fill: "F1F5F9", type: ShadingType.CLEAR } : void 0,
2089
- margins: { top: 100, bottom: 100, left: 120, right: 120 },
2541
+ shading: { fill: rowBg, type: ShadingType.CLEAR },
2542
+ margins: { top: 100, bottom: 100, left: 140, right: 140 },
2090
2543
  borders: {
2091
- top: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
2092
- bottom: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
2093
- left: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
2094
- right: { style: BorderStyle.SINGLE, size: 4, color: "CBD5E1" }
2544
+ top: { style: BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2545
+ bottom: { style: BorderStyle.SINGLE, size: isHeader ? 8 : 4, color: isHeader ? "CBD5E1" : "E2E8F0" },
2546
+ left: { style: BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2547
+ right: { style: BorderStyle.SINGLE, size: 4, color: "E2E8F0" }
2095
2548
  },
2096
2549
  children: [
2097
2550
  new Paragraph({
2098
2551
  alignment,
2099
- children: rowNode.isHeader ? runs.map((r) => r instanceof TextRun ? new TextRun({ ...r, bold: true, color: "0F172A" }) : r) : runs
2552
+ children: runs
2100
2553
  })
2101
2554
  ]
2102
2555
  })
@@ -2105,7 +2558,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2105
2558
  }
2106
2559
  tableRows.push(
2107
2560
  new TableRow({
2108
- tableHeader: rowNode.isHeader,
2561
+ tableHeader: isHeader,
2109
2562
  children: cells
2110
2563
  })
2111
2564
  );
@@ -2121,7 +2574,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2121
2574
  }
2122
2575
  if (node.type === "list" && node.children) {
2123
2576
  for (const item of node.children) {
2124
- const runs = await convertInlinesToTextRuns(item.inlines, baseDir);
2577
+ const runs = await convertInlinesToTextRuns(item.inlines, baseDir, {
2578
+ font: defaultFont,
2579
+ size: 21,
2580
+ color: "334155"
2581
+ });
2125
2582
  let prefix = "";
2126
2583
  if (item.checked !== void 0) {
2127
2584
  prefix = item.checked ? "[X] " : "[ ] ";
@@ -2133,7 +2590,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2133
2590
  ...prefix ? [new TextRun({ text: prefix, bold: true, font: "Consolas" })] : [],
2134
2591
  ...runs
2135
2592
  ],
2136
- spacing: { before: 40, after: 40 }
2593
+ spacing: { before: 20, after: 20, line: 260 }
2137
2594
  })
2138
2595
  );
2139
2596
  }
@@ -2208,88 +2665,205 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2208
2665
  continue;
2209
2666
  }
2210
2667
  }
2211
- const headerObj = metadata.header || config.header;
2212
- const footerObj = metadata.footer || config.footer;
2213
- const headerTabStops = [
2214
- { type: TabStopType.CENTER, position: 4513 },
2215
- { type: TabStopType.RIGHT, position: 9026 }
2216
- ];
2217
- const docHeader = headerObj ? new Header2({
2668
+ const contentWidthTwip = Math.max(
2669
+ 1e3,
2670
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
2671
+ );
2672
+ const centerPos = Math.round(contentWidthTwip / 2);
2673
+ const rightPos = contentWidthTwip;
2674
+ const headerRuns = [];
2675
+ if ((_d = resolved.header) == null ? void 0 : _d.left) {
2676
+ headerRuns.push(
2677
+ new TextRun({
2678
+ text: resolved.header.left.text,
2679
+ color: resolved.header.left.color.replace("#", ""),
2680
+ size: (resolved.header.left.fontSize || 9) * 2,
2681
+ font: resolved.header.left.fontFamily || defaultFont,
2682
+ bold: resolved.header.left.bold,
2683
+ italics: resolved.header.left.italic
2684
+ })
2685
+ );
2686
+ }
2687
+ headerRuns.push(new TextRun({ text: " " }));
2688
+ if ((_e = resolved.header) == null ? void 0 : _e.center) {
2689
+ headerRuns.push(
2690
+ new TextRun({
2691
+ text: resolved.header.center.text,
2692
+ color: resolved.header.center.color.replace("#", ""),
2693
+ size: (resolved.header.center.fontSize || 9) * 2,
2694
+ font: resolved.header.center.fontFamily || defaultFont,
2695
+ bold: resolved.header.center.bold,
2696
+ italics: resolved.header.center.italic
2697
+ })
2698
+ );
2699
+ }
2700
+ headerRuns.push(new TextRun({ text: " " }));
2701
+ if ((_f = resolved.header) == null ? void 0 : _f.right) {
2702
+ headerRuns.push(
2703
+ new TextRun({
2704
+ text: resolved.header.right.text,
2705
+ color: resolved.header.right.color.replace("#", ""),
2706
+ size: (resolved.header.right.fontSize || 9) * 2,
2707
+ font: resolved.header.right.fontFamily || defaultFont,
2708
+ bold: resolved.header.right.bold,
2709
+ italics: resolved.header.right.italic
2710
+ })
2711
+ );
2712
+ }
2713
+ const docHeader = resolved.header ? new Header2({
2218
2714
  children: [
2219
2715
  new Paragraph({
2220
- tabStops: headerTabStops,
2221
- children: [
2222
- // Left zone
2223
- ...headerObj.left ? [
2716
+ tabStops: [
2717
+ {
2718
+ type: TabStopType.CENTER,
2719
+ position: centerPos
2720
+ },
2721
+ {
2722
+ type: TabStopType.RIGHT,
2723
+ position: rightPos
2724
+ }
2725
+ ],
2726
+ border: resolved.header.divider ? {
2727
+ bottom: {
2728
+ style: BorderStyle.SINGLE,
2729
+ size: 4,
2730
+ space: 6,
2731
+ color: (resolved.header.dividerColor || "#CBD5E1").replace("#", "")
2732
+ }
2733
+ } : void 0,
2734
+ children: headerRuns,
2735
+ spacing: { after: 120 }
2736
+ })
2737
+ ]
2738
+ }) : void 0;
2739
+ const footerRuns = [];
2740
+ if ((_g = resolved.footer) == null ? void 0 : _g.left) {
2741
+ footerRuns.push(
2742
+ new TextRun({
2743
+ text: resolved.footer.left.text,
2744
+ color: resolved.footer.left.color.replace("#", ""),
2745
+ size: (resolved.footer.left.fontSize || 9) * 2,
2746
+ font: resolved.footer.left.fontFamily || defaultFont,
2747
+ bold: resolved.footer.left.bold,
2748
+ italics: resolved.footer.left.italic
2749
+ })
2750
+ );
2751
+ }
2752
+ footerRuns.push(new TextRun({ text: " " }));
2753
+ if ((_h = resolved.footer) == null ? void 0 : _h.center) {
2754
+ footerRuns.push(
2755
+ new TextRun({
2756
+ text: resolved.footer.center.text,
2757
+ color: resolved.footer.center.color.replace("#", ""),
2758
+ size: (resolved.footer.center.fontSize || 9) * 2,
2759
+ font: resolved.footer.center.fontFamily || defaultFont,
2760
+ bold: resolved.footer.center.bold,
2761
+ italics: resolved.footer.center.italic
2762
+ })
2763
+ );
2764
+ }
2765
+ footerRuns.push(new TextRun({ text: " " }));
2766
+ if ((_i = resolved.footer) == null ? void 0 : _i.right) {
2767
+ const rZone = resolved.footer.right;
2768
+ const rColor = rZone.color.replace("#", "");
2769
+ const rSize = (rZone.fontSize || 9) * 2;
2770
+ const rFont = rZone.fontFamily || defaultFont;
2771
+ const rBold = rZone.bold;
2772
+ const rItalics = rZone.italic;
2773
+ if (rZone.text.includes("{page}") || rZone.text.includes("{pages}")) {
2774
+ const parts = rZone.text.split(/(\{page\}|\{pages\})/gi);
2775
+ for (const part of parts) {
2776
+ if (part.toLowerCase() === "{page}") {
2777
+ footerRuns.push(
2224
2778
  new TextRun({
2225
- text: headerObj.left.replace("{title}", metadata.title || ""),
2226
- color: "94A3B8",
2227
- size: 18
2779
+ children: [PageNumber.CURRENT],
2780
+ color: rColor,
2781
+ size: rSize,
2782
+ font: rFont,
2783
+ bold: rBold,
2784
+ italics: rItalics
2228
2785
  })
2229
- ] : [],
2230
- // Center zone (tab + text)
2231
- ...headerObj.center ? [
2232
- new TextRun({ text: " ", color: "94A3B8", size: 18 }),
2786
+ );
2787
+ } else if (part.toLowerCase() === "{pages}") {
2788
+ footerRuns.push(
2233
2789
  new TextRun({
2234
- text: headerObj.center.replace("{title}", metadata.title || ""),
2235
- color: "94A3B8",
2236
- size: 18
2790
+ children: [PageNumber.TOTAL_PAGES],
2791
+ color: rColor,
2792
+ size: rSize,
2793
+ font: rFont,
2794
+ bold: rBold,
2795
+ italics: rItalics
2237
2796
  })
2238
- ] : [],
2239
- // Right zone (tab + text) — skip extra tab if center already used one
2240
- ...headerObj.right ? [
2241
- new TextRun({
2242
- text: headerObj.left || headerObj.center ? " " : "",
2243
- color: "94A3B8",
2244
- size: 18
2245
- }),
2797
+ );
2798
+ } else if (part) {
2799
+ footerRuns.push(
2246
2800
  new TextRun({
2247
- text: headerObj.right.replace("{title}", metadata.title || ""),
2248
- color: "94A3B8",
2249
- size: 18
2801
+ text: part,
2802
+ color: rColor,
2803
+ size: rSize,
2804
+ font: rFont,
2805
+ bold: rBold,
2806
+ italics: rItalics
2250
2807
  })
2251
- ] : []
2252
- ]
2253
- })
2254
- ]
2255
- }) : void 0;
2256
- const docFooter = footerObj ? new Footer({
2808
+ );
2809
+ }
2810
+ }
2811
+ } else {
2812
+ footerRuns.push(
2813
+ new TextRun({
2814
+ text: rZone.text,
2815
+ color: rColor,
2816
+ size: rSize,
2817
+ font: rFont,
2818
+ bold: rBold,
2819
+ italics: rItalics
2820
+ })
2821
+ );
2822
+ }
2823
+ }
2824
+ const docFooter = resolved.footer ? new Footer({
2257
2825
  children: [
2258
2826
  new Paragraph({
2259
- tabStops: headerTabStops,
2260
- children: [
2261
- // Left zone
2262
- ...footerObj.left ? [
2263
- new TextRun({
2264
- text: footerObj.left.replace("{page}", "").replace("{pages}", "").trim(),
2265
- color: "94A3B8",
2266
- size: 18
2267
- })
2268
- ] : [],
2269
- // Right zone: always includes page number if right is configured or footer exists
2270
- new TextRun({ text: " ", color: "94A3B8", size: 18 }),
2271
- new TextRun({ text: "Page ", color: "94A3B8", size: 18 }),
2272
- new TextRun({ children: [PageNumber.CURRENT], color: "94A3B8", size: 18 }),
2273
- new TextRun({ text: " of ", color: "94A3B8", size: 18 }),
2274
- new TextRun({ children: [PageNumber.TOTAL_PAGES], color: "94A3B8", size: 18 })
2275
- ]
2827
+ tabStops: [
2828
+ {
2829
+ type: TabStopType.CENTER,
2830
+ position: centerPos
2831
+ },
2832
+ {
2833
+ type: TabStopType.RIGHT,
2834
+ position: rightPos
2835
+ }
2836
+ ],
2837
+ border: resolved.footer.divider ? {
2838
+ top: {
2839
+ style: BorderStyle.SINGLE,
2840
+ size: 4,
2841
+ space: 6,
2842
+ color: (resolved.footer.dividerColor || "#CBD5E1").replace("#", "")
2843
+ }
2844
+ } : void 0,
2845
+ children: footerRuns,
2846
+ spacing: { before: 120 }
2276
2847
  })
2277
2848
  ]
2278
2849
  }) : void 0;
2279
- const topMargin = parseMarginToTwip(((_d = metadata.margins) == null ? void 0 : _d.top) || ((_e = config.margins) == null ? void 0 : _e.top), 1440);
2280
- const bottomMargin = parseMarginToTwip(((_f = metadata.margins) == null ? void 0 : _f.bottom) || ((_g = config.margins) == null ? void 0 : _g.bottom), 1440);
2281
- const leftMargin = parseMarginToTwip(((_h = metadata.margins) == null ? void 0 : _h.left) || ((_i = config.margins) == null ? void 0 : _i.left), 1440);
2282
- const rightMargin = parseMarginToTwip(((_j = metadata.margins) == null ? void 0 : _j.right) || ((_k = config.margins) == null ? void 0 : _k.right), 1440);
2283
- const isLandscape = (metadata.orientation || config.orientation) === "landscape";
2850
+ const isLandscape = resolved.orientation === "landscape";
2284
2851
  const document = new Document({
2285
2852
  styles: {
2286
2853
  default: {
2287
2854
  document: {
2288
2855
  run: {
2289
- font: "Segoe UI",
2290
- size: 22,
2291
- // 11pt
2292
- color: "0F172A"
2856
+ font: defaultFont,
2857
+ size: 21,
2858
+ // 10.5pt
2859
+ color: textHex
2860
+ },
2861
+ paragraph: {
2862
+ spacing: {
2863
+ line: 276,
2864
+ // 1.15 line spacing
2865
+ after: 140
2866
+ }
2293
2867
  }
2294
2868
  }
2295
2869
  }
@@ -2299,13 +2873,15 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2299
2873
  properties: {
2300
2874
  page: {
2301
2875
  size: {
2876
+ width: resolved.paperDimensions.widthTwip,
2877
+ height: resolved.paperDimensions.heightTwip,
2302
2878
  orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
2303
2879
  },
2304
2880
  margin: {
2305
- top: topMargin,
2306
- bottom: bottomMargin,
2307
- left: leftMargin,
2308
- right: rightMargin,
2881
+ top: resolved.margins.topTwip,
2882
+ bottom: resolved.margins.bottomTwip,
2883
+ left: resolved.margins.leftTwip,
2884
+ right: resolved.margins.rightTwip,
2309
2885
  header: 720,
2310
2886
  footer: 720
2311
2887
  }