@masumdev/markforge 0.3.0 → 0.4.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.
@@ -7,197 +7,9 @@ try {
7
7
  });
8
8
  }
9
9
  } catch {}
10
- import {
11
- MARKFORGE_VERSION
12
- } from "./chunk-HKB4CSPZ.mjs";
13
10
  import {
14
11
  DEFAULT_CONFIG
15
- } from "./chunk-IYGPJIK5.mjs";
16
- import "./chunk-YZHGBG4N.mjs";
17
-
18
- // src/ui/App.tsx
19
- import { useState, useEffect } from "react";
20
- import { Box as Box4 } from "ink";
21
-
22
- // src/ui/Header.tsx
23
- import * as path from "path";
24
- import { Box, Text } from "ink";
25
- import { jsx, jsxs } from "react/jsx-runtime";
26
- function formatDisplayPath(p) {
27
- if (!p) return "";
28
- try {
29
- const rel = path.relative(process.cwd(), p);
30
- return rel && !rel.startsWith("..") && rel.length < p.length ? `./${rel}` : p;
31
- } catch {
32
- return p;
33
- }
34
- }
35
- var Header = ({
36
- inputFile,
37
- theme = "corporate",
38
- targetFormats = ["docx", "pdf"]
39
- }) => {
40
- const themeLabel = typeof theme === "object" ? "Custom (ThemeProps)" : String(theme || "corporate");
41
- return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: /* @__PURE__ */ jsxs(
42
- Box,
43
- {
44
- borderStyle: "round",
45
- borderColor: "cyan",
46
- paddingX: 2,
47
- paddingY: 1,
48
- flexDirection: "column",
49
- children: [
50
- /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", alignItems: "center", children: [
51
- /* @__PURE__ */ jsxs(Box, { children: [
52
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "MARKFORGE" }),
53
- /* @__PURE__ */ jsx(Text, { color: "gray", children: " | Document Publishing Engine" })
54
- ] }),
55
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "black", backgroundColor: "cyan", bold: true, children: [
56
- " ",
57
- "v",
58
- MARKFORGE_VERSION,
59
- " "
60
- ] }) })
61
- ] }),
62
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
63
- inputFile && /* @__PURE__ */ jsxs(Box, { children: [
64
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "Source: " }),
65
- /* @__PURE__ */ jsx(Text, { color: "white", bold: true, children: formatDisplayPath(inputFile) })
66
- ] }),
67
- /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginTop: 0, children: [
68
- /* @__PURE__ */ jsxs(Box, { children: [
69
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "Theme: " }),
70
- /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: themeLabel })
71
- ] }),
72
- targetFormats && targetFormats.length > 0 && /* @__PURE__ */ jsxs(Box, { children: [
73
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "Targets: " }),
74
- /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: targetFormats.map((f) => f.toUpperCase()).join(", ") })
75
- ] })
76
- ] })
77
- ] }),
78
- /* @__PURE__ */ jsxs(Box, { marginTop: 1, justifyContent: "space-between", children: [
79
- /* @__PURE__ */ jsxs(Box, { children: [
80
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "Author: " }),
81
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "Ma'sum" }),
82
- /* @__PURE__ */ jsx(Text, { color: "gray", children: " (@masumrpg)" })
83
- ] }),
84
- /* @__PURE__ */ jsxs(Box, { children: [
85
- /* @__PURE__ */ jsx(Text, { color: "gray", children: "GitHub: " }),
86
- /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: "https://github.com/masumrpg" })
87
- ] })
88
- ] })
89
- ]
90
- }
91
- ) });
92
- };
93
-
94
- // src/ui/LiveProgress.tsx
95
- import { Box as Box2, Text as Text2 } from "ink";
96
- import Spinner from "ink-spinner";
97
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
98
- var LiveProgress = ({ status, isCompiling }) => {
99
- if (!isCompiling) return null;
100
- return /* @__PURE__ */ jsxs2(Box2, { marginY: 1, alignItems: "center", children: [
101
- /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
102
- /* @__PURE__ */ jsxs2(Box2, { marginLeft: 1, children: [
103
- /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "[COMPILING] " }),
104
- /* @__PURE__ */ jsx2(Text2, { color: "white", children: status })
105
- ] })
106
- ] });
107
- };
108
-
109
- // src/ui/SummaryTable.tsx
110
- import * as path2 from "path";
111
- import { Box as Box3, Text as Text3 } from "ink";
112
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
113
- function formatBytes(bytes) {
114
- if (bytes === 0) return "0 B";
115
- const k = 1024;
116
- const sizes = ["B", "KB", "MB", "GB"];
117
- const i = Math.floor(Math.log(bytes) / Math.log(k));
118
- return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
119
- }
120
- function formatDisplayPath2(p) {
121
- if (!p) return "";
122
- try {
123
- const rel = path2.relative(process.cwd(), p);
124
- return rel && !rel.startsWith("..") && rel.length < p.length ? `./${rel}` : p;
125
- } catch {
126
- return p;
127
- }
128
- }
129
- var SummaryTable = ({ result }) => {
130
- if (!result) return null;
131
- const hasErrors = result.errors.length > 0;
132
- return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginY: 1, children: /* @__PURE__ */ jsxs3(
133
- Box3,
134
- {
135
- borderStyle: "round",
136
- borderColor: hasErrors ? "red" : "green",
137
- paddingX: 2,
138
- paddingY: 1,
139
- flexDirection: "column",
140
- children: [
141
- /* @__PURE__ */ jsxs3(Box3, { justifyContent: "space-between", alignItems: "center", children: [
142
- /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: hasErrors ? "red" : "green", children: hasErrors ? "[COMPILATION FAILED]" : "[COMPILATION SUCCESSFUL]" }) }),
143
- /* @__PURE__ */ jsxs3(Box3, { children: [
144
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Duration: " }),
145
- /* @__PURE__ */ jsxs3(Text3, { color: "yellow", bold: true, children: [
146
- result.durationMs,
147
- "ms"
148
- ] })
149
- ] })
150
- ] }),
151
- result.files.length > 0 && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
152
- /* @__PURE__ */ jsxs3(Text3, { bold: true, color: "white", children: [
153
- "Generated Documents (",
154
- result.files.length,
155
- "):"
156
- ] }),
157
- result.files.map((f, i) => {
158
- const formatColor = f.format === "docx" ? "blue" : f.format === "pdf" ? "red" : "yellow";
159
- const paddedBadge = `[${f.format.toUpperCase()}]`.padEnd(7, " ");
160
- return /* @__PURE__ */ jsxs3(Box3, { marginTop: 0, flexDirection: "column", marginLeft: 1, children: [
161
- /* @__PURE__ */ jsxs3(Box3, { justifyContent: "space-between", alignItems: "center", children: [
162
- /* @__PURE__ */ jsxs3(Box3, { children: [
163
- /* @__PURE__ */ jsx3(Text3, { color: formatColor, bold: true, children: paddedBadge }),
164
- /* @__PURE__ */ jsx3(Text3, { color: "white", bold: true, children: f.fileName })
165
- ] }),
166
- /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: formatBytes(f.sizeBytes) })
167
- ] }),
168
- /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
169
- "\u2514\u2500 ",
170
- formatDisplayPath2(f.filePath)
171
- ] }) })
172
- ] }, i);
173
- })
174
- ] }),
175
- hasErrors && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
176
- /* @__PURE__ */ jsx3(Text3, { bold: true, color: "red", children: "Errors:" }),
177
- result.errors.map((err, i) => /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
178
- "- ",
179
- err
180
- ] }) }, i))
181
- ] }),
182
- /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, justifyContent: "space-between", alignItems: "center", children: [
183
- /* @__PURE__ */ jsxs3(Box3, { children: [
184
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Author: " }),
185
- /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "Ma'sum" }),
186
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " (@masumrpg)" })
187
- ] }),
188
- /* @__PURE__ */ jsxs3(Box3, { children: [
189
- /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "GitHub: " }),
190
- /* @__PURE__ */ jsx3(Text3, { color: "blue", underline: true, children: "https://github.com/masumrpg" })
191
- ] })
192
- ] })
193
- ]
194
- }
195
- ) });
196
- };
197
-
198
- // src/core/engine.ts
199
- import * as fs5 from "fs";
200
- import * as path7 from "path";
12
+ } from "./chunk-BPHDY6VB.mjs";
201
13
 
202
14
  // src/core/parser.ts
203
15
  import matter from "gray-matter";
@@ -231,6 +43,26 @@ function parseInlineSpans(text) {
231
43
  remaining = remaining.slice(imgMatch[0].length);
232
44
  continue;
233
45
  }
46
+ const fnMatch = remaining.match(/^\[\^([\w-]+)\]/);
47
+ if (fnMatch) {
48
+ const fnId = fnMatch[1];
49
+ spans.push({
50
+ type: "footnoteRef",
51
+ content: fnId,
52
+ footnoteId: fnId
53
+ });
54
+ remaining = remaining.slice(fnMatch[0].length);
55
+ continue;
56
+ }
57
+ const mathMatch = remaining.match(/^\$([^$\n]+?)\$/);
58
+ if (mathMatch && !mathMatch[1].startsWith("$")) {
59
+ spans.push({
60
+ type: "mathInline",
61
+ content: mathMatch[1]
62
+ });
63
+ remaining = remaining.slice(mathMatch[0].length);
64
+ continue;
65
+ }
234
66
  const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/);
235
67
  if (linkMatch) {
236
68
  spans.push({
@@ -330,7 +162,7 @@ function parseInlineSpans(text) {
330
162
  remaining = remaining.slice(fullTag.length);
331
163
  continue;
332
164
  }
333
- const nextSpecial = remaining.search(/[\*\_\[\!`~<]/);
165
+ const nextSpecial = remaining.search(/[\*\_\[\!`~<\$]/);
334
166
  if (nextSpecial === -1) {
335
167
  spans.push({
336
168
  type: "text",
@@ -356,6 +188,35 @@ function parseInlineSpans(text) {
356
188
  function slugify(text) {
357
189
  return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
358
190
  }
191
+ function applyHeadingNumbering(nodes, tocEntries, options) {
192
+ const depth = options.depth ?? 3;
193
+ const skipH1 = options.skipH1 ?? false;
194
+ const prefix = options.prefix ?? "";
195
+ const counters = [0, 0, 0, 0, 0, 0];
196
+ for (const node of nodes) {
197
+ if (node.type === "heading" && node.level) {
198
+ const lvl = node.level;
199
+ if (lvl > depth) continue;
200
+ if (lvl === 1 && skipH1) continue;
201
+ const idx = lvl - 1;
202
+ counters[idx]++;
203
+ for (let c = idx + 1; c < counters.length; c++) {
204
+ counters[c] = 0;
205
+ }
206
+ const startIdx = skipH1 ? 1 : 0;
207
+ const parts = counters.slice(startIdx, idx + 1).filter((n) => n > 0);
208
+ const numberStr = parts.join(".") + ".";
209
+ const fullPrefix = prefix ? `${prefix} ${numberStr} ` : `${numberStr} `;
210
+ const originalText = node.text || "";
211
+ node.text = fullPrefix + originalText;
212
+ node.inlines = parseInlineSpans(node.text);
213
+ const toc = tocEntries.find((t) => t.id === node.id);
214
+ if (toc) {
215
+ toc.text = node.text;
216
+ }
217
+ }
218
+ }
219
+ }
359
220
  function parseMarkdownDocument(rawMarkdown) {
360
221
  const { data: frontmatter, content } = matter(rawMarkdown);
361
222
  const metadata = frontmatter || {};
@@ -367,6 +228,7 @@ function parseMarkdownDocument(rawMarkdown) {
367
228
  const lines = cleanContent.split(/\r?\n/);
368
229
  const nodes = [];
369
230
  const tocEntries = [];
231
+ const footnoteDefs = [];
370
232
  let i = 0;
371
233
  while (i < lines.length) {
372
234
  const line = lines[i];
@@ -395,6 +257,29 @@ function parseMarkdownDocument(rawMarkdown) {
395
257
  i++;
396
258
  continue;
397
259
  }
260
+ if (line.trim().startsWith("$$")) {
261
+ const mathLines = [];
262
+ const singleLine = line.trim().match(/^\$\$(.+)\$\$$/);
263
+ if (singleLine) {
264
+ nodes.push({
265
+ type: "mathBlock",
266
+ text: singleLine[1].trim()
267
+ });
268
+ i++;
269
+ continue;
270
+ }
271
+ i++;
272
+ while (i < lines.length && !lines[i].trim().startsWith("$$")) {
273
+ mathLines.push(lines[i]);
274
+ i++;
275
+ }
276
+ if (i < lines.length) i++;
277
+ nodes.push({
278
+ type: "mathBlock",
279
+ text: mathLines.join("\n").trim()
280
+ });
281
+ continue;
282
+ }
398
283
  const codeBlockMatch = line.match(/^```(\w+)?/);
399
284
  if (codeBlockMatch) {
400
285
  const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
@@ -421,6 +306,100 @@ function parseMarkdownDocument(rawMarkdown) {
421
306
  }
422
307
  continue;
423
308
  }
309
+ const colsMatch = line.trim().match(/^:::columns(?:\s+\[?([\w\s=.-]+)\]?)?$/i);
310
+ if (colsMatch) {
311
+ const attrStr = colsMatch[1] || "";
312
+ let colsCount = 2;
313
+ let colGap = "1.5rem";
314
+ if (attrStr) {
315
+ const numMatch = attrStr.trim().match(/^(\d+)$/);
316
+ const cMatch = attrStr.match(/cols=(\d+)/i) || attrStr.match(/columns=(\d+)/i);
317
+ const gMatch = attrStr.match(/gap=([^\s]+)/i);
318
+ if (numMatch) colsCount = parseInt(numMatch[1], 10);
319
+ else if (cMatch) colsCount = parseInt(cMatch[1], 10);
320
+ if (gMatch) colGap = gMatch[1];
321
+ }
322
+ const columnNodes = [];
323
+ let currentColumnLines = [];
324
+ let inColBlock = false;
325
+ i++;
326
+ while (i < lines.length) {
327
+ const curLine = lines[i];
328
+ const trimmed = curLine.trim();
329
+ if (/^:::col(?:umn)?$/i.test(trimmed)) {
330
+ if (currentColumnLines.length > 0) {
331
+ const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
332
+ columnNodes.push({
333
+ type: "column",
334
+ children: subDoc.nodes
335
+ });
336
+ currentColumnLines = [];
337
+ }
338
+ inColBlock = true;
339
+ i++;
340
+ } else if (trimmed === ":::") {
341
+ if (inColBlock) {
342
+ if (currentColumnLines.length > 0) {
343
+ const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
344
+ columnNodes.push({
345
+ type: "column",
346
+ children: subDoc.nodes
347
+ });
348
+ currentColumnLines = [];
349
+ }
350
+ inColBlock = false;
351
+ i++;
352
+ } else {
353
+ if (currentColumnLines.length > 0) {
354
+ const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
355
+ columnNodes.push({
356
+ type: "column",
357
+ children: subDoc.nodes
358
+ });
359
+ currentColumnLines = [];
360
+ }
361
+ i++;
362
+ break;
363
+ }
364
+ } else {
365
+ currentColumnLines.push(curLine);
366
+ i++;
367
+ }
368
+ }
369
+ if (currentColumnLines.length > 0) {
370
+ const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
371
+ columnNodes.push({
372
+ type: "column",
373
+ children: subDoc.nodes
374
+ });
375
+ }
376
+ nodes.push({
377
+ type: "columns",
378
+ columnsCount: columnNodes.length > 0 ? columnNodes.length : colsCount,
379
+ columnGap: colGap,
380
+ children: columnNodes
381
+ });
382
+ continue;
383
+ }
384
+ const fnDefMatch = line.match(/^\[\^([\w-]+)\]:\s+(.+)$/);
385
+ if (fnDefMatch) {
386
+ const fnId = fnDefMatch[1];
387
+ const fnText = fnDefMatch[2].trim();
388
+ const inlines = parseInlineSpans(fnText);
389
+ footnoteDefs.push({
390
+ id: fnId,
391
+ text: fnText,
392
+ inlines
393
+ });
394
+ nodes.push({
395
+ type: "footnoteDef",
396
+ footnoteId: fnId,
397
+ text: fnText,
398
+ inlines
399
+ });
400
+ i++;
401
+ continue;
402
+ }
424
403
  const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
425
404
  if (calloutMatch) {
426
405
  const calloutType = calloutMatch[1].toUpperCase();
@@ -544,7 +523,7 @@ function parseMarkdownDocument(rawMarkdown) {
544
523
  continue;
545
524
  }
546
525
  const paraLines = [];
547
- while (i < lines.length && lines[i].trim() && !lines[i].match(/^#{1,6}\s+/) && !lines[i].startsWith("```") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !lines[i].match(/^(\s*)([-*+]|\d+\.)\s+/)) {
526
+ while (i < lines.length && lines[i].trim() && !lines[i].match(/^#{1,6}\s+/) && !lines[i].startsWith("```") && !lines[i].startsWith("$$") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !lines[i].match(/^:::columns/) && !lines[i].match(/^\[\^[\w-]+\]:\s+/) && !lines[i].match(/^(\s*)([-*+]|\d+\.)\s+/)) {
548
527
  paraLines.push(lines[i]);
549
528
  i++;
550
529
  }
@@ -555,43 +534,29 @@ function parseMarkdownDocument(rawMarkdown) {
555
534
  inlines: parseInlineSpans(paraText)
556
535
  });
557
536
  }
537
+ if (metadata.numberHeadings) {
538
+ const opts = typeof metadata.numberHeadings === "boolean" ? { enabled: metadata.numberHeadings } : metadata.numberHeadings;
539
+ if (opts.enabled !== false) {
540
+ applyHeadingNumbering(nodes, tocEntries, opts);
541
+ }
542
+ }
558
543
  return {
559
544
  metadata,
560
545
  content: cleanContent,
561
546
  nodes,
562
547
  tocEntries,
563
- inlinedStyles
548
+ inlinedStyles,
549
+ footnoteDefs
564
550
  };
565
551
  }
566
552
 
567
- // src/core/docx/docxBuilder.ts
568
- import {
569
- Document,
570
- Paragraph,
571
- TextRun,
572
- HeadingLevel,
573
- Table,
574
- TableRow,
575
- TableCell,
576
- WidthType,
577
- BorderStyle,
578
- AlignmentType,
579
- ImageRun,
580
- Header as Header2,
581
- Footer,
582
- PageNumber,
583
- Packer,
584
- PageOrientation,
585
- convertInchesToTwip,
586
- convertMillimetersToTwip,
587
- ShadingType,
588
- ExternalHyperlink,
589
- TabStopType
590
- } from "docx";
553
+ // src/core/html/htmlBuilder.ts
554
+ import * as fs2 from "fs";
555
+ import * as path2 from "path";
591
556
 
592
557
  // src/core/imageResolver.ts
593
558
  import * as fs from "fs";
594
- import * as path3 from "path";
559
+ import * as path from "path";
595
560
  var memoryImageCache = /* @__PURE__ */ new Map();
596
561
  function getMimeType(filePathOrUrl) {
597
562
  const clean = filePathOrUrl.split("?")[0].toLowerCase();
@@ -648,9 +613,14 @@ async function resolveImage(src, baseDir = process.cwd()) {
648
613
  memoryImageCache.set(cacheKey, resolved2);
649
614
  return resolved2;
650
615
  }
651
- const localPath = path3.isAbsolute(src) ? src : path3.resolve(baseDir, src);
616
+ let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
652
617
  if (!fs.existsSync(localPath)) {
653
- return null;
618
+ const cwdPath = path.resolve(process.cwd(), src);
619
+ if (fs.existsSync(cwdPath)) {
620
+ localPath = cwdPath;
621
+ } else {
622
+ return null;
623
+ }
654
624
  }
655
625
  const buffer = fs.readFileSync(localPath);
656
626
  const mimeType = getMimeType(localPath);
@@ -670,6 +640,164 @@ async function resolveImage(src, baseDir = process.cwd()) {
670
640
  }
671
641
  }
672
642
 
643
+ // src/core/html/htmlThemes.ts
644
+ var THEME_COMPONENTS = `
645
+ /* Fallback CSS variables \u2014 overridden by each named theme's :root block */
646
+ :root {
647
+ --mf-bg: #ffffff;
648
+ --mf-text: #0f172a;
649
+ --mf-text-muted: #64748b;
650
+ --mf-primary: #33CDCF;
651
+ --mf-primary-dark: #009DA0;
652
+ --mf-primary-light: #ECFDFD;
653
+ --mf-border: #e2e8f0;
654
+ --mf-card-bg: #f8fafc;
655
+ --mf-code-bg: #0f172a;
656
+ --mf-code-text: #f8fafc;
657
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
658
+ --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
659
+ }
660
+
661
+ /* Document header */
662
+ .document-header { margin-bottom: 2.5rem; padding-bottom: 1.5rem; border-bottom: 2px solid var(--mf-border); }
663
+ .document-title { font-size: 2.5rem; font-weight: 800; margin: 0 0 0.5rem 0; }
664
+ .document-subtitle { font-size: 1.25rem; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
665
+ .document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
666
+
667
+ /* Table of Contents */
668
+ .table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; }
669
+ .table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
670
+ .table-of-contents ul { list-style: none; padding: 0; margin: 0; }
671
+ .table-of-contents li { padding: 0.25rem 0; }
672
+ .table-of-contents a { color: var(--mf-primary-dark); text-decoration: none; }
673
+ .table-of-contents a:hover { text-decoration: underline; }
674
+
675
+ /* Links */
676
+ a { color: var(--mf-primary-dark); font-weight: 600; text-decoration: none; }
677
+ a:hover { text-decoration: underline; }
678
+
679
+ /* Blockquote */
680
+ blockquote { border-left: 4px solid var(--mf-primary); background-color: var(--mf-card-bg); margin: 1.2rem 0; padding: 0.8rem 1.2rem; border-radius: 0 8px 8px 0; color: #334155; font-style: italic; }
681
+
682
+ /* Callout / Alert Boxes */
683
+ .callout { border-left: 4px solid var(--mf-primary); background: var(--mf-card-bg); border-radius: 6px; padding: 1rem 1.2rem; margin: 1.2rem 0; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
684
+ .callout-title { font-weight: 700; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.4rem; }
685
+ .callout-NOTE { border-color: #33CDCF; background: #ECFDFD; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
686
+ .callout-NOTE .callout-title { color: #009DA0; }
687
+ .callout-TIP { border-color: #10b981; background: #ecfdf5; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
688
+ .callout-TIP .callout-title { color: #10b981; }
689
+ .callout-WARNING { border-color: #f59e0b; background: #fffbeb; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
690
+ .callout-WARNING .callout-title { color: #f59e0b; }
691
+ .callout-CAUTION { border-color: #ef4444; background: #fef2f2; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
692
+ .callout-CAUTION .callout-title { color: #ef4444; }
693
+ .callout-IMPORTANT { border-color: #8b5cf6; background: #f5f3ff; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
694
+ .callout-IMPORTANT .callout-title { color: #8b5cf6; }
695
+
696
+ /* Inline Code */
697
+ code { font-family: var(--mf-font-mono); font-size: 0.88em; background-color: #f1f5f9; padding: 0.2em 0.4em; border-radius: 4px; color: #0f172a; }
698
+
699
+ /* Code Blocks */
700
+ pre { background-color: var(--mf-code-bg); color: var(--mf-code-text); padding: 1.2rem; border-radius: 8px; overflow-x: auto; font-family: var(--mf-font-mono); font-size: 0.9rem; line-height: 1.5; margin: 1.2rem 0; }
701
+ pre code { background-color: transparent; color: inherit; padding: 0; }
702
+
703
+ /* Tables */
704
+ table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; font-size: 0.95rem; }
705
+ th, td { border: 1px solid var(--mf-border); padding: 0.75rem 1rem; text-align: left; }
706
+ th { background-color: var(--mf-card-bg); font-weight: 600; color: #0f172a; }
707
+ tr:nth-child(even) { background-color: var(--mf-card-bg); }
708
+
709
+ /* Images */
710
+ img { max-width: 100%; height: auto; border-radius: 6px; margin: 1rem 0; }
711
+
712
+ /* Dividers */
713
+ hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
714
+
715
+ /* Print / PDF */
716
+ @media print {
717
+ body { padding: 0; }
718
+ .document-container { max-width: 100%; }
719
+ pre, table, blockquote, .callout { break-inside: avoid; }
720
+ /* Remove scrollbars \u2014 PDF has no scrolling */
721
+ pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
722
+ }
723
+ `;
724
+ var THEME_CORPORATE = `
725
+ :root {
726
+ --mf-bg: #ffffff;
727
+ --mf-text: #0f172a;
728
+ --mf-text-muted: #64748b;
729
+ --mf-primary: #33CDCF;
730
+ --mf-primary-dark: #009DA0;
731
+ --mf-primary-light: #ECFDFD;
732
+ --mf-border: #e2e8f0;
733
+ --mf-card-bg: #f8fafc;
734
+ --mf-code-bg: #0f172a;
735
+ --mf-code-text: #f8fafc;
736
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
737
+ --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
738
+ }
739
+ 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; }
740
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
741
+ 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; }
742
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
743
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
744
+ h3 { font-size: 1.3rem; }
745
+ h4 { font-size: 1.1rem; }
746
+ p { margin: 0.8rem 0; }
747
+ `;
748
+ var THEMES = {
749
+ corporate: THEME_CORPORATE,
750
+ default: THEME_CORPORATE
751
+ };
752
+ function generateThemeCss(theme) {
753
+ if (!theme || theme === "corporate" || theme === "default" || theme === "corporate" /* CORPORATE */) {
754
+ return THEME_CORPORATE;
755
+ }
756
+ if (typeof theme === "object") {
757
+ const bg = theme.backgroundColor || "#ffffff";
758
+ const text = theme.textColor || "#0f172a";
759
+ const textMuted = theme.textMuted || "#64748b";
760
+ const primary = theme.primaryColor || "#33CDCF";
761
+ const primaryDark = theme.primaryDark || primary;
762
+ const primaryLight = theme.primaryLight || "#ECFDFD";
763
+ const border = theme.borderColor || "#e2e8f0";
764
+ const cardBg = theme.cardBackground || "#f8fafc";
765
+ const codeBg = theme.codeBackground || "#0f172a";
766
+ const codeText = theme.codeText || "#f8fafc";
767
+ const font = theme.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
768
+ const fontMono = theme.fontMono || "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
769
+ return `
770
+ :root {
771
+ --mf-bg: ${bg};
772
+ --mf-text: ${text};
773
+ --mf-text-muted: ${textMuted};
774
+ --mf-primary: ${primary};
775
+ --mf-primary-dark: ${primaryDark};
776
+ --mf-primary-light: ${primaryLight};
777
+ --mf-border: ${border};
778
+ --mf-card-bg: ${cardBg};
779
+ --mf-code-bg: ${codeBg};
780
+ --mf-code-text: ${codeText};
781
+ --mf-font-family: ${font};
782
+ --mf-font-mono: ${fontMono};
783
+ }
784
+ 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; }
785
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
786
+ 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; }
787
+ h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
788
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
789
+ h3 { font-size: 1.3rem; }
790
+ h4 { font-size: 1.1rem; }
791
+ p { margin: 0.8rem 0; }
792
+ ${theme.customCss || ""}
793
+ `;
794
+ }
795
+ if (typeof theme === "string" && THEMES[theme]) {
796
+ return THEMES[theme];
797
+ }
798
+ return THEME_CORPORATE;
799
+ }
800
+
673
801
  // src/core/syntax/syntaxHighlighter.ts
674
802
  var SYNTAX_COLORS = {
675
803
  keyword: "FF7B72",
@@ -1025,182 +1153,6 @@ function highlightCodeToHtml(code, lang = "", theme = "github-dark") {
1025
1153
  return htmlLines.join("\n");
1026
1154
  }
1027
1155
 
1028
- // src/core/mermaid/mermaidRenderer.ts
1029
- import { spawnSync as spawnSync2 } from "child_process";
1030
- import * as fs4 from "fs";
1031
- import * as os2 from "os";
1032
- import * as path6 from "path";
1033
- import { pathToFileURL as pathToFileURL2 } from "url";
1034
-
1035
- // src/core/pdf/pdfBuilder.ts
1036
- import * as fs3 from "fs";
1037
- import * as path5 from "path";
1038
- import * as os from "os";
1039
- import { pathToFileURL } from "url";
1040
- import { spawnSync } from "child_process";
1041
-
1042
- // src/core/html/htmlBuilder.ts
1043
- import * as fs2 from "fs";
1044
- import * as path4 from "path";
1045
-
1046
- // src/core/html/htmlThemes.ts
1047
- var THEME_COMPONENTS = `
1048
- /* Fallback CSS variables \u2014 overridden by each named theme's :root block */
1049
- :root {
1050
- --mf-bg: #ffffff;
1051
- --mf-text: #0f172a;
1052
- --mf-text-muted: #64748b;
1053
- --mf-primary: #33CDCF;
1054
- --mf-primary-dark: #009DA0;
1055
- --mf-primary-light: #ECFDFD;
1056
- --mf-border: #e2e8f0;
1057
- --mf-card-bg: #f8fafc;
1058
- --mf-code-bg: #0f172a;
1059
- --mf-code-text: #f8fafc;
1060
- --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1061
- --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1062
- }
1063
-
1064
- /* Document header */
1065
- .document-header { margin-bottom: 2.5rem; padding-bottom: 1.5rem; border-bottom: 2px solid var(--mf-border); }
1066
- .document-title { font-size: 2.5rem; font-weight: 800; margin: 0 0 0.5rem 0; }
1067
- .document-subtitle { font-size: 1.25rem; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
1068
- .document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
1069
-
1070
- /* Table of Contents */
1071
- .table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; }
1072
- .table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
1073
- .table-of-contents ul { list-style: none; padding: 0; margin: 0; }
1074
- .table-of-contents li { padding: 0.25rem 0; }
1075
- .table-of-contents a { color: var(--mf-primary-dark); text-decoration: none; }
1076
- .table-of-contents a:hover { text-decoration: underline; }
1077
-
1078
- /* Links */
1079
- a { color: var(--mf-primary-dark); font-weight: 600; text-decoration: none; }
1080
- a:hover { text-decoration: underline; }
1081
-
1082
- /* Blockquote */
1083
- blockquote { border-left: 4px solid var(--mf-primary); background-color: var(--mf-card-bg); margin: 1.2rem 0; padding: 0.8rem 1.2rem; border-radius: 0 8px 8px 0; color: #334155; font-style: italic; }
1084
-
1085
- /* Callout / Alert Boxes */
1086
- .callout { border-left: 4px solid var(--mf-primary); background: var(--mf-card-bg); border-radius: 6px; padding: 1rem 1.2rem; margin: 1.2rem 0; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
1087
- .callout-title { font-weight: 700; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.4rem; }
1088
- .callout-NOTE { border-color: #33CDCF; background: #ECFDFD; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
1089
- .callout-NOTE .callout-title { color: #009DA0; }
1090
- .callout-TIP { border-color: #10b981; background: #ecfdf5; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
1091
- .callout-TIP .callout-title { color: #10b981; }
1092
- .callout-WARNING { border-color: #f59e0b; background: #fffbeb; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
1093
- .callout-WARNING .callout-title { color: #f59e0b; }
1094
- .callout-CAUTION { border-color: #ef4444; background: #fef2f2; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
1095
- .callout-CAUTION .callout-title { color: #ef4444; }
1096
- .callout-IMPORTANT { border-color: #8b5cf6; background: #f5f3ff; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
1097
- .callout-IMPORTANT .callout-title { color: #8b5cf6; }
1098
-
1099
- /* Inline Code */
1100
- code { font-family: var(--mf-font-mono); font-size: 0.88em; background-color: #f1f5f9; padding: 0.2em 0.4em; border-radius: 4px; color: #0f172a; }
1101
-
1102
- /* Code Blocks */
1103
- pre { background-color: var(--mf-code-bg); color: var(--mf-code-text); padding: 1.2rem; border-radius: 8px; overflow-x: auto; font-family: var(--mf-font-mono); font-size: 0.9rem; line-height: 1.5; margin: 1.2rem 0; }
1104
- pre code { background-color: transparent; color: inherit; padding: 0; }
1105
-
1106
- /* Tables */
1107
- table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; font-size: 0.95rem; }
1108
- th, td { border: 1px solid var(--mf-border); padding: 0.75rem 1rem; text-align: left; }
1109
- th { background-color: var(--mf-card-bg); font-weight: 600; color: #0f172a; }
1110
- tr:nth-child(even) { background-color: var(--mf-card-bg); }
1111
-
1112
- /* Images */
1113
- img { max-width: 100%; height: auto; border-radius: 6px; margin: 1rem 0; }
1114
-
1115
- /* Dividers */
1116
- hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
1117
-
1118
- /* Print / PDF */
1119
- @media print {
1120
- body { padding: 0; }
1121
- .document-container { max-width: 100%; }
1122
- pre, table, blockquote, .callout { break-inside: avoid; }
1123
- /* Remove scrollbars \u2014 PDF has no scrolling */
1124
- pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
1125
- }
1126
- `;
1127
- var THEME_CORPORATE = `
1128
- :root {
1129
- --mf-bg: #ffffff;
1130
- --mf-text: #0f172a;
1131
- --mf-text-muted: #64748b;
1132
- --mf-primary: #33CDCF;
1133
- --mf-primary-dark: #009DA0;
1134
- --mf-primary-light: #ECFDFD;
1135
- --mf-border: #e2e8f0;
1136
- --mf-card-bg: #f8fafc;
1137
- --mf-code-bg: #0f172a;
1138
- --mf-code-text: #f8fafc;
1139
- --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1140
- --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
1141
- }
1142
- 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; }
1143
- .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1144
- 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; }
1145
- h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1146
- h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
1147
- h3 { font-size: 1.3rem; }
1148
- h4 { font-size: 1.1rem; }
1149
- p { margin: 0.8rem 0; }
1150
- `;
1151
- var THEMES = {
1152
- corporate: THEME_CORPORATE,
1153
- default: THEME_CORPORATE
1154
- };
1155
- function generateThemeCss(theme) {
1156
- if (!theme || theme === "corporate" || theme === "default" || theme === "corporate" /* CORPORATE */) {
1157
- return THEME_CORPORATE;
1158
- }
1159
- if (typeof theme === "object") {
1160
- const bg = theme.backgroundColor || "#ffffff";
1161
- const text = theme.textColor || "#0f172a";
1162
- const textMuted = theme.textMuted || "#64748b";
1163
- const primary = theme.primaryColor || "#33CDCF";
1164
- const primaryDark = theme.primaryDark || primary;
1165
- const primaryLight = theme.primaryLight || "#ECFDFD";
1166
- const border = theme.borderColor || "#e2e8f0";
1167
- const cardBg = theme.cardBackground || "#f8fafc";
1168
- const codeBg = theme.codeBackground || "#0f172a";
1169
- const codeText = theme.codeText || "#f8fafc";
1170
- const font = theme.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
1171
- const fontMono = theme.fontMono || "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
1172
- return `
1173
- :root {
1174
- --mf-bg: ${bg};
1175
- --mf-text: ${text};
1176
- --mf-text-muted: ${textMuted};
1177
- --mf-primary: ${primary};
1178
- --mf-primary-dark: ${primaryDark};
1179
- --mf-primary-light: ${primaryLight};
1180
- --mf-border: ${border};
1181
- --mf-card-bg: ${cardBg};
1182
- --mf-code-bg: ${codeBg};
1183
- --mf-code-text: ${codeText};
1184
- --mf-font-family: ${font};
1185
- --mf-font-mono: ${fontMono};
1186
- }
1187
- 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; }
1188
- .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
1189
- 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; }
1190
- h1 { font-size: 2.2rem; border-bottom: 2px solid var(--mf-primary); padding-bottom: 0.5rem; }
1191
- h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
1192
- h3 { font-size: 1.3rem; }
1193
- h4 { font-size: 1.1rem; }
1194
- p { margin: 0.8rem 0; }
1195
- ${theme.customCss || ""}
1196
- `;
1197
- }
1198
- if (typeof theme === "string" && THEMES[theme]) {
1199
- return THEMES[theme];
1200
- }
1201
- return THEME_CORPORATE;
1202
- }
1203
-
1204
1156
  // src/config/resolveConfig.ts
1205
1157
  var PAPER_DIMENSIONS_TWIP = {
1206
1158
  A4: { width: 11906, height: 16838 },
@@ -1246,7 +1198,8 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
1246
1198
  return str;
1247
1199
  }
1248
1200
  function replaceDocumentTokens(template = "", meta) {
1249
- 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 || "");
1201
+ const currentYear = meta.year || (/* @__PURE__ */ new Date()).getFullYear().toString();
1202
+ 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(/\{year\}/gi, currentYear).replace(/\{company\}/gi, meta.company || "");
1250
1203
  }
1251
1204
  function normalizeWatermark(rawWatermark) {
1252
1205
  if (!rawWatermark) {
@@ -1383,6 +1336,129 @@ function normalizeSignatures(raw, meta = {}) {
1383
1336
  spacingBeforeTwip
1384
1337
  };
1385
1338
  }
1339
+ function normalizeCoverPage(rawCover, tokenCtx = {}) {
1340
+ if (!rawCover) return void 0;
1341
+ const cfg = typeof rawCover === "object" ? rawCover : {};
1342
+ if (cfg.enabled === false) return void 0;
1343
+ const preset = cfg.preset || "modern";
1344
+ const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title || "Document Title";
1345
+ const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle;
1346
+ const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author;
1347
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
1348
+ const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version;
1349
+ let dateStr;
1350
+ if (typeof cfg.date === "string") {
1351
+ dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
1352
+ } else if (cfg.date === true) {
1353
+ dateStr = tokenCtx.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1354
+ } else {
1355
+ dateStr = tokenCtx.date;
1356
+ }
1357
+ const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1358
+ const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1359
+ const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1360
+ const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1361
+ const logoWidth = cfg.logoWidth;
1362
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
1363
+ const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1364
+ const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
1365
+ return {
1366
+ enabled: true,
1367
+ preset,
1368
+ title,
1369
+ subtitle,
1370
+ author,
1371
+ company,
1372
+ version,
1373
+ date: dateStr,
1374
+ badge,
1375
+ badgeColor,
1376
+ badgeTextColor,
1377
+ logo,
1378
+ logoWidth,
1379
+ bgGradient,
1380
+ textColor,
1381
+ footerText
1382
+ };
1383
+ }
1384
+ function normalizeBackCover(rawBack, tokenCtx = {}) {
1385
+ if (!rawBack) return void 0;
1386
+ const cfg = typeof rawBack === "object" ? rawBack : {};
1387
+ if (cfg.enabled === false) return void 0;
1388
+ const preset = cfg.preset || "modern";
1389
+ const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
1390
+ const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
1391
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
1392
+ const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
1393
+ const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
1394
+ const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
1395
+ const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
1396
+ let socialMap;
1397
+ if (cfg.social && typeof cfg.social === "object") {
1398
+ socialMap = {};
1399
+ for (const [k, v] of Object.entries(cfg.social)) {
1400
+ if (typeof v === "string") {
1401
+ socialMap[k] = replaceDocumentTokens(v, tokenCtx);
1402
+ }
1403
+ }
1404
+ }
1405
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
1406
+ const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
1407
+ const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1408
+ const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1409
+ const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1410
+ const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1411
+ const logoWidth = cfg.logoWidth;
1412
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
1413
+ const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1414
+ return {
1415
+ enabled: true,
1416
+ preset,
1417
+ title,
1418
+ subtitle,
1419
+ company,
1420
+ address,
1421
+ email,
1422
+ phone,
1423
+ website,
1424
+ social: socialMap,
1425
+ copyright,
1426
+ badge,
1427
+ badgeColor,
1428
+ badgeTextColor,
1429
+ logo,
1430
+ logoWidth,
1431
+ bgGradient,
1432
+ textColor
1433
+ };
1434
+ }
1435
+ function normalizeNumberHeadings(raw) {
1436
+ if (raw === void 0 || raw === false) return void 0;
1437
+ if (raw === true) {
1438
+ return { enabled: true, depth: 3, skipH1: false, prefix: "" };
1439
+ }
1440
+ if (typeof raw === "object") {
1441
+ const obj = raw;
1442
+ if (obj.enabled === false) return void 0;
1443
+ return {
1444
+ enabled: true,
1445
+ depth: obj.depth ?? 3,
1446
+ skipH1: obj.skipH1 ?? false,
1447
+ prefix: obj.prefix ?? ""
1448
+ };
1449
+ }
1450
+ return void 0;
1451
+ }
1452
+ function normalizeSecurity(raw) {
1453
+ if (!raw) return void 0;
1454
+ const sec = raw;
1455
+ if (!sec.userPassword && !sec.ownerPassword && !sec.permissions) return void 0;
1456
+ return {
1457
+ userPassword: sec.userPassword,
1458
+ ownerPassword: sec.ownerPassword,
1459
+ permissions: sec.permissions
1460
+ };
1461
+ }
1386
1462
  function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1387
1463
  const configMeta = userConfig.metadata || {};
1388
1464
  const mergedMeta = { ...configMeta, ...frontmatter };
@@ -1425,6 +1501,15 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1425
1501
  const watermark = normalizeWatermark(rawWatermark);
1426
1502
  const rawSignatures = mergedMeta.signatures || userConfig.signatures;
1427
1503
  const signatures = normalizeSignatures(rawSignatures, tokenContext);
1504
+ const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
1505
+ const coverPage = normalizeCoverPage(rawCover, tokenContext);
1506
+ const rawBack = mergedMeta.backCover !== void 0 ? mergedMeta.backCover : userConfig.backCover;
1507
+ const backCover = normalizeBackCover(rawBack, tokenContext);
1508
+ const rawNumberHeadings = mergedMeta.numberHeadings !== void 0 ? mergedMeta.numberHeadings : userConfig.numberHeadings;
1509
+ const numberHeadings = normalizeNumberHeadings(rawNumberHeadings);
1510
+ const rawSecurity = mergedMeta.security || userConfig.security;
1511
+ const security = normalizeSecurity(rawSecurity);
1512
+ const math = mergedMeta.math !== false && userConfig.math !== false;
1428
1513
  const cssList = [];
1429
1514
  const addCss = (item) => {
1430
1515
  if (!item) return;
@@ -1454,6 +1539,11 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1454
1539
  toc,
1455
1540
  signatures,
1456
1541
  watermark,
1542
+ coverPage,
1543
+ backCover,
1544
+ numberHeadings,
1545
+ security,
1546
+ math,
1457
1547
  css: cssList,
1458
1548
  embedImages,
1459
1549
  bundleHtml,
@@ -1461,6 +1551,45 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1461
1551
  };
1462
1552
  }
1463
1553
 
1554
+ // src/core/math/mathRenderer.ts
1555
+ import katex from "katex";
1556
+ function renderMathToHtml(latex, displayMode = false) {
1557
+ try {
1558
+ return katex.renderToString(latex.trim(), {
1559
+ displayMode,
1560
+ throwOnError: false,
1561
+ output: "htmlAndMathml",
1562
+ strict: false
1563
+ });
1564
+ } catch {
1565
+ return `<span class="katex-fallback">${latex}</span>`;
1566
+ }
1567
+ }
1568
+ var KATEX_INLINE_CSS = `
1569
+ .katex { font: normal 1.21em KaTeX_Main, Times New Roman, serif; line-height: 1.2; text-indent: 0; text-rendering: auto; border-color: currentColor; }
1570
+ .katex * { -ms-high-contrast-adjust: none !important; }
1571
+ .katex .katex-html { display: inline-block; }
1572
+ .katex .katex-mathml { clip: rect(1px, 1px, 1px, 1px); border: 0; height: 1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
1573
+ .katex-display { display: block; margin: 1em 0; text-align: center; }
1574
+ .katex-display > .katex { display: inline-block; text-align: initial; }
1575
+ .katex .base { position: relative; white-space: nowrap; width: min-content; }
1576
+ .katex .strut { display: inline-block; }
1577
+ .katex .mord { display: inline-block; }
1578
+ .katex .mbin { display: inline-block; }
1579
+ .katex .mrel { display: inline-block; }
1580
+ .katex .mopen { display: inline-block; }
1581
+ .katex .mclose { display: inline-block; }
1582
+ .katex .mpunct { display: inline-block; }
1583
+ .katex .minner { display: inline-block; }
1584
+ .katex .mop { display: inline-block; }
1585
+ .katex .frac-line { width: 100%; border-bottom-style: solid; }
1586
+ .katex .vlist-t { display: inline-table; table-layout: fixed; }
1587
+ .katex .vlist-r { display: table-row; }
1588
+ .katex .vlist { display: table-cell; vertical-align: bottom; position: relative; }
1589
+ .katex .msupsub { text-align: left; }
1590
+ .katex .sqrt > .root { margin-left: 0.27777778em; margin-right: -0.55555556em; }
1591
+ `;
1592
+
1464
1593
  // src/core/html/htmlBuilder.ts
1465
1594
  function escapeHtml(str) {
1466
1595
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
@@ -1499,78 +1628,30 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1499
1628
  result += `<del>${inner}</del>`;
1500
1629
  continue;
1501
1630
  }
1502
- if (span.type === "code") {
1503
- result += `<code>${escapeHtml(span.content)}</code>`;
1504
- continue;
1505
- }
1506
- if (span.type === "htmlInline") {
1507
- result += span.content;
1508
- continue;
1509
- }
1510
- result += escapeHtml(span.content);
1511
- }
1512
- return result;
1513
- }
1514
- async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1515
- const resolved = resolveDocumentConfig(doc.metadata, config);
1516
- const baseThemeCss = generateThemeCss(resolved.theme);
1517
- let customCss = "";
1518
- for (const cssPath of resolved.css) {
1519
- const fullCssPath = path4.isAbsolute(cssPath) ? cssPath : path4.resolve(baseDir, cssPath);
1520
- if (fs2.existsSync(fullCssPath)) {
1521
- customCss += `
1522
- /* Custom CSS: ${cssPath} */
1523
- ` + fs2.readFileSync(fullCssPath, "utf-8");
1524
- }
1525
- }
1526
- const inlinedCss = doc.inlinedStyles.join("\n");
1527
- let bodyHtml = "";
1528
- if (resolved.title) {
1529
- bodyHtml += ` <header class="document-header">
1530
- `;
1531
- bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
1532
- `;
1533
- if (resolved.subtitle) {
1534
- bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
1535
- `;
1536
- }
1537
- if (resolved.author || resolved.date || resolved.version) {
1538
- bodyHtml += ` <div class="document-meta">
1539
- `;
1540
- if (resolved.author) {
1541
- bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
1542
- `;
1543
- }
1544
- if (resolved.version) {
1545
- bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
1546
- `;
1547
- }
1548
- if (resolved.date) {
1549
- bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
1550
- `;
1551
- }
1552
- bodyHtml += ` </div>
1553
- `;
1631
+ if (span.type === "code") {
1632
+ result += `<code>${escapeHtml(span.content)}</code>`;
1633
+ continue;
1554
1634
  }
1555
- bodyHtml += ` </header>
1556
- `;
1557
- }
1558
- if (resolved.toc && doc.tocEntries.length > 0) {
1559
- bodyHtml += ` <nav class="table-of-contents">
1560
- `;
1561
- bodyHtml += ` <h2>Table of Contents</h2>
1562
- <ul>
1563
- `;
1564
- for (const entry of doc.tocEntries) {
1565
- const indent = " ".repeat(entry.level);
1566
- bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1567
- `;
1635
+ if (span.type === "mathInline") {
1636
+ result += renderMathToHtml(span.content, false);
1637
+ continue;
1568
1638
  }
1569
- bodyHtml += ` </ul>
1570
- </nav>
1571
- `;
1639
+ if (span.type === "footnoteRef") {
1640
+ const id = escapeHtml(span.footnoteId || span.content);
1641
+ result += `<sup><a href="#fn-${id}" id="fnref-${id}" class="markforge-fnref">[${escapeHtml(span.content)}]</a></sup>`;
1642
+ continue;
1643
+ }
1644
+ if (span.type === "htmlInline") {
1645
+ result += span.content;
1646
+ continue;
1647
+ }
1648
+ result += escapeHtml(span.content);
1572
1649
  }
1573
- for (const node of doc.nodes) {
1650
+ return result;
1651
+ }
1652
+ async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
1653
+ let bodyHtml = "";
1654
+ for (const node of nodes) {
1574
1655
  if (node.type === "heading") {
1575
1656
  const inner = await renderInlinesToHtml(node.inlines, baseDir);
1576
1657
  bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
@@ -1580,6 +1661,26 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1580
1661
  if (node.type === "paragraph") {
1581
1662
  const inner = await renderInlinesToHtml(node.inlines, baseDir);
1582
1663
  bodyHtml += ` <p>${inner}</p>
1664
+ `;
1665
+ continue;
1666
+ }
1667
+ if (node.type === "mathBlock") {
1668
+ bodyHtml += ` <div class="math-block">${renderMathToHtml(node.text || "", true)}</div>
1669
+ `;
1670
+ continue;
1671
+ }
1672
+ if (node.type === "columns") {
1673
+ const cols = node.columnsCount || 2;
1674
+ const gap = node.columnGap || "1.5rem";
1675
+ let colChildrenHtml = "";
1676
+ for (const col of node.children || []) {
1677
+ const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir);
1678
+ colChildrenHtml += ` <div class="markforge-col">
1679
+ ${colInner} </div>
1680
+ `;
1681
+ }
1682
+ bodyHtml += ` <div class="markforge-columns" style="--cols: ${cols}; --col-gap: ${gap};">
1683
+ ${colChildrenHtml} </div>
1583
1684
  `;
1584
1685
  continue;
1585
1686
  }
@@ -1668,6 +1769,429 @@ ${escapeHtml(node.text || "")}
1668
1769
  continue;
1669
1770
  }
1670
1771
  }
1772
+ return bodyHtml;
1773
+ }
1774
+ async function renderCoverPageHtml(cover, baseDir = process.cwd()) {
1775
+ let logoHtml = "";
1776
+ if (cover.logo) {
1777
+ const resolvedLogo = await resolveImage(cover.logo, baseDir);
1778
+ const src = resolvedLogo ? resolvedLogo.dataUri : cover.logo;
1779
+ const widthStyle = cover.logoWidth ? `max-width: ${typeof cover.logoWidth === "number" ? cover.logoWidth + "px" : cover.logoWidth}; max-height: 80px; width: auto; height: auto;` : "max-height: 60px; max-width: 180px; width: auto; height: auto;";
1780
+ logoHtml = `<div class="cover-logo"><img src="${src}" alt="Logo" style="${widthStyle} object-fit: contain;" /></div>`;
1781
+ }
1782
+ const badgeHtml = cover.badge ? `<div class="cover-badge" style="${cover.badgeColor ? `background-color: ${cover.badgeColor};` : ""}${cover.badgeTextColor ? `color: ${cover.badgeTextColor};` : ""}">${escapeHtml(cover.badge)}</div>` : "";
1783
+ const titleHtml = `<h1 class="cover-title">${escapeHtml(cover.title)}</h1>`;
1784
+ const subtitleHtml = cover.subtitle ? `<div class="cover-subtitle">${escapeHtml(cover.subtitle)}</div>` : "";
1785
+ const metaItems = [];
1786
+ if (cover.company) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Organization:</span> <span class="cover-meta-value">${escapeHtml(cover.company)}</span></div>`);
1787
+ if (cover.author) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Author:</span> <span class="cover-meta-value">${escapeHtml(cover.author)}</span></div>`);
1788
+ if (cover.version) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Version:</span> <span class="cover-meta-value">${escapeHtml(cover.version)}</span></div>`);
1789
+ if (cover.date) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Date:</span> <span class="cover-meta-value">${escapeHtml(cover.date)}</span></div>`);
1790
+ const metaHtml = metaItems.length > 0 ? `<div class="cover-meta">${metaItems.join("\n")}</div>` : "";
1791
+ const footerHtml = cover.footerText ? `<div class="cover-footer-text">${escapeHtml(cover.footerText)}</div>` : "";
1792
+ const css = `
1793
+ .markforge-cover {
1794
+ min-height: 100vh;
1795
+ box-sizing: border-box;
1796
+ display: flex;
1797
+ flex-direction: column;
1798
+ justify-content: space-between;
1799
+ padding: 4rem 3.5rem;
1800
+ page-break-after: always;
1801
+ break-after: page;
1802
+ position: relative;
1803
+ z-index: 2;
1804
+ background: ${cover.bgGradient || "#FFFFFF"};
1805
+ -webkit-print-color-adjust: exact;
1806
+ print-color-adjust: exact;
1807
+ color: ${cover.textColor || "#0F172A"};
1808
+ }
1809
+ .markforge-cover.cover-modern {
1810
+ border-top: 8px solid #0D998D;
1811
+ }
1812
+ .markforge-cover.cover-corporate-split {
1813
+ border-left: 12px solid #0D998D;
1814
+ }
1815
+ .markforge-cover.cover-card {
1816
+ background: #F8FAFC;
1817
+ }
1818
+ .cover-top {
1819
+ display: flex;
1820
+ justify-content: space-between;
1821
+ align-items: flex-start;
1822
+ width: 100%;
1823
+ }
1824
+ .cover-badge {
1825
+ display: inline-block;
1826
+ padding: 0.35rem 0.85rem;
1827
+ font-size: 0.78rem;
1828
+ font-weight: 700;
1829
+ letter-spacing: 0.08em;
1830
+ text-transform: uppercase;
1831
+ background-color: #ECFDFD;
1832
+ color: #0D998D;
1833
+ border-radius: 4px;
1834
+ border: 1px solid #33CDCF;
1835
+ }
1836
+ .cover-body {
1837
+ margin: auto 0;
1838
+ }
1839
+ .cover-title {
1840
+ font-size: 2.8rem;
1841
+ font-weight: 800;
1842
+ line-height: 1.15;
1843
+ margin: 0 0 1rem 0;
1844
+ color: inherit;
1845
+ }
1846
+ .cover-subtitle {
1847
+ font-size: 1.35rem;
1848
+ font-weight: 400;
1849
+ color: #64748B;
1850
+ margin: 0 0 2rem 0;
1851
+ line-height: 1.4;
1852
+ }
1853
+ .cover-meta {
1854
+ display: flex;
1855
+ flex-direction: column;
1856
+ gap: 0.5rem;
1857
+ border-top: 1.5px solid #E2E8F0;
1858
+ padding-top: 1.5rem;
1859
+ max-width: 480px;
1860
+ }
1861
+ .cover-meta-item {
1862
+ font-size: 0.92rem;
1863
+ display: flex;
1864
+ gap: 0.75rem;
1865
+ }
1866
+ .cover-meta-label {
1867
+ font-weight: 600;
1868
+ color: #64748B;
1869
+ min-width: 110px;
1870
+ }
1871
+ .cover-meta-value {
1872
+ font-weight: 500;
1873
+ color: #0F172A;
1874
+ }
1875
+ .cover-bottom {
1876
+ display: flex;
1877
+ justify-content: space-between;
1878
+ align-items: flex-end;
1879
+ font-size: 0.82rem;
1880
+ color: #94A3B8;
1881
+ }
1882
+ @media print {
1883
+ .markforge-cover {
1884
+ page-break-after: always;
1885
+ break-after: page;
1886
+ height: 100vh;
1887
+ min-height: 100vh;
1888
+ max-height: 100vh;
1889
+ box-sizing: border-box;
1890
+ overflow: hidden;
1891
+ margin: 0;
1892
+ -webkit-print-color-adjust: exact;
1893
+ print-color-adjust: exact;
1894
+ }
1895
+ }
1896
+ `;
1897
+ const html = ` <section class="markforge-cover cover-${cover.preset}">
1898
+ <div class="cover-top">
1899
+ ${logoHtml}
1900
+ ${badgeHtml}
1901
+ </div>
1902
+ <div class="cover-body">
1903
+ ${titleHtml}
1904
+ ${subtitleHtml}
1905
+ ${metaHtml}
1906
+ </div>
1907
+ <div class="cover-bottom">
1908
+ ${footerHtml}
1909
+ </div>
1910
+ </section>
1911
+ `;
1912
+ return { html, css };
1913
+ }
1914
+ async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
1915
+ let logoHtml = "";
1916
+ if (backCover.logo) {
1917
+ const resolved = await resolveImage(backCover.logo, baseDir);
1918
+ const src = resolved ? resolved.dataUri : backCover.logo;
1919
+ const widthStyle = backCover.logoWidth ? `style="width: ${typeof backCover.logoWidth === "number" ? `${backCover.logoWidth}px` : backCover.logoWidth}; max-width: 100%;"` : `style="max-width: 160px; height: auto;"`;
1920
+ logoHtml = `<div class="back-logo"><img src="${src}" alt="Brand Logo" ${widthStyle} /></div>`;
1921
+ }
1922
+ const badgeHtml = backCover.badge ? `<div class="back-badge" style="${backCover.badgeColor ? `background-color: ${backCover.badgeColor};` : ""}${backCover.badgeTextColor ? `color: ${backCover.badgeTextColor};` : ""}">${escapeHtml(backCover.badge)}</div>` : "";
1923
+ const titleHtml = `<h1 class="back-title">${escapeHtml(backCover.title)}</h1>`;
1924
+ const subtitleHtml = backCover.subtitle ? `<div class="back-subtitle">${escapeHtml(backCover.subtitle)}</div>` : "";
1925
+ const contactItems = [];
1926
+ if (backCover.company) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Organization:</span> <span class="back-contact-value">${escapeHtml(backCover.company)}</span></div>`);
1927
+ if (backCover.address) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Address:</span> <span class="back-contact-value">${escapeHtml(backCover.address)}</span></div>`);
1928
+ if (backCover.email) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Email:</span> <a href="mailto:${escapeHtml(backCover.email)}" class="back-contact-link">${escapeHtml(backCover.email)}</a></div>`);
1929
+ if (backCover.phone) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Phone:</span> <span class="back-contact-value">${escapeHtml(backCover.phone)}</span></div>`);
1930
+ if (backCover.website) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Website:</span> <a href="${escapeHtml(backCover.website)}" target="_blank" class="back-contact-link">${escapeHtml(backCover.website)}</a></div>`);
1931
+ if (backCover.social) {
1932
+ for (const [network, url] of Object.entries(backCover.social)) {
1933
+ if (url) {
1934
+ contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">${escapeHtml(network.toUpperCase())}:</span> <a href="${escapeHtml(url)}" target="_blank" class="back-contact-link">${escapeHtml(url)}</a></div>`);
1935
+ }
1936
+ }
1937
+ }
1938
+ const contactHtml = contactItems.length > 0 ? `<div class="back-contact-grid">${contactItems.join("\n")}</div>` : "";
1939
+ const copyrightHtml = backCover.copyright ? `<div class="back-copyright">${escapeHtml(backCover.copyright)}</div>` : "";
1940
+ const isDark = backCover.preset === "corporate";
1941
+ const css = `
1942
+ .markforge-back-cover {
1943
+ min-height: 100vh;
1944
+ box-sizing: border-box;
1945
+ display: flex;
1946
+ flex-direction: column;
1947
+ justify-content: space-between;
1948
+ padding: 4rem 3.5rem;
1949
+ page-break-before: always;
1950
+ break-before: page;
1951
+ position: relative;
1952
+ z-index: 2;
1953
+ background: ${backCover.bgGradient || (isDark ? "#0F172A" : "#FFFFFF")};
1954
+ color: ${backCover.textColor || (isDark ? "#F8FAFC" : "#0F172A")};
1955
+ }
1956
+ .markforge-back-cover.back-modern {
1957
+ border-bottom: 8px solid #0D998D;
1958
+ }
1959
+ .markforge-back-cover.back-corporate {
1960
+ border-left: 12px solid #33CDCF;
1961
+ }
1962
+ .markforge-back-cover.back-card {
1963
+ background: #F8FAFC;
1964
+ }
1965
+ .back-top {
1966
+ display: flex;
1967
+ justify-content: space-between;
1968
+ align-items: flex-start;
1969
+ width: 100%;
1970
+ }
1971
+ .back-badge {
1972
+ display: inline-block;
1973
+ padding: 0.35rem 0.85rem;
1974
+ font-size: 0.78rem;
1975
+ font-weight: 700;
1976
+ letter-spacing: 0.08em;
1977
+ text-transform: uppercase;
1978
+ background-color: #ECFDFD;
1979
+ color: #0D998D;
1980
+ border-radius: 4px;
1981
+ border: 1px solid #33CDCF;
1982
+ }
1983
+ .back-body {
1984
+ margin: auto 0;
1985
+ }
1986
+ .back-title {
1987
+ font-size: 2.6rem;
1988
+ font-weight: 800;
1989
+ line-height: 1.15;
1990
+ margin: 0 0 0.75rem 0;
1991
+ color: inherit;
1992
+ }
1993
+ .back-subtitle {
1994
+ font-size: 1.25rem;
1995
+ font-weight: 400;
1996
+ color: ${isDark ? "#94A3B8" : "#64748B"};
1997
+ margin: 0 0 2rem 0;
1998
+ line-height: 1.4;
1999
+ }
2000
+ .back-contact-grid {
2001
+ display: flex;
2002
+ flex-direction: column;
2003
+ gap: 0.6rem;
2004
+ border-top: 1.5px solid ${isDark ? "#334155" : "#E2E8F0"};
2005
+ padding-top: 1.5rem;
2006
+ max-width: 540px;
2007
+ }
2008
+ .back-contact-item {
2009
+ font-size: 0.92rem;
2010
+ display: flex;
2011
+ gap: 0.75rem;
2012
+ }
2013
+ .back-contact-label {
2014
+ font-weight: 600;
2015
+ color: ${isDark ? "#94A3B8" : "#64748B"};
2016
+ min-width: 110px;
2017
+ }
2018
+ .back-contact-link {
2019
+ color: #0D998D;
2020
+ text-decoration: none;
2021
+ font-weight: 600;
2022
+ }
2023
+ .back-contact-link:hover {
2024
+ text-decoration: underline;
2025
+ }
2026
+ .back-copyright {
2027
+ font-size: 0.82rem;
2028
+ color: ${isDark ? "#64748B" : "#94A3B8"};
2029
+ border-top: 1px solid ${isDark ? "#1E293B" : "#F1F5F9"};
2030
+ padding-top: 1rem;
2031
+ margin-top: 2rem;
2032
+ }
2033
+ @media print {
2034
+ .markforge-back-cover {
2035
+ page: back-cover-page;
2036
+ page-break-before: always;
2037
+ break-before: page;
2038
+ page-break-after: avoid;
2039
+ break-after: avoid;
2040
+ min-height: 100vh;
2041
+ height: 100vh;
2042
+ max-height: 100vh;
2043
+ margin: 0;
2044
+ box-sizing: border-box;
2045
+ overflow: hidden;
2046
+ -webkit-print-color-adjust: exact;
2047
+ print-color-adjust: exact;
2048
+ }
2049
+ }
2050
+ `;
2051
+ const html = ` <section class="markforge-back-cover back-${backCover.preset}">
2052
+ <div class="back-top">
2053
+ ${logoHtml}
2054
+ ${badgeHtml}
2055
+ </div>
2056
+ <div class="back-body">
2057
+ ${titleHtml}
2058
+ ${subtitleHtml}
2059
+ ${contactHtml}
2060
+ </div>
2061
+ ${copyrightHtml}
2062
+ </section>
2063
+ `;
2064
+ return { html, css };
2065
+ }
2066
+ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2067
+ var _a;
2068
+ const resolved = resolveDocumentConfig(doc.metadata, config);
2069
+ const baseThemeCss = generateThemeCss(resolved.theme);
2070
+ let customCss = "";
2071
+ for (const cssPath of resolved.css) {
2072
+ const fullCssPath = path2.isAbsolute(cssPath) ? cssPath : path2.resolve(baseDir, cssPath);
2073
+ if (fs2.existsSync(fullCssPath)) {
2074
+ customCss += `
2075
+ /* Custom CSS: ${cssPath} */
2076
+ ` + fs2.readFileSync(fullCssPath, "utf-8");
2077
+ }
2078
+ }
2079
+ const inlinedCss = doc.inlinedStyles.join("\n");
2080
+ const extraCss = `
2081
+ .markforge-columns {
2082
+ display: grid;
2083
+ grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
2084
+ gap: var(--col-gap, 1.5rem);
2085
+ margin: 1.5rem 0;
2086
+ }
2087
+ .markforge-col {
2088
+ min-width: 0;
2089
+ }
2090
+ .markforge-fnref {
2091
+ text-decoration: none;
2092
+ font-size: 0.8em;
2093
+ vertical-align: super;
2094
+ color: #0D998D;
2095
+ font-weight: 700;
2096
+ }
2097
+ .markforge-footnotes {
2098
+ margin-top: 3rem;
2099
+ padding-top: 1rem;
2100
+ font-size: 0.88rem;
2101
+ color: #64748B;
2102
+ }
2103
+ .markforge-footnotes hr {
2104
+ border: 0;
2105
+ border-top: 1px solid #E2E8F0;
2106
+ margin-bottom: 1rem;
2107
+ }
2108
+ .markforge-fn-return {
2109
+ text-decoration: none;
2110
+ color: #0D998D;
2111
+ }
2112
+ .math-block {
2113
+ margin: 1.5rem 0;
2114
+ text-align: center;
2115
+ overflow-x: auto;
2116
+ }
2117
+ `;
2118
+ let coverHtml = "";
2119
+ let coverCss = "";
2120
+ if (resolved.coverPage && resolved.coverPage.enabled) {
2121
+ const coverRes = await renderCoverPageHtml(resolved.coverPage, baseDir);
2122
+ coverHtml = coverRes.html;
2123
+ coverCss = coverRes.css;
2124
+ }
2125
+ let backHtml = "";
2126
+ let backCss = "";
2127
+ if (resolved.backCover && resolved.backCover.enabled) {
2128
+ const backRes = await renderBackCoverHtml(resolved.backCover, baseDir);
2129
+ backHtml = backRes.html;
2130
+ backCss = backRes.css;
2131
+ }
2132
+ let bodyHtml = "";
2133
+ if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
2134
+ bodyHtml += ` <header class="document-header">
2135
+ `;
2136
+ bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
2137
+ `;
2138
+ if (resolved.subtitle) {
2139
+ bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
2140
+ `;
2141
+ }
2142
+ if (resolved.author || resolved.date || resolved.version) {
2143
+ bodyHtml += ` <div class="document-meta">
2144
+ `;
2145
+ if (resolved.author) {
2146
+ bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
2147
+ `;
2148
+ }
2149
+ if (resolved.version) {
2150
+ bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
2151
+ `;
2152
+ }
2153
+ if (resolved.date) {
2154
+ bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
2155
+ `;
2156
+ }
2157
+ bodyHtml += ` </div>
2158
+ `;
2159
+ }
2160
+ bodyHtml += ` </header>
2161
+ `;
2162
+ }
2163
+ if (resolved.toc && doc.tocEntries.length > 0) {
2164
+ bodyHtml += ` <nav class="table-of-contents">
2165
+ `;
2166
+ bodyHtml += ` <h2>Table of Contents</h2>
2167
+ <ul>
2168
+ `;
2169
+ for (const entry of doc.tocEntries) {
2170
+ const indent = " ".repeat(entry.level);
2171
+ bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
2172
+ `;
2173
+ }
2174
+ bodyHtml += ` </ul>
2175
+ </nav>
2176
+ `;
2177
+ }
2178
+ bodyHtml += await renderNodesToHtml(doc.nodes, resolved, baseDir);
2179
+ let footnotesHtml = "";
2180
+ if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
2181
+ let fnListHtml = "";
2182
+ for (const def of doc.footnoteDefs) {
2183
+ const defInner = await renderInlinesToHtml(def.inlines, baseDir);
2184
+ fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">&#8617;</a></li>
2185
+ `;
2186
+ }
2187
+ footnotesHtml = `
2188
+ <footer class="markforge-footnotes">
2189
+ <hr />
2190
+ <ol>
2191
+ ${fnListHtml} </ol>
2192
+ </footer>
2193
+ `;
2194
+ }
1671
2195
  let watermarkCss = "";
1672
2196
  let watermarkHtml = "";
1673
2197
  if (resolved.watermark) {
@@ -1677,12 +2201,16 @@ ${escapeHtml(node.text || "")}
1677
2201
  position: fixed;
1678
2202
  top: 0;
1679
2203
  left: 0;
1680
- width: 100vw;
1681
- height: 100vh;
2204
+ right: 0;
2205
+ bottom: 0;
2206
+ width: 100%;
2207
+ height: 100%;
1682
2208
  pointer-events: none;
1683
2209
  z-index: 0;
1684
2210
  user-select: none;
1685
2211
  -webkit-user-select: none;
2212
+ -webkit-print-color-adjust: exact;
2213
+ print-color-adjust: exact;
1686
2214
  }
1687
2215
  .document-container {
1688
2216
  position: relative;
@@ -1693,8 +2221,12 @@ ${escapeHtml(node.text || "")}
1693
2221
  position: fixed;
1694
2222
  top: 0;
1695
2223
  left: 0;
2224
+ right: 0;
2225
+ bottom: 0;
1696
2226
  width: 100vw;
1697
2227
  height: 100vh;
2228
+ pointer-events: none;
2229
+ z-index: 0;
1698
2230
  -webkit-print-color-adjust: exact;
1699
2231
  print-color-adjust: exact;
1700
2232
  }
@@ -1705,16 +2237,16 @@ ${escapeHtml(node.text || "")}
1705
2237
  (function() {
1706
2238
  try {
1707
2239
  var canvas = document.createElement('canvas');
1708
- var dpr = 3;
1709
- var width = 800;
1710
- var height = 1100;
1711
- canvas.width = Math.round(width * dpr);
1712
- canvas.height = Math.round(height * dpr);
2240
+ var dpr = 2;
2241
+ var width = 1200;
2242
+ var height = 1600;
2243
+ canvas.width = width * dpr;
2244
+ canvas.height = height * dpr;
1713
2245
  var ctx = canvas.getContext('2d');
1714
2246
  if (ctx) {
1715
2247
  ctx.scale(dpr, dpr);
1716
2248
  ctx.translate(width / 2, height / 2);
1717
- ctx.rotate((${wm.rotate} * Math.PI) / 180);
2249
+ ctx.rotate((-Math.abs(${wm.rotate || 45}) * Math.PI) / 180);
1718
2250
  ctx.textAlign = 'center';
1719
2251
  ctx.textBaseline = 'middle';
1720
2252
  ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
@@ -1741,10 +2273,6 @@ ${escapeHtml(node.text || "")}
1741
2273
  if (resolved.signatures && resolved.signatures.items.length > 0) {
1742
2274
  const sig = resolved.signatures;
1743
2275
  const numItems = sig.items.length;
1744
- let justifyCss = "flex-end";
1745
- if (sig.align === "left") justifyCss = "flex-start";
1746
- else if (sig.align === "center") justifyCss = "center";
1747
- else if (sig.align === "space-between") justifyCss = "space-between";
1748
2276
  signaturesCss = `
1749
2277
  .markforge-signatures {
1750
2278
  margin-top: ${sig.spacingBefore};
@@ -1856,6 +2384,10 @@ ${itemCards}
1856
2384
  <style>
1857
2385
  ${THEME_COMPONENTS}
1858
2386
  ${baseThemeCss}
2387
+ ${KATEX_INLINE_CSS}
2388
+ ${extraCss}
2389
+ ${coverCss}
2390
+ ${backCss}
1859
2391
  ${customCss}
1860
2392
  ${inlinedCss}
1861
2393
  ${watermarkCss}
@@ -1863,14 +2395,102 @@ ${signaturesCss}
1863
2395
  </style>
1864
2396
  </head>
1865
2397
  <body>
1866
- ${watermarkHtml} <div class="document-container">
1867
- ${bodyHtml}${signaturesHtml} </div>
2398
+ ${watermarkHtml}${coverHtml} <div class="document-container">
2399
+ ${bodyHtml}${footnotesHtml}${signaturesHtml} </div>
1868
2400
  ${mermaidScript}
1869
- </body>
2401
+ ${backHtml}</body>
1870
2402
  </html>`;
1871
2403
  }
1872
2404
 
1873
2405
  // src/core/pdf/pdfBuilder.ts
2406
+ import * as fs3 from "fs";
2407
+ import * as path3 from "path";
2408
+ import * as os from "os";
2409
+ import { pathToFileURL } from "url";
2410
+ import { spawnSync } from "child_process";
2411
+ import { PDFDocument } from "pdf-lib";
2412
+ import { encryptPDF } from "@pdfsmaller/pdf-encrypt";
2413
+ function escapeXml(str) {
2414
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2415
+ }
2416
+ function generateWatermarkPngBuffer(chromePath, wm) {
2417
+ const tmpHtml = path3.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.html`);
2418
+ const tmpPng = path3.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
2419
+ try {
2420
+ const text = escapeXml(wm.text.toUpperCase());
2421
+ const fontSize = (wm.fontSize || 52) * 1.5;
2422
+ const color = wm.color || "#E11D48";
2423
+ const opacity = wm.opacity !== void 0 ? wm.opacity : 0.12;
2424
+ const rotate = wm.rotate !== void 0 ? wm.rotate : -45;
2425
+ const html = `<!DOCTYPE html>
2426
+ <html>
2427
+ <head>
2428
+ <meta charset="utf-8">
2429
+ <style>
2430
+ html, body {
2431
+ margin: 0;
2432
+ padding: 0;
2433
+ width: 1200px;
2434
+ height: 1600px;
2435
+ background: transparent;
2436
+ overflow: hidden;
2437
+ }
2438
+ .wm-box {
2439
+ width: 1200px;
2440
+ height: 1600px;
2441
+ display: flex;
2442
+ align-items: center;
2443
+ justify-content: center;
2444
+ transform: rotate(${rotate}deg);
2445
+ }
2446
+ .wm-text {
2447
+ font-family: system-ui, -apple-system, sans-serif;
2448
+ font-weight: 900;
2449
+ font-size: ${fontSize}px;
2450
+ color: ${color};
2451
+ opacity: ${opacity};
2452
+ letter-spacing: 0.15em;
2453
+ text-transform: uppercase;
2454
+ white-space: nowrap;
2455
+ }
2456
+ </style>
2457
+ </head>
2458
+ <body>
2459
+ <div class="wm-box"><span class="wm-text">${text}</span></div>
2460
+ </body>
2461
+ </html>`;
2462
+ fs3.writeFileSync(tmpHtml, html, "utf8");
2463
+ const fileUrl = pathToFileURL(tmpHtml).href;
2464
+ const isWin = process.platform === "win32";
2465
+ spawnSync(
2466
+ chromePath,
2467
+ [
2468
+ "--headless=new",
2469
+ "--disable-gpu",
2470
+ "--disable-sync",
2471
+ "--disable-extensions",
2472
+ ...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
2473
+ `--screenshot=${tmpPng}`,
2474
+ "--window-size=1200,1600",
2475
+ "--default-background-color=00000000",
2476
+ fileUrl
2477
+ ],
2478
+ { timeout: 15e3, windowsHide: true }
2479
+ );
2480
+ if (fs3.existsSync(tmpPng) && fs3.statSync(tmpPng).size > 0) {
2481
+ return fs3.readFileSync(tmpPng);
2482
+ }
2483
+ return null;
2484
+ } catch {
2485
+ return null;
2486
+ } finally {
2487
+ try {
2488
+ if (fs3.existsSync(tmpHtml)) fs3.unlinkSync(tmpHtml);
2489
+ if (fs3.existsSync(tmpPng)) fs3.unlinkSync(tmpPng);
2490
+ } catch {
2491
+ }
2492
+ }
2493
+ }
1874
2494
  function findChromeExecutable() {
1875
2495
  if (process.env.CHROME_PATH && fs3.existsSync(process.env.CHROME_PATH)) {
1876
2496
  return process.env.CHROME_PATH;
@@ -1935,7 +2555,7 @@ function findChromeExecutable() {
1935
2555
  return null;
1936
2556
  }
1937
2557
  function injectPagedMediaStyles(html, config, metadata) {
1938
- var _a, _b, _c, _d, _e, _f;
2558
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1939
2559
  const resolved = resolveDocumentConfig(metadata || {}, config);
1940
2560
  const size = resolved.paperSize;
1941
2561
  const orientation = resolved.orientation;
@@ -1979,6 +2599,38 @@ function injectPagedMediaStyles(html, config, metadata) {
1979
2599
  ${fontStyle}
1980
2600
  }`;
1981
2601
  };
2602
+ const coverPageCss = ((_a = resolved.coverPage) == null ? void 0 : _a.enabled) ? `
2603
+ @page :first {
2604
+ margin-top: 0;
2605
+ margin-bottom: 0;
2606
+ margin-left: 0;
2607
+ margin-right: 0;
2608
+ background-image: none !important;
2609
+ @top-left { content: none; }
2610
+ @top-center { content: none; }
2611
+ @top-right { content: none; }
2612
+ @bottom-left { content: none; }
2613
+ @bottom-center { content: none; }
2614
+ @bottom-right { content: none; }
2615
+ }` : "";
2616
+ const backCoverCss = ((_b = resolved.backCover) == null ? void 0 : _b.enabled) ? `
2617
+ @page back-cover-page {
2618
+ size: ${size} ${orientation};
2619
+ margin: 0;
2620
+ background-image: none !important;
2621
+ @top-left { content: none; }
2622
+ @top-center { content: none; }
2623
+ @top-right { content: none; }
2624
+ @bottom-left { content: none; }
2625
+ @bottom-center { content: none; }
2626
+ @bottom-right { content: none; }
2627
+ }
2628
+ .markforge-back-cover {
2629
+ page: back-cover-page;
2630
+ min-height: 100vh;
2631
+ height: 100vh;
2632
+ box-sizing: border-box;
2633
+ }` : "";
1982
2634
  const pagedCss = `
1983
2635
  @page {
1984
2636
  size: ${size} ${orientation};
@@ -1986,15 +2638,18 @@ function injectPagedMediaStyles(html, config, metadata) {
1986
2638
  margin-bottom: ${bottom};
1987
2639
  margin-left: ${left};
1988
2640
  margin-right: ${right};
1989
- ${buildZoneCss("top-left", (_a = resolved.header) == null ? void 0 : _a.left)}
1990
- ${buildZoneCss("top-center", (_b = resolved.header) == null ? void 0 : _b.center)}
1991
- ${buildZoneCss("top-right", (_c = resolved.header) == null ? void 0 : _c.right)}
1992
- ${buildZoneCss("bottom-left", (_d = resolved.footer) == null ? void 0 : _d.left)}
1993
- ${buildZoneCss("bottom-center", (_e = resolved.footer) == null ? void 0 : _e.center)}
1994
- ${buildZoneCss("bottom-right", (_f = resolved.footer) == null ? void 0 : _f.right, true)}
2641
+ ${buildZoneCss("top-left", (_c = resolved.header) == null ? void 0 : _c.left)}
2642
+ ${buildZoneCss("top-center", (_d = resolved.header) == null ? void 0 : _d.center)}
2643
+ ${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
2644
+ ${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
2645
+ ${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
2646
+ ${buildZoneCss("bottom-right", (_h = resolved.footer) == null ? void 0 : _h.right, true)}
1995
2647
  }
2648
+ ${coverPageCss}
2649
+ ${backCoverCss}
1996
2650
  @media print {
1997
2651
  body { padding: 0; }
2652
+ .document-watermark { display: none !important; }
1998
2653
  h1, h2, h3, pre, table, blockquote, .callout {
1999
2654
  break-inside: avoid;
2000
2655
  }
@@ -2042,15 +2697,17 @@ startxref
2042
2697
  return Buffer.from(pdfBody, "utf-8");
2043
2698
  }
2044
2699
  async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2700
+ var _a, _b, _c;
2045
2701
  const baseHtml = await buildHtmlDocument(doc, config, baseDir);
2046
2702
  const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
2047
2703
  const chromePath = findChromeExecutable();
2048
2704
  if (chromePath) {
2049
2705
  const tmpId = Math.random().toString(36).substring(2, 9);
2050
2706
  const tmpDir = os.tmpdir();
2051
- const tmpHtml = path5.join(tmpDir, `markforge_${tmpId}.html`);
2052
- const tmpPdf = path5.join(tmpDir, `markforge_${tmpId}.pdf`);
2053
- const tmpProfile = path5.join(tmpDir, `markforge_prof_${tmpId}`);
2707
+ const tmpHtml = path3.join(tmpDir, `markforge_${tmpId}.html`);
2708
+ const tmpPdf = path3.join(tmpDir, `markforge_${tmpId}.pdf`);
2709
+ const tmpProfile = path3.join(tmpDir, `markforge_prof_${tmpId}`);
2710
+ const isWin = process.platform === "win32";
2054
2711
  const isolatedFlags = [
2055
2712
  `--user-data-dir=${tmpProfile}`,
2056
2713
  "--no-first-run",
@@ -2061,7 +2718,6 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2061
2718
  "--disable-default-apps",
2062
2719
  "--disable-extensions",
2063
2720
  "--disable-domain-reliability",
2064
- "--disable-client-side-phishing-detection",
2065
2721
  "--disable-breakpad",
2066
2722
  "--disable-component-extensions-with-background-pages",
2067
2723
  "--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
@@ -2070,12 +2726,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2070
2726
  "--mute-audio",
2071
2727
  "--no-service-autorun",
2072
2728
  "--disable-gpu",
2073
- "--no-sandbox",
2074
- "--disable-setuid-sandbox",
2075
- "--allow-file-access-from-files",
2076
- "--disable-web-security",
2729
+ ...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
2077
2730
  "--force-color-profile=srgb",
2078
- "--no-pdf-header-footer"
2731
+ "--no-pdf-header-footer",
2732
+ "--window-size=1200,1600"
2079
2733
  ];
2080
2734
  try {
2081
2735
  fs3.writeFileSync(tmpHtml, pagedHtml, "utf-8");
@@ -2090,7 +2744,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2090
2744
  `--print-to-pdf=${tmpPdf}`,
2091
2745
  fileUrl
2092
2746
  ],
2093
- { timeout: 3e4 }
2747
+ { timeout: 3e4, windowsHide: true }
2094
2748
  );
2095
2749
  if ((res.status !== 0 || !fs3.existsSync(tmpPdf)) && chromePath) {
2096
2750
  res = spawnSync(
@@ -2101,12 +2755,78 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2101
2755
  `--print-to-pdf=${tmpPdf}`,
2102
2756
  fileUrl
2103
2757
  ],
2104
- { timeout: 3e4 }
2758
+ { timeout: 3e4, windowsHide: true }
2105
2759
  );
2106
2760
  }
2107
2761
  if (fs3.existsSync(tmpPdf) && fs3.statSync(tmpPdf).size > 0) {
2108
2762
  const pdfBuffer = fs3.readFileSync(tmpPdf);
2109
- return pdfBuffer;
2763
+ try {
2764
+ const pdfDoc = await PDFDocument.load(pdfBuffer);
2765
+ const resolved = resolveDocumentConfig(doc.metadata, config);
2766
+ if (resolved.title) pdfDoc.setTitle(resolved.title);
2767
+ if (resolved.author) pdfDoc.setAuthor(resolved.author);
2768
+ if (resolved.subtitle) pdfDoc.setSubject(resolved.subtitle);
2769
+ pdfDoc.setCreator("MarkForge Enterprise Document Generator");
2770
+ pdfDoc.setProducer("MarkForge (by Ma'sum)");
2771
+ pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
2772
+ if (((_a = resolved.backCover) == null ? void 0 : _a.enabled) && pdfDoc.getPageCount() > 2) {
2773
+ pdfDoc.removePage(pdfDoc.getPageCount() - 1);
2774
+ }
2775
+ if (resolved.watermark) {
2776
+ const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
2777
+ if (wmPng) {
2778
+ const embeddedPng = await pdfDoc.embedPng(wmPng);
2779
+ const pages = pdfDoc.getPages();
2780
+ const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
2781
+ const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? pages.length - 1 : pages.length;
2782
+ for (let i = startPageIndex; i < endPageIndex; i++) {
2783
+ const page = pages[i];
2784
+ const { width, height } = page.getSize();
2785
+ page.drawImage(embeddedPng, {
2786
+ x: 0,
2787
+ y: 0,
2788
+ width,
2789
+ height
2790
+ });
2791
+ }
2792
+ }
2793
+ }
2794
+ const savedBytes = await pdfDoc.save();
2795
+ let finalBuffer = Buffer.from(savedBytes);
2796
+ if (resolved.security) {
2797
+ const sec = resolved.security;
2798
+ const hasUserPassword = typeof sec.userPassword === "string" && sec.userPassword.length > 0;
2799
+ const hasOwnerPassword = typeof sec.ownerPassword === "string" && sec.ownerPassword.length > 0;
2800
+ if (hasUserPassword || hasOwnerPassword) {
2801
+ try {
2802
+ const userPass = sec.userPassword ?? "";
2803
+ const ownerPass = sec.ownerPassword ?? userPass;
2804
+ const perms = sec.permissions;
2805
+ const encryptedBytes = await encryptPDF(
2806
+ new Uint8Array(finalBuffer),
2807
+ userPass,
2808
+ {
2809
+ ownerPassword: ownerPass,
2810
+ algorithm: "AES-256",
2811
+ allowPrinting: (perms == null ? void 0 : perms.printing) !== "none",
2812
+ allowHighQualityPrint: (perms == null ? void 0 : perms.printing) === "highResolution",
2813
+ allowModifying: (perms == null ? void 0 : perms.modifying) ?? true,
2814
+ allowCopying: (perms == null ? void 0 : perms.copying) ?? true,
2815
+ allowAnnotating: (perms == null ? void 0 : perms.annotating) ?? true,
2816
+ allowFillingForms: (perms == null ? void 0 : perms.fillingForms) ?? true,
2817
+ allowExtraction: (perms == null ? void 0 : perms.contentAccessibility) ?? true,
2818
+ allowAssembly: (perms == null ? void 0 : perms.documentAssembly) ?? true
2819
+ }
2820
+ );
2821
+ finalBuffer = Buffer.from(encryptedBytes);
2822
+ } catch {
2823
+ }
2824
+ }
2825
+ }
2826
+ return finalBuffer;
2827
+ } catch {
2828
+ return pdfBuffer;
2829
+ }
2110
2830
  }
2111
2831
  } catch {
2112
2832
  } finally {
@@ -2121,7 +2841,37 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2121
2841
  return createFallbackPdfBuffer(doc.metadata.title || "MarkForge Document");
2122
2842
  }
2123
2843
 
2844
+ // src/core/docx/docxBuilder.ts
2845
+ import {
2846
+ Document,
2847
+ Paragraph,
2848
+ TextRun,
2849
+ HeadingLevel,
2850
+ Table,
2851
+ TableRow,
2852
+ TableCell,
2853
+ WidthType,
2854
+ BorderStyle,
2855
+ AlignmentType,
2856
+ ImageRun,
2857
+ Header,
2858
+ Footer,
2859
+ PageNumber,
2860
+ Packer,
2861
+ PageOrientation,
2862
+ convertInchesToTwip,
2863
+ convertMillimetersToTwip,
2864
+ ShadingType,
2865
+ ExternalHyperlink,
2866
+ TabStopType
2867
+ } from "docx";
2868
+
2124
2869
  // src/core/mermaid/mermaidRenderer.ts
2870
+ import { spawnSync as spawnSync2 } from "child_process";
2871
+ import * as fs4 from "fs";
2872
+ import * as os2 from "os";
2873
+ import * as path4 from "path";
2874
+ import { pathToFileURL as pathToFileURL2 } from "url";
2125
2875
  async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
2126
2876
  const chromePath = findChromeExecutable();
2127
2877
  if (!chromePath) {
@@ -2129,8 +2879,8 @@ async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
2129
2879
  }
2130
2880
  const tmpId = Math.random().toString(36).substring(2, 9);
2131
2881
  const tmpDir = os2.tmpdir();
2132
- const tmpHtml = path6.join(tmpDir, `mermaid_${tmpId}.html`);
2133
- const tmpScreenshot = path6.join(tmpDir, `mermaid_${tmpId}.png`);
2882
+ const tmpHtml = path4.join(tmpDir, `mermaid_${tmpId}.html`);
2883
+ const tmpScreenshot = path4.join(tmpDir, `mermaid_${tmpId}.png`);
2134
2884
  const htmlContent = `<!DOCTYPE html>
2135
2885
  <html>
2136
2886
  <head>
@@ -2338,6 +3088,29 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
2338
3088
  );
2339
3089
  continue;
2340
3090
  }
3091
+ if (span.type === "mathInline") {
3092
+ runs.push(
3093
+ new TextRun({
3094
+ text: span.content,
3095
+ font: "Cambria Math",
3096
+ italics: true,
3097
+ size: options.size,
3098
+ color: options.color || "0F172A"
3099
+ })
3100
+ );
3101
+ continue;
3102
+ }
3103
+ if (span.type === "footnoteRef") {
3104
+ runs.push(
3105
+ new TextRun({
3106
+ text: `[${span.content}]`,
3107
+ superScript: true,
3108
+ color: "009DA0",
3109
+ bold: true
3110
+ })
3111
+ );
3112
+ continue;
3113
+ }
2341
3114
  runs.push(
2342
3115
  new TextRun({
2343
3116
  text: span.content,
@@ -2352,7 +3125,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
2352
3125
  return runs;
2353
3126
  }
2354
3127
  async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2355
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
3128
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2356
3129
  const resolved = resolveDocumentConfig(doc.metadata, config);
2357
3130
  const docElements = [];
2358
3131
  const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
@@ -2363,7 +3136,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2363
3136
  const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
2364
3137
  const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
2365
3138
  const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
2366
- if (resolved.title) {
3139
+ if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
2367
3140
  docElements.push(
2368
3141
  new Paragraph({
2369
3142
  children: [
@@ -2779,7 +3552,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2779
3552
  }
2780
3553
  if (node.type === "table" && node.children) {
2781
3554
  const tableRows = [];
2782
- const numCols = ((_b = (_a = node.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) || 1;
3555
+ const numCols = ((_c = (_b = node.children[0]) == null ? void 0 : _b.children) == null ? void 0 : _c.length) || 1;
2783
3556
  const colWidth = Math.floor(9e3 / numCols);
2784
3557
  for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
2785
3558
  const rowNode = node.children[rowIdx];
@@ -2789,7 +3562,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2789
3562
  if (rowNode.children) {
2790
3563
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
2791
3564
  const cellNode = rowNode.children[colIdx];
2792
- const align = (_c = node.align) == null ? void 0 : _c[colIdx];
3565
+ const align = (_d = node.align) == null ? void 0 : _d[colIdx];
2793
3566
  let alignment = AlignmentType.LEFT;
2794
3567
  if (align === "center") alignment = AlignmentType.CENTER;
2795
3568
  if (align === "right") alignment = AlignmentType.RIGHT;
@@ -2928,6 +3701,113 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2928
3701
  }
2929
3702
  continue;
2930
3703
  }
3704
+ if (node.type === "mathBlock") {
3705
+ docElements.push(
3706
+ new Paragraph({
3707
+ alignment: AlignmentType.CENTER,
3708
+ children: [
3709
+ new TextRun({
3710
+ text: node.text || "",
3711
+ font: "Cambria Math",
3712
+ italics: true,
3713
+ size: 24,
3714
+ // 12pt
3715
+ color: textHex
3716
+ })
3717
+ ],
3718
+ spacing: { before: 180, after: 180 },
3719
+ shading: { fill: cardBgHex, type: ShadingType.CLEAR },
3720
+ border: {
3721
+ top: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
3722
+ bottom: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
3723
+ left: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
3724
+ right: { style: BorderStyle.SINGLE, size: 4, color: borderHex }
3725
+ }
3726
+ })
3727
+ );
3728
+ continue;
3729
+ }
3730
+ if (node.type === "columns") {
3731
+ const cols = node.columnsCount || 2;
3732
+ const contentWidth = Math.max(
3733
+ 1e3,
3734
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
3735
+ );
3736
+ const cellWidthDxa = Math.floor(contentWidth / cols);
3737
+ const cells = [];
3738
+ for (const col of node.children || []) {
3739
+ const colParagraphs = [];
3740
+ for (const childNode of col.children || []) {
3741
+ if (childNode.type === "heading") {
3742
+ const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, bold: true, size: 24, color: primaryDarkHex });
3743
+ colParagraphs.push(new Paragraph({ children: runs, spacing: { before: 120, after: 60 } }));
3744
+ } else if (childNode.type === "paragraph") {
3745
+ const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
3746
+ colParagraphs.push(new Paragraph({ children: runs, spacing: { after: 100 } }));
3747
+ } else if (childNode.type === "list" && childNode.children) {
3748
+ for (const item of childNode.children) {
3749
+ const runs = await convertInlinesToTextRuns(item.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
3750
+ colParagraphs.push(new Paragraph({ children: [new TextRun({ text: "\u2022 ", font: defaultFont, color: primaryHex }), ...runs], spacing: { after: 40 } }));
3751
+ }
3752
+ }
3753
+ }
3754
+ if (colParagraphs.length === 0) colParagraphs.push(new Paragraph({}));
3755
+ cells.push(
3756
+ new TableCell({
3757
+ width: { size: cellWidthDxa, type: WidthType.DXA },
3758
+ borders: {
3759
+ top: { style: BorderStyle.NONE, size: 0, color: "auto" },
3760
+ bottom: { style: BorderStyle.NONE, size: 0, color: "auto" },
3761
+ left: { style: BorderStyle.NONE, size: 0, color: "auto" },
3762
+ right: { style: BorderStyle.NONE, size: 0, color: "auto" }
3763
+ },
3764
+ margins: { top: 60, bottom: 60, left: 100, right: 100 },
3765
+ children: colParagraphs
3766
+ })
3767
+ );
3768
+ }
3769
+ docElements.push(
3770
+ new Table({
3771
+ width: { size: 100, type: WidthType.PERCENTAGE },
3772
+ rows: [new TableRow({ children: cells })]
3773
+ })
3774
+ );
3775
+ docElements.push(new Paragraph({ spacing: { after: 120 } }));
3776
+ continue;
3777
+ }
3778
+ }
3779
+ if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
3780
+ docElements.push(
3781
+ new Paragraph({
3782
+ border: {
3783
+ top: { style: BorderStyle.SINGLE, size: 4, color: borderHex, space: 8 }
3784
+ },
3785
+ spacing: { before: 360, after: 120 }
3786
+ })
3787
+ );
3788
+ for (const def of doc.footnoteDefs) {
3789
+ const defRuns = await convertInlinesToTextRuns(def.inlines, baseDir, {
3790
+ font: defaultFont,
3791
+ size: 18,
3792
+ // 9pt
3793
+ color: textMutedHex
3794
+ });
3795
+ docElements.push(
3796
+ new Paragraph({
3797
+ children: [
3798
+ new TextRun({
3799
+ text: `[${def.id}] `,
3800
+ bold: true,
3801
+ color: primaryDarkHex,
3802
+ font: defaultFont,
3803
+ size: 18
3804
+ }),
3805
+ ...defRuns
3806
+ ],
3807
+ spacing: { after: 60 }
3808
+ })
3809
+ );
3810
+ }
2931
3811
  }
2932
3812
  if (resolved.signatures && resolved.signatures.items.length > 0) {
2933
3813
  const sig = resolved.signatures;
@@ -2989,7 +3869,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2989
3869
  const centerPos = Math.round(contentWidthTwip / 2);
2990
3870
  const rightPos = contentWidthTwip;
2991
3871
  const headerRuns = [];
2992
- if ((_d = resolved.header) == null ? void 0 : _d.left) {
3872
+ if ((_e = resolved.header) == null ? void 0 : _e.left) {
2993
3873
  headerRuns.push(
2994
3874
  new TextRun({
2995
3875
  text: resolved.header.left.text,
@@ -3002,7 +3882,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3002
3882
  );
3003
3883
  }
3004
3884
  headerRuns.push(new TextRun({ text: " " }));
3005
- if ((_e = resolved.header) == null ? void 0 : _e.center) {
3885
+ if ((_f = resolved.header) == null ? void 0 : _f.center) {
3006
3886
  headerRuns.push(
3007
3887
  new TextRun({
3008
3888
  text: resolved.header.center.text,
@@ -3015,7 +3895,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3015
3895
  );
3016
3896
  }
3017
3897
  headerRuns.push(new TextRun({ text: " " }));
3018
- if ((_f = resolved.header) == null ? void 0 : _f.right) {
3898
+ if ((_g = resolved.header) == null ? void 0 : _g.right) {
3019
3899
  headerRuns.push(
3020
3900
  new TextRun({
3021
3901
  text: resolved.header.right.text,
@@ -3027,7 +3907,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3027
3907
  })
3028
3908
  );
3029
3909
  }
3030
- const docHeader = resolved.header ? new Header2({
3910
+ const docHeader = resolved.header ? new Header({
3031
3911
  children: [
3032
3912
  new Paragraph({
3033
3913
  tabStops: [
@@ -3054,7 +3934,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3054
3934
  ]
3055
3935
  }) : void 0;
3056
3936
  const footerRuns = [];
3057
- if ((_g = resolved.footer) == null ? void 0 : _g.left) {
3937
+ if ((_h = resolved.footer) == null ? void 0 : _h.left) {
3058
3938
  footerRuns.push(
3059
3939
  new TextRun({
3060
3940
  text: resolved.footer.left.text,
@@ -3067,7 +3947,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3067
3947
  );
3068
3948
  }
3069
3949
  footerRuns.push(new TextRun({ text: " " }));
3070
- if ((_h = resolved.footer) == null ? void 0 : _h.center) {
3950
+ if ((_i = resolved.footer) == null ? void 0 : _i.center) {
3071
3951
  footerRuns.push(
3072
3952
  new TextRun({
3073
3953
  text: resolved.footer.center.text,
@@ -3080,7 +3960,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3080
3960
  );
3081
3961
  }
3082
3962
  footerRuns.push(new TextRun({ text: " " }));
3083
- if ((_i = resolved.footer) == null ? void 0 : _i.right) {
3963
+ if ((_j = resolved.footer) == null ? void 0 : _j.right) {
3084
3964
  const rZone = resolved.footer.right;
3085
3965
  const rColor = rZone.color.replace("#", "");
3086
3966
  const rSize = (rZone.fontSize || 9) * 2;
@@ -3165,6 +4045,91 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3165
4045
  ]
3166
4046
  }) : void 0;
3167
4047
  const isLandscape = resolved.orientation === "landscape";
4048
+ const docSections = [];
4049
+ if (resolved.coverPage && resolved.coverPage.enabled) {
4050
+ const coverElements = await buildDocxCoverPageElements(
4051
+ resolved.coverPage,
4052
+ defaultFont,
4053
+ textHex,
4054
+ primaryHex,
4055
+ primaryDarkHex,
4056
+ textMutedHex,
4057
+ baseDir
4058
+ );
4059
+ docSections.push({
4060
+ properties: {
4061
+ page: {
4062
+ size: {
4063
+ width: resolved.paperDimensions.widthTwip,
4064
+ height: resolved.paperDimensions.heightTwip,
4065
+ orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
4066
+ },
4067
+ margin: {
4068
+ top: resolved.margins.topTwip,
4069
+ bottom: resolved.margins.bottomTwip,
4070
+ left: resolved.margins.leftTwip,
4071
+ right: resolved.margins.rightTwip
4072
+ }
4073
+ }
4074
+ },
4075
+ headers: void 0,
4076
+ footers: void 0,
4077
+ children: coverElements
4078
+ });
4079
+ }
4080
+ docSections.push({
4081
+ properties: {
4082
+ page: {
4083
+ size: {
4084
+ width: resolved.paperDimensions.widthTwip,
4085
+ height: resolved.paperDimensions.heightTwip,
4086
+ orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
4087
+ },
4088
+ margin: {
4089
+ top: resolved.margins.topTwip,
4090
+ bottom: resolved.margins.bottomTwip,
4091
+ left: resolved.margins.leftTwip,
4092
+ right: resolved.margins.rightTwip,
4093
+ header: 720,
4094
+ footer: 720
4095
+ }
4096
+ }
4097
+ },
4098
+ headers: docHeader ? { default: docHeader } : void 0,
4099
+ footers: docFooter ? { default: docFooter } : void 0,
4100
+ children: docElements
4101
+ });
4102
+ if (resolved.backCover && resolved.backCover.enabled) {
4103
+ const backElements = await buildDocxBackCoverElements(
4104
+ resolved.backCover,
4105
+ defaultFont,
4106
+ textHex,
4107
+ primaryHex,
4108
+ primaryDarkHex,
4109
+ textMutedHex,
4110
+ baseDir
4111
+ );
4112
+ docSections.push({
4113
+ properties: {
4114
+ page: {
4115
+ size: {
4116
+ width: resolved.paperDimensions.widthTwip,
4117
+ height: resolved.paperDimensions.heightTwip,
4118
+ orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
4119
+ },
4120
+ margin: {
4121
+ top: resolved.margins.topTwip,
4122
+ bottom: resolved.margins.bottomTwip,
4123
+ left: resolved.margins.leftTwip,
4124
+ right: resolved.margins.rightTwip
4125
+ }
4126
+ }
4127
+ },
4128
+ headers: void 0,
4129
+ footers: void 0,
4130
+ children: backElements
4131
+ });
4132
+ }
3168
4133
  const document = new Document({
3169
4134
  styles: {
3170
4135
  default: {
@@ -3185,33 +4150,250 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3185
4150
  }
3186
4151
  }
3187
4152
  },
3188
- sections: [
3189
- {
3190
- properties: {
3191
- page: {
3192
- size: {
3193
- width: resolved.paperDimensions.widthTwip,
3194
- height: resolved.paperDimensions.heightTwip,
3195
- orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
3196
- },
3197
- margin: {
3198
- top: resolved.margins.topTwip,
3199
- bottom: resolved.margins.bottomTwip,
3200
- left: resolved.margins.leftTwip,
3201
- right: resolved.margins.rightTwip,
3202
- header: 720,
3203
- footer: 720
3204
- }
3205
- }
3206
- },
3207
- headers: docHeader ? { default: docHeader } : void 0,
3208
- footers: docFooter ? { default: docFooter } : void 0,
3209
- children: docElements
3210
- }
3211
- ]
4153
+ sections: docSections
3212
4154
  });
3213
4155
  return await Packer.toBuffer(document);
3214
4156
  }
4157
+ async function buildDocxBackCoverElements(backCover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
4158
+ var _a;
4159
+ const elements = [];
4160
+ elements.push(new Paragraph({ spacing: { before: 1800 } }));
4161
+ if (backCover.logo) {
4162
+ const resolvedLogo = await resolveImage(backCover.logo, baseDir);
4163
+ if (resolvedLogo) {
4164
+ const logoW = typeof backCover.logoWidth === "number" ? backCover.logoWidth : 140;
4165
+ const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
4166
+ elements.push(
4167
+ new Paragraph({
4168
+ children: [
4169
+ new ImageRun({
4170
+ data: resolvedLogo.buffer,
4171
+ transformation: {
4172
+ width: logoW,
4173
+ height: Math.round(logoW * 0.75)
4174
+ },
4175
+ type: logoType
4176
+ })
4177
+ ],
4178
+ spacing: { after: 240 }
4179
+ })
4180
+ );
4181
+ }
4182
+ }
4183
+ if (backCover.badge) {
4184
+ elements.push(
4185
+ new Paragraph({
4186
+ children: [
4187
+ new TextRun({
4188
+ text: `[ ${backCover.badge.toUpperCase()} ]`,
4189
+ font: defaultFont,
4190
+ size: 20,
4191
+ bold: true,
4192
+ color: primaryDarkHex
4193
+ })
4194
+ ],
4195
+ spacing: { after: 240 }
4196
+ })
4197
+ );
4198
+ }
4199
+ elements.push(
4200
+ new Paragraph({
4201
+ children: [
4202
+ new TextRun({
4203
+ text: backCover.title,
4204
+ font: defaultFont,
4205
+ size: 52,
4206
+ // 26pt
4207
+ bold: true,
4208
+ color: textHex
4209
+ })
4210
+ ],
4211
+ spacing: { after: 140 }
4212
+ })
4213
+ );
4214
+ if (backCover.subtitle) {
4215
+ elements.push(
4216
+ new Paragraph({
4217
+ children: [
4218
+ new TextRun({
4219
+ text: backCover.subtitle,
4220
+ font: defaultFont,
4221
+ size: 24,
4222
+ // 12pt
4223
+ color: textMutedHex
4224
+ })
4225
+ ],
4226
+ spacing: { after: 480 }
4227
+ })
4228
+ );
4229
+ }
4230
+ elements.push(
4231
+ new Paragraph({
4232
+ border: {
4233
+ bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
4234
+ },
4235
+ spacing: { after: 480 }
4236
+ })
4237
+ );
4238
+ const contactRuns = [];
4239
+ if (backCover.company) contactRuns.push(new TextRun({ text: `Organization: ${backCover.company}
4240
+ `, font: defaultFont, size: 21, color: textHex }));
4241
+ if (backCover.address) contactRuns.push(new TextRun({ text: `Address: ${backCover.address}
4242
+ `, font: defaultFont, size: 21, color: textHex }));
4243
+ if (backCover.email) contactRuns.push(new TextRun({ text: `Email: ${backCover.email}
4244
+ `, font: defaultFont, size: 21, color: textHex }));
4245
+ if (backCover.phone) contactRuns.push(new TextRun({ text: `Phone: ${backCover.phone}
4246
+ `, font: defaultFont, size: 21, color: textHex }));
4247
+ if (backCover.website) contactRuns.push(new TextRun({ text: `Website: ${backCover.website}
4248
+ `, font: defaultFont, size: 21, color: textHex }));
4249
+ if (backCover.social) {
4250
+ for (const [net, url] of Object.entries(backCover.social)) {
4251
+ if (url) {
4252
+ contactRuns.push(new TextRun({ text: `${net.toUpperCase()}: ${url}
4253
+ `, font: defaultFont, size: 21, color: textHex }));
4254
+ }
4255
+ }
4256
+ }
4257
+ if (contactRuns.length > 0) {
4258
+ elements.push(
4259
+ new Paragraph({
4260
+ children: contactRuns,
4261
+ spacing: { before: 360, after: 360 }
4262
+ })
4263
+ );
4264
+ }
4265
+ if (backCover.copyright) {
4266
+ elements.push(
4267
+ new Paragraph({
4268
+ children: [
4269
+ new TextRun({
4270
+ text: backCover.copyright,
4271
+ font: defaultFont,
4272
+ size: 18,
4273
+ color: "94A3B8"
4274
+ })
4275
+ ],
4276
+ spacing: { before: 720 }
4277
+ })
4278
+ );
4279
+ }
4280
+ return elements;
4281
+ }
4282
+ async function buildDocxCoverPageElements(cover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
4283
+ var _a;
4284
+ const elements = [];
4285
+ elements.push(new Paragraph({ spacing: { before: 1800 } }));
4286
+ if (cover.logo) {
4287
+ const resolvedLogo = await resolveImage(cover.logo, baseDir);
4288
+ if (resolvedLogo) {
4289
+ const logoW = typeof cover.logoWidth === "number" ? cover.logoWidth : 140;
4290
+ const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
4291
+ elements.push(
4292
+ new Paragraph({
4293
+ children: [
4294
+ new ImageRun({
4295
+ data: resolvedLogo.buffer,
4296
+ transformation: {
4297
+ width: logoW,
4298
+ height: Math.round(logoW * 0.75)
4299
+ },
4300
+ type: logoType
4301
+ })
4302
+ ],
4303
+ spacing: { after: 240 }
4304
+ })
4305
+ );
4306
+ }
4307
+ }
4308
+ if (cover.badge) {
4309
+ elements.push(
4310
+ new Paragraph({
4311
+ children: [
4312
+ new TextRun({
4313
+ text: `[ ${cover.badge.toUpperCase()} ]`,
4314
+ font: defaultFont,
4315
+ size: 20,
4316
+ bold: true,
4317
+ color: primaryDarkHex
4318
+ })
4319
+ ],
4320
+ spacing: { after: 240 }
4321
+ })
4322
+ );
4323
+ }
4324
+ elements.push(
4325
+ new Paragraph({
4326
+ children: [
4327
+ new TextRun({
4328
+ text: cover.title,
4329
+ font: defaultFont,
4330
+ size: 56,
4331
+ // 28pt
4332
+ bold: true,
4333
+ color: textHex
4334
+ })
4335
+ ],
4336
+ spacing: { after: 140 }
4337
+ })
4338
+ );
4339
+ if (cover.subtitle) {
4340
+ elements.push(
4341
+ new Paragraph({
4342
+ children: [
4343
+ new TextRun({
4344
+ text: cover.subtitle,
4345
+ font: defaultFont,
4346
+ size: 26,
4347
+ // 13pt
4348
+ color: textMutedHex
4349
+ })
4350
+ ],
4351
+ spacing: { after: 480 }
4352
+ })
4353
+ );
4354
+ }
4355
+ elements.push(
4356
+ new Paragraph({
4357
+ border: {
4358
+ bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
4359
+ },
4360
+ spacing: { after: 480 }
4361
+ })
4362
+ );
4363
+ if (cover.company || cover.author || cover.version || cover.date) {
4364
+ const metaRuns = [];
4365
+ if (cover.company) metaRuns.push(new TextRun({ text: `Organization: ${cover.company}
4366
+ `, font: defaultFont, size: 21, color: textHex }));
4367
+ if (cover.author) metaRuns.push(new TextRun({ text: `Author: ${cover.author}
4368
+ `, font: defaultFont, size: 21, color: textHex }));
4369
+ if (cover.version) metaRuns.push(new TextRun({ text: `Version: ${cover.version}
4370
+ `, font: defaultFont, size: 21, color: textHex }));
4371
+ if (cover.date) metaRuns.push(new TextRun({ text: `Date: ${cover.date}
4372
+ `, font: defaultFont, size: 21, color: textHex }));
4373
+ elements.push(
4374
+ new Paragraph({
4375
+ children: metaRuns,
4376
+ spacing: { before: 360, after: 360 }
4377
+ })
4378
+ );
4379
+ }
4380
+ if (cover.footerText) {
4381
+ elements.push(
4382
+ new Paragraph({
4383
+ children: [
4384
+ new TextRun({
4385
+ text: cover.footerText,
4386
+ font: defaultFont,
4387
+ size: 18,
4388
+ color: "94A3B8"
4389
+ })
4390
+ ],
4391
+ spacing: { before: 720 }
4392
+ })
4393
+ );
4394
+ }
4395
+ return elements;
4396
+ }
3215
4397
  async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
3216
4398
  const cellParagraphs = [];
3217
4399
  if (item.title) {
@@ -3347,126 +4529,9 @@ function createEmptyDocxCell(widthDxa) {
3347
4529
  });
3348
4530
  }
3349
4531
 
3350
- // src/core/engine.ts
3351
- async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgress) {
3352
- const startTime = Date.now();
3353
- const config = { ...DEFAULT_CONFIG, ...userConfig };
3354
- let rawMarkdown = "";
3355
- let baseDir = process.cwd();
3356
- let inputFileName = "document.md";
3357
- let isFilePath = false;
3358
- if (fs5.existsSync(inputFilePathOrContent)) {
3359
- isFilePath = true;
3360
- rawMarkdown = fs5.readFileSync(inputFilePathOrContent, "utf-8");
3361
- baseDir = path7.dirname(path7.resolve(inputFilePathOrContent));
3362
- inputFileName = path7.basename(inputFilePathOrContent);
3363
- } else {
3364
- rawMarkdown = inputFilePathOrContent;
3365
- }
3366
- onProgress == null ? void 0 : onProgress(`Parsing markdown AST: ${inputFileName}...`);
3367
- const parsedDoc = parseMarkdownDocument(rawMarkdown);
3368
- const baseName = inputFileName.replace(/\.(md|mdx|markdown)$/i, "");
3369
- const outputDir = config.outputDir ? path7.isAbsolute(config.outputDir) ? config.outputDir : path7.resolve(process.cwd(), config.outputDir) : baseDir;
3370
- if (!fs5.existsSync(outputDir)) {
3371
- fs5.mkdirSync(outputDir, { recursive: true });
3372
- }
3373
- const formats = Array.isArray(config.to) ? config.to : [config.to || "docx", "pdf"];
3374
- const generatedFiles = [];
3375
- const errors = [];
3376
- for (const fmt of formats) {
3377
- try {
3378
- if (fmt === "docx") {
3379
- onProgress == null ? void 0 : onProgress(`Generating DOCX document: ${baseName}.docx...`);
3380
- const docxBuffer = await buildDocxDocument(parsedDoc, config, baseDir);
3381
- const docxPath = path7.join(outputDir, `${baseName}.docx`);
3382
- fs5.writeFileSync(docxPath, docxBuffer);
3383
- generatedFiles.push({
3384
- format: "docx",
3385
- filePath: docxPath,
3386
- fileName: `${baseName}.docx`,
3387
- sizeBytes: docxBuffer.length
3388
- });
3389
- } else if (fmt === "html") {
3390
- onProgress == null ? void 0 : onProgress(`Generating HTML document: ${baseName}.html...`);
3391
- const htmlString = await buildHtmlDocument(parsedDoc, config, baseDir);
3392
- const htmlPath = path7.join(outputDir, `${baseName}.html`);
3393
- fs5.writeFileSync(htmlPath, htmlString, "utf-8");
3394
- generatedFiles.push({
3395
- format: "html",
3396
- filePath: htmlPath,
3397
- fileName: `${baseName}.html`,
3398
- sizeBytes: Buffer.byteLength(htmlString, "utf-8")
3399
- });
3400
- } else if (fmt === "pdf") {
3401
- onProgress == null ? void 0 : onProgress(`Generating PDF document: ${baseName}.pdf...`);
3402
- const pdfBuffer = await buildPdfDocument(parsedDoc, config, baseDir);
3403
- const pdfPath = path7.join(outputDir, `${baseName}.pdf`);
3404
- fs5.writeFileSync(pdfPath, pdfBuffer);
3405
- generatedFiles.push({
3406
- format: "pdf",
3407
- filePath: pdfPath,
3408
- fileName: `${baseName}.pdf`,
3409
- sizeBytes: pdfBuffer.length
3410
- });
3411
- }
3412
- } catch (err) {
3413
- const errMsg = err instanceof Error ? err.message : String(err);
3414
- errors.push(`Failed to generate ${fmt}: ${errMsg}`);
3415
- }
3416
- }
3417
- const durationMs = Date.now() - startTime;
3418
- return {
3419
- inputFile: isFilePath ? inputFilePathOrContent : "inline-string",
3420
- durationMs,
3421
- metadata: parsedDoc.metadata,
3422
- files: generatedFiles,
3423
- errors
3424
- };
3425
- }
3426
-
3427
- // src/ui/App.tsx
3428
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3429
- var App = ({ inputFile, config, onComplete }) => {
3430
- const [status, setStatus] = useState("Initializing...");
3431
- const [isCompiling, setIsCompiling] = useState(true);
3432
- const [result, setResult] = useState(null);
3433
- useEffect(() => {
3434
- async function run() {
3435
- try {
3436
- const res = await compileMarkdown(inputFile, config, (msg) => setStatus(msg));
3437
- setResult(res);
3438
- setIsCompiling(false);
3439
- onComplete == null ? void 0 : onComplete(res);
3440
- } catch (err) {
3441
- const errMsg = err instanceof Error ? err.message : String(err);
3442
- const errResult = {
3443
- inputFile,
3444
- durationMs: 0,
3445
- metadata: {},
3446
- files: [],
3447
- errors: [errMsg]
3448
- };
3449
- setResult(errResult);
3450
- setIsCompiling(false);
3451
- onComplete == null ? void 0 : onComplete(errResult);
3452
- }
3453
- }
3454
- run();
3455
- }, [inputFile, config, onComplete]);
3456
- const targetFormats = Array.isArray(config.to) ? config.to : config.to ? [String(config.to)] : ["docx", "pdf"];
3457
- return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
3458
- /* @__PURE__ */ jsx4(
3459
- Header,
3460
- {
3461
- inputFile,
3462
- theme: config.theme,
3463
- targetFormats
3464
- }
3465
- ),
3466
- /* @__PURE__ */ jsx4(LiveProgress, { status, isCompiling }),
3467
- /* @__PURE__ */ jsx4(SummaryTable, { result })
3468
- ] });
3469
- };
3470
4532
  export {
3471
- App
4533
+ parseMarkdownDocument,
4534
+ buildHtmlDocument,
4535
+ buildPdfDocument,
4536
+ buildDocxDocument
3472
4537
  };