@masumdev/markforge 0.3.0 → 0.4.1

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",
@@ -351,11 +183,51 @@ function parseInlineSpans(text) {
351
183
  remaining = remaining.slice(nextSpecial);
352
184
  }
353
185
  }
354
- return spans;
186
+ const merged = [];
187
+ for (const s of spans) {
188
+ const prev = merged[merged.length - 1];
189
+ if (prev && prev.type === "text" && s.type === "text") {
190
+ prev.content += s.content;
191
+ } else {
192
+ merged.push(s);
193
+ }
194
+ }
195
+ return merged;
355
196
  }
356
197
  function slugify(text) {
357
198
  return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
358
199
  }
200
+ function applyHeadingNumbering(nodes, tocEntries, options) {
201
+ const depth = options.depth ?? 3;
202
+ const skipH1 = options.skipH1 ?? false;
203
+ const prefix = options.prefix ?? "";
204
+ const counters = [0, 0, 0, 0, 0, 0];
205
+ for (const node of nodes) {
206
+ if (node.type === "heading" && node.level) {
207
+ if (node._numbered) continue;
208
+ const lvl = node.level;
209
+ if (lvl > depth) continue;
210
+ if (lvl === 1 && skipH1) continue;
211
+ const idx = lvl - 1;
212
+ counters[idx]++;
213
+ for (let c = idx + 1; c < counters.length; c++) {
214
+ counters[c] = 0;
215
+ }
216
+ const startIdx = skipH1 ? 1 : 0;
217
+ const parts = counters.slice(startIdx, idx + 1).filter((n) => n > 0);
218
+ const numberStr = parts.join(".") + ".";
219
+ const fullPrefix = prefix ? `${prefix} ${numberStr} ` : `${numberStr} `;
220
+ const originalText = node.text || "";
221
+ node.text = fullPrefix + originalText;
222
+ node.inlines = parseInlineSpans(node.text);
223
+ node._numbered = true;
224
+ const toc = tocEntries.find((t) => t.id === node.id);
225
+ if (toc) {
226
+ toc.text = node.text;
227
+ }
228
+ }
229
+ }
230
+ }
359
231
  function parseMarkdownDocument(rawMarkdown) {
360
232
  const { data: frontmatter, content } = matter(rawMarkdown);
361
233
  const metadata = frontmatter || {};
@@ -367,6 +239,7 @@ function parseMarkdownDocument(rawMarkdown) {
367
239
  const lines = cleanContent.split(/\r?\n/);
368
240
  const nodes = [];
369
241
  const tocEntries = [];
242
+ const footnoteDefs = [];
370
243
  let i = 0;
371
244
  while (i < lines.length) {
372
245
  const line = lines[i];
@@ -395,6 +268,29 @@ function parseMarkdownDocument(rawMarkdown) {
395
268
  i++;
396
269
  continue;
397
270
  }
271
+ if (line.trim().startsWith("$$")) {
272
+ const mathLines = [];
273
+ const singleLine = line.trim().match(/^\$\$(.+)\$\$$/);
274
+ if (singleLine) {
275
+ nodes.push({
276
+ type: "mathBlock",
277
+ text: singleLine[1].trim()
278
+ });
279
+ i++;
280
+ continue;
281
+ }
282
+ i++;
283
+ while (i < lines.length && !lines[i].trim().startsWith("$$")) {
284
+ mathLines.push(lines[i]);
285
+ i++;
286
+ }
287
+ if (i < lines.length) i++;
288
+ nodes.push({
289
+ type: "mathBlock",
290
+ text: mathLines.join("\n").trim()
291
+ });
292
+ continue;
293
+ }
398
294
  const codeBlockMatch = line.match(/^```(\w+)?/);
399
295
  if (codeBlockMatch) {
400
296
  const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
@@ -421,6 +317,100 @@ function parseMarkdownDocument(rawMarkdown) {
421
317
  }
422
318
  continue;
423
319
  }
320
+ const colsMatch = line.trim().match(/^:::columns(?:\s+\[?([\w\s=.-]+)\]?)?$/i);
321
+ if (colsMatch) {
322
+ const attrStr = colsMatch[1] || "";
323
+ let colsCount = 2;
324
+ let colGap = "1.5rem";
325
+ if (attrStr) {
326
+ const numMatch = attrStr.trim().match(/^(\d+)$/);
327
+ const cMatch = attrStr.match(/cols=(\d+)/i) || attrStr.match(/columns=(\d+)/i);
328
+ const gMatch = attrStr.match(/gap=([^\s]+)/i);
329
+ if (numMatch) colsCount = parseInt(numMatch[1], 10);
330
+ else if (cMatch) colsCount = parseInt(cMatch[1], 10);
331
+ if (gMatch) colGap = gMatch[1];
332
+ }
333
+ const columnNodes = [];
334
+ let currentColumnLines = [];
335
+ let inColBlock = false;
336
+ i++;
337
+ while (i < lines.length) {
338
+ const curLine = lines[i];
339
+ const trimmed = curLine.trim();
340
+ if (/^:::col(?:umn)?$/i.test(trimmed)) {
341
+ if (currentColumnLines.length > 0) {
342
+ const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
343
+ columnNodes.push({
344
+ type: "column",
345
+ children: subDoc.nodes
346
+ });
347
+ currentColumnLines = [];
348
+ }
349
+ inColBlock = true;
350
+ i++;
351
+ } else if (trimmed === ":::") {
352
+ if (inColBlock) {
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
+ inColBlock = false;
362
+ i++;
363
+ } else {
364
+ if (currentColumnLines.length > 0) {
365
+ const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
366
+ columnNodes.push({
367
+ type: "column",
368
+ children: subDoc.nodes
369
+ });
370
+ currentColumnLines = [];
371
+ }
372
+ i++;
373
+ break;
374
+ }
375
+ } else {
376
+ currentColumnLines.push(curLine);
377
+ i++;
378
+ }
379
+ }
380
+ if (currentColumnLines.length > 0) {
381
+ const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
382
+ columnNodes.push({
383
+ type: "column",
384
+ children: subDoc.nodes
385
+ });
386
+ }
387
+ nodes.push({
388
+ type: "columns",
389
+ columnsCount: columnNodes.length > 0 ? columnNodes.length : colsCount,
390
+ columnGap: colGap,
391
+ children: columnNodes
392
+ });
393
+ continue;
394
+ }
395
+ const fnDefMatch = line.match(/^\[\^([\w-]+)\]:\s+(.+)$/);
396
+ if (fnDefMatch) {
397
+ const fnId = fnDefMatch[1];
398
+ const fnText = fnDefMatch[2].trim();
399
+ const inlines = parseInlineSpans(fnText);
400
+ footnoteDefs.push({
401
+ id: fnId,
402
+ text: fnText,
403
+ inlines
404
+ });
405
+ nodes.push({
406
+ type: "footnoteDef",
407
+ footnoteId: fnId,
408
+ text: fnText,
409
+ inlines
410
+ });
411
+ i++;
412
+ continue;
413
+ }
424
414
  const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
425
415
  if (calloutMatch) {
426
416
  const calloutType = calloutMatch[1].toUpperCase();
@@ -544,7 +534,7 @@ function parseMarkdownDocument(rawMarkdown) {
544
534
  continue;
545
535
  }
546
536
  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+/)) {
537
+ 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
538
  paraLines.push(lines[i]);
549
539
  i++;
550
540
  }
@@ -555,43 +545,29 @@ function parseMarkdownDocument(rawMarkdown) {
555
545
  inlines: parseInlineSpans(paraText)
556
546
  });
557
547
  }
548
+ if (metadata.numberHeadings) {
549
+ const opts = typeof metadata.numberHeadings === "boolean" ? { enabled: metadata.numberHeadings } : metadata.numberHeadings;
550
+ if (opts.enabled !== false) {
551
+ applyHeadingNumbering(nodes, tocEntries, opts);
552
+ }
553
+ }
558
554
  return {
559
555
  metadata,
560
556
  content: cleanContent,
561
557
  nodes,
562
558
  tocEntries,
563
- inlinedStyles
559
+ inlinedStyles,
560
+ footnoteDefs
564
561
  };
565
562
  }
566
563
 
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";
564
+ // src/core/html/htmlBuilder.ts
565
+ import * as fs2 from "fs";
566
+ import * as path2 from "path";
591
567
 
592
568
  // src/core/imageResolver.ts
593
569
  import * as fs from "fs";
594
- import * as path3 from "path";
570
+ import * as path from "path";
595
571
  var memoryImageCache = /* @__PURE__ */ new Map();
596
572
  function getMimeType(filePathOrUrl) {
597
573
  const clean = filePathOrUrl.split("?")[0].toLowerCase();
@@ -648,9 +624,14 @@ async function resolveImage(src, baseDir = process.cwd()) {
648
624
  memoryImageCache.set(cacheKey, resolved2);
649
625
  return resolved2;
650
626
  }
651
- const localPath = path3.isAbsolute(src) ? src : path3.resolve(baseDir, src);
627
+ let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
652
628
  if (!fs.existsSync(localPath)) {
653
- return null;
629
+ const cwdPath = path.resolve(process.cwd(), src);
630
+ if (fs.existsSync(cwdPath)) {
631
+ localPath = cwdPath;
632
+ } else {
633
+ return null;
634
+ }
654
635
  }
655
636
  const buffer = fs.readFileSync(localPath);
656
637
  const mimeType = getMimeType(localPath);
@@ -670,135 +651,297 @@ async function resolveImage(src, baseDir = process.cwd()) {
670
651
  }
671
652
  }
672
653
 
673
- // src/core/syntax/syntaxHighlighter.ts
674
- var SYNTAX_COLORS = {
675
- keyword: "FF7B72",
676
- // Bright coral-red — keywords (import, def, return...)
677
- string: "E07C4F",
678
- // Warm orange — strings (visible on dark bg)
679
- comment: "8B949E",
680
- // Muted gray — comments (italic)
681
- number: "79C0FF",
682
- // Sky blue — numbers
683
- boolean: "FF9580",
684
- // Salmon — true / false / None
685
- function: "D2A8FF",
686
- // Soft purple — function names
687
- type: "7EE787",
688
- // Bright green — Types / Classes
689
- operator: "FF7B72",
690
- // Same as keyword — = + - * / > <
691
- punctuation: "8B9AC0",
692
- // Steel blue-gray — () [] {} , . ;
693
- plain: "E2E8F0"
694
- // Light gray — identifiers / plain text
695
- };
696
- var SYNTAX_COLORS_LIGHT = {
697
- keyword: "D73A49",
698
- // Crimson red — keywords
699
- string: "0A7E5C",
700
- // Forest teal — strings (readable on white)
701
- comment: "6A737D",
702
- // Muted gray — comments
703
- number: "005CC5",
704
- // Cobalt blue — numbers
705
- boolean: "D73A49",
706
- // Crimson — true/false/None
707
- function: "6F42C1",
708
- // Purple — function names
709
- type: "22863A",
710
- // Forest green — Types / Classes
711
- operator: "D73A49",
712
- // Crimson — operators
713
- punctuation: "586069",
714
- // Dark gray — punctuation
715
- plain: "24292E"
716
- // Near-black — identifiers
717
- };
718
- var SYNTAX_THEMES = {
719
- "github-dark": SYNTAX_COLORS,
720
- "dark": SYNTAX_COLORS,
721
- "github-light": SYNTAX_COLORS_LIGHT,
722
- "light": SYNTAX_COLORS_LIGHT,
723
- "dracula": {
724
- keyword: "FF79C6",
725
- string: "F1FA8C",
726
- comment: "6272A4",
727
- number: "BD93F9",
728
- boolean: "BD93F9",
729
- function: "50FA7B",
730
- type: "8BE9FD",
731
- operator: "FF79C6",
732
- punctuation: "F8F8F2",
733
- plain: "F8F8F2"
734
- },
735
- "monokai": {
736
- keyword: "F92672",
737
- string: "E6DB74",
738
- comment: "75715E",
739
- number: "AE81FF",
740
- boolean: "AE81FF",
741
- function: "A6E22E",
742
- type: "66D9EF",
743
- operator: "F92672",
744
- punctuation: "F8F8F2",
745
- plain: "F8F8F2"
746
- },
747
- "nord": {
748
- keyword: "81A1C1",
749
- string: "A3BE8C",
750
- comment: "616E88",
751
- number: "B48EAD",
752
- boolean: "81A1C1",
753
- function: "88C0D0",
754
- type: "8FBCBB",
755
- operator: "81A1C1",
756
- punctuation: "ECEFF4",
757
- plain: "D8DEE9"
758
- }
759
- };
760
- function getSyntaxPalette(themeName = "github-dark") {
761
- return SYNTAX_THEMES[themeName.toLowerCase()] || SYNTAX_THEMES["github-dark"];
654
+ // src/core/html/htmlThemes.ts
655
+ var THEME_COMPONENTS = `
656
+ /* Fallback CSS variables \u2014 overridden by each named theme's :root block */
657
+ :root {
658
+ --mf-bg: #ffffff;
659
+ --mf-text: #0f172a;
660
+ --mf-text-muted: #64748b;
661
+ --mf-primary: #33CDCF;
662
+ --mf-primary-dark: #009DA0;
663
+ --mf-primary-light: #ECFDFD;
664
+ --mf-border: #e2e8f0;
665
+ --mf-card-bg: #f8fafc;
666
+ --mf-code-bg: #0f172a;
667
+ --mf-code-text: #f8fafc;
668
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
669
+ --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
762
670
  }
763
- var JS_KEYWORDS = /* @__PURE__ */ new Set([
764
- "import",
765
- "from",
766
- "export",
767
- "default",
768
- "const",
769
- "let",
770
- "var",
771
- "function",
772
- "async",
773
- "await",
774
- "return",
775
- "if",
776
- "else",
777
- "for",
778
- "while",
779
- "switch",
780
- "case",
781
- "break",
782
- "continue",
783
- "new",
784
- "this",
785
- "typeof",
786
- "instanceof",
787
- "class",
788
- "extends",
789
- "implements",
790
- "interface",
791
- "type",
792
- "enum",
793
- "as",
794
- "try",
795
- "catch",
796
- "finally",
797
- "throw",
798
- "void",
799
- "yield",
800
- "static",
801
- "readonly",
671
+
672
+ /* Document header */
673
+ .document-header { margin-bottom: 2.5rem; padding-bottom: 1.5rem; border-bottom: 2px solid var(--mf-border); }
674
+ .document-title { font-size: 2.5rem; font-weight: 800; margin: 0 0 0.5rem 0; }
675
+ .document-subtitle { font-size: 1.25rem; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
676
+ .document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
677
+
678
+ /* Table of Contents */
679
+ .table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; page-break-after: always; break-after: page; }
680
+ .table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
681
+ .table-of-contents ul { list-style: none; padding: 0; margin: 0; }
682
+ .table-of-contents li { padding: 0.25rem 0; }
683
+ .table-of-contents a { color: var(--mf-primary-dark); text-decoration: none; }
684
+ .table-of-contents a:hover { text-decoration: underline; }
685
+
686
+ /* Links */
687
+ a { color: var(--mf-primary-dark); font-weight: 600; text-decoration: none; }
688
+ a:hover { text-decoration: underline; }
689
+
690
+ /* Blockquote */
691
+ 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; }
692
+
693
+ /* Callout / Alert Boxes */
694
+ .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; }
695
+ .callout-title { font-weight: 700; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.4rem; }
696
+ .callout-NOTE { border-color: #33CDCF; background: #ECFDFD; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
697
+ .callout-NOTE .callout-title { color: #009DA0; }
698
+ .callout-TIP { border-color: #10b981; background: #ecfdf5; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
699
+ .callout-TIP .callout-title { color: #10b981; }
700
+ .callout-WARNING { border-color: #f59e0b; background: #fffbeb; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
701
+ .callout-WARNING .callout-title { color: #f59e0b; }
702
+ .callout-CAUTION { border-color: #ef4444; background: #fef2f2; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
703
+ .callout-CAUTION .callout-title { color: #ef4444; }
704
+ .callout-IMPORTANT { border-color: #8b5cf6; background: #f5f3ff; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
705
+ .callout-IMPORTANT .callout-title { color: #8b5cf6; }
706
+
707
+ /* Inline Code */
708
+ code { font-family: var(--mf-font-mono); font-size: 0.88em; background-color: #f1f5f9; padding: 0.2em 0.4em; border-radius: 4px; color: #0f172a; }
709
+
710
+ /* Code Blocks */
711
+ 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; }
712
+ pre code { background-color: transparent; color: inherit; padding: 0; }
713
+
714
+ /* Tables */
715
+ table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; font-size: 0.95rem; }
716
+ th, td { border: 1px solid var(--mf-border); padding: 0.75rem 1rem; text-align: left; }
717
+ th { background-color: var(--mf-card-bg); font-weight: 600; color: #0f172a; }
718
+ tr:nth-child(even) { background-color: var(--mf-card-bg); }
719
+
720
+ /* Images */
721
+ img { max-width: 100%; height: auto; border-radius: 6px; margin: 1rem 0; }
722
+
723
+ /* Dividers */
724
+ hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
725
+
726
+ /* Print / PDF */
727
+ @media print {
728
+ body { padding: 0; }
729
+ .document-container { max-width: 100%; }
730
+ pre, table, blockquote, .callout { break-inside: avoid; }
731
+ /* Remove scrollbars \u2014 PDF has no scrolling */
732
+ pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
733
+ }
734
+ `;
735
+ var THEME_CORPORATE = `
736
+ :root {
737
+ --mf-bg: #ffffff;
738
+ --mf-text: #0f172a;
739
+ --mf-text-muted: #64748b;
740
+ --mf-primary: #33CDCF;
741
+ --mf-primary-dark: #009DA0;
742
+ --mf-primary-light: #ECFDFD;
743
+ --mf-border: #e2e8f0;
744
+ --mf-card-bg: #f8fafc;
745
+ --mf-code-bg: #0f172a;
746
+ --mf-code-text: #f8fafc;
747
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
748
+ --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
749
+ }
750
+ 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; }
751
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
752
+ h1, h2, h3, h4, h5, h6 { color: var(--mf-primary-dark); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
753
+ h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
754
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
755
+ h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
756
+ h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
757
+ h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
758
+ h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
759
+ p { margin: 0.8rem 0; }
760
+ `;
761
+ var THEMES = {
762
+ corporate: THEME_CORPORATE,
763
+ default: THEME_CORPORATE
764
+ };
765
+ function generateThemeCss(theme) {
766
+ if (!theme || theme === "corporate" || theme === "default" || theme === "corporate" /* CORPORATE */) {
767
+ return THEME_CORPORATE;
768
+ }
769
+ if (typeof theme === "object") {
770
+ const bg = theme.backgroundColor || "#ffffff";
771
+ const text = theme.textColor || "#0f172a";
772
+ const textMuted = theme.textMuted || "#64748b";
773
+ const primary = theme.primaryColor || "#33CDCF";
774
+ const primaryDark = theme.primaryDark || primary;
775
+ const primaryLight = theme.primaryLight || "#ECFDFD";
776
+ const border = theme.borderColor || "#e2e8f0";
777
+ const cardBg = theme.cardBackground || "#f8fafc";
778
+ const codeBg = theme.codeBackground || "#0f172a";
779
+ const codeText = theme.codeText || "#f8fafc";
780
+ const font = theme.fontFamily || "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif";
781
+ const fontMono = theme.fontMono || "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace";
782
+ return `
783
+ :root {
784
+ --mf-bg: ${bg};
785
+ --mf-text: ${text};
786
+ --mf-text-muted: ${textMuted};
787
+ --mf-primary: ${primary};
788
+ --mf-primary-dark: ${primaryDark};
789
+ --mf-primary-light: ${primaryLight};
790
+ --mf-border: ${border};
791
+ --mf-card-bg: ${cardBg};
792
+ --mf-code-bg: ${codeBg};
793
+ --mf-code-text: ${codeText};
794
+ --mf-font-family: ${font};
795
+ --mf-font-mono: ${fontMono};
796
+ }
797
+ 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; }
798
+ .document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
799
+ h1, h2, h3, h4, h5, h6 { color: var(--mf-primary-dark); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
800
+ h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
801
+ h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
802
+ h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
803
+ h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
804
+ h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
805
+ h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
806
+ p { margin: 0.8rem 0; }
807
+ ${theme.customCss || ""}
808
+ `;
809
+ }
810
+ if (typeof theme === "string" && THEMES[theme]) {
811
+ return THEMES[theme];
812
+ }
813
+ return THEME_CORPORATE;
814
+ }
815
+
816
+ // src/core/syntax/syntaxHighlighter.ts
817
+ var SYNTAX_COLORS = {
818
+ keyword: "FF7B72",
819
+ // Bright coral-red — keywords (import, def, return...)
820
+ string: "E07C4F",
821
+ // Warm orange — strings (visible on dark bg)
822
+ comment: "8B949E",
823
+ // Muted gray — comments (italic)
824
+ number: "79C0FF",
825
+ // Sky blue — numbers
826
+ boolean: "FF9580",
827
+ // Salmon — true / false / None
828
+ function: "D2A8FF",
829
+ // Soft purple — function names
830
+ type: "7EE787",
831
+ // Bright green — Types / Classes
832
+ operator: "FF7B72",
833
+ // Same as keyword — = + - * / > <
834
+ punctuation: "8B9AC0",
835
+ // Steel blue-gray — () [] {} , . ;
836
+ plain: "E2E8F0"
837
+ // Light gray — identifiers / plain text
838
+ };
839
+ var SYNTAX_COLORS_LIGHT = {
840
+ keyword: "D73A49",
841
+ // Crimson red — keywords
842
+ string: "0A7E5C",
843
+ // Forest teal — strings (readable on white)
844
+ comment: "6A737D",
845
+ // Muted gray — comments
846
+ number: "005CC5",
847
+ // Cobalt blue — numbers
848
+ boolean: "D73A49",
849
+ // Crimson — true/false/None
850
+ function: "6F42C1",
851
+ // Purple — function names
852
+ type: "22863A",
853
+ // Forest green — Types / Classes
854
+ operator: "D73A49",
855
+ // Crimson — operators
856
+ punctuation: "586069",
857
+ // Dark gray — punctuation
858
+ plain: "24292E"
859
+ // Near-black — identifiers
860
+ };
861
+ var SYNTAX_THEMES = {
862
+ "github-dark": SYNTAX_COLORS,
863
+ "dark": SYNTAX_COLORS,
864
+ "github-light": SYNTAX_COLORS_LIGHT,
865
+ "light": SYNTAX_COLORS_LIGHT,
866
+ "dracula": {
867
+ keyword: "FF79C6",
868
+ string: "F1FA8C",
869
+ comment: "6272A4",
870
+ number: "BD93F9",
871
+ boolean: "BD93F9",
872
+ function: "50FA7B",
873
+ type: "8BE9FD",
874
+ operator: "FF79C6",
875
+ punctuation: "F8F8F2",
876
+ plain: "F8F8F2"
877
+ },
878
+ "monokai": {
879
+ keyword: "F92672",
880
+ string: "E6DB74",
881
+ comment: "75715E",
882
+ number: "AE81FF",
883
+ boolean: "AE81FF",
884
+ function: "A6E22E",
885
+ type: "66D9EF",
886
+ operator: "F92672",
887
+ punctuation: "F8F8F2",
888
+ plain: "F8F8F2"
889
+ },
890
+ "nord": {
891
+ keyword: "81A1C1",
892
+ string: "A3BE8C",
893
+ comment: "616E88",
894
+ number: "B48EAD",
895
+ boolean: "81A1C1",
896
+ function: "88C0D0",
897
+ type: "8FBCBB",
898
+ operator: "81A1C1",
899
+ punctuation: "ECEFF4",
900
+ plain: "D8DEE9"
901
+ }
902
+ };
903
+ function getSyntaxPalette(themeName = "github-dark") {
904
+ return SYNTAX_THEMES[themeName.toLowerCase()] || SYNTAX_THEMES["github-dark"];
905
+ }
906
+ var JS_KEYWORDS = /* @__PURE__ */ new Set([
907
+ "import",
908
+ "from",
909
+ "export",
910
+ "default",
911
+ "const",
912
+ "let",
913
+ "var",
914
+ "function",
915
+ "async",
916
+ "await",
917
+ "return",
918
+ "if",
919
+ "else",
920
+ "for",
921
+ "while",
922
+ "switch",
923
+ "case",
924
+ "break",
925
+ "continue",
926
+ "new",
927
+ "this",
928
+ "typeof",
929
+ "instanceof",
930
+ "class",
931
+ "extends",
932
+ "implements",
933
+ "interface",
934
+ "type",
935
+ "enum",
936
+ "as",
937
+ "try",
938
+ "catch",
939
+ "finally",
940
+ "throw",
941
+ "void",
942
+ "yield",
943
+ "static",
944
+ "readonly",
802
945
  "private",
803
946
  "public",
804
947
  "protected"
@@ -907,298 +1050,122 @@ function tokenizeCodeLine(line, lang = "", theme = "github-dark") {
907
1050
  var _a;
908
1051
  const COLORS = getSyntaxPalette(theme);
909
1052
  if (!line) {
910
- return [{ text: " ", type: "plain", colorHex: COLORS.plain }];
911
- }
912
- const normalizedLang = (lang || "").toLowerCase().trim();
913
- const tokens = [];
914
- let pos = 0;
915
- if (line.trimStart().startsWith("//") || line.trimStart().startsWith("#") || line.trimStart().startsWith("--")) {
916
- const leadWs = ((_a = line.match(/^\s*/)) == null ? void 0 : _a[0]) || "";
917
- if (leadWs) {
918
- tokens.push({ text: leadWs, type: "plain", colorHex: COLORS.plain });
919
- }
920
- tokens.push({
921
- text: line.slice(leadWs.length),
922
- type: "comment",
923
- colorHex: COLORS.comment,
924
- italic: true
925
- });
926
- return tokens;
927
- }
928
- while (pos < line.length) {
929
- if (/\s/.test(line[pos])) {
930
- let ws = "";
931
- while (pos < line.length && /\s/.test(line[pos])) {
932
- ws += line[pos++];
933
- }
934
- tokens.push({ text: ws, type: "plain", colorHex: COLORS.plain });
935
- continue;
936
- }
937
- if (line.slice(pos, pos + 2) === "//") {
938
- tokens.push({
939
- text: line.slice(pos),
940
- type: "comment",
941
- colorHex: COLORS.comment,
942
- italic: true
943
- });
944
- break;
945
- }
946
- if (line[pos] === '"' || line[pos] === "'" || line[pos] === "`") {
947
- const quote = line[pos];
948
- let str = quote;
949
- pos++;
950
- while (pos < line.length) {
951
- if (line[pos] === "\\" && pos + 1 < line.length) {
952
- str += line[pos] + line[pos + 1];
953
- pos += 2;
954
- continue;
955
- }
956
- str += line[pos];
957
- if (line[pos] === quote) {
958
- pos++;
959
- break;
960
- }
961
- pos++;
962
- }
963
- tokens.push({ text: str, type: "string", colorHex: COLORS.string });
964
- continue;
965
- }
966
- if (/\d/.test(line[pos])) {
967
- let num = "";
968
- while (pos < line.length && /[\d.a-fA-FxX]/.test(line[pos])) {
969
- num += line[pos++];
970
- }
971
- tokens.push({ text: num, type: "number", colorHex: COLORS.number });
972
- continue;
973
- }
974
- if (/[a-zA-Z_$]/.test(line[pos])) {
975
- let word = "";
976
- while (pos < line.length && /[a-zA-Z0-9_$]/.test(line[pos])) {
977
- word += line[pos++];
978
- }
979
- if (word === "true" || word === "false" || word === "null" || word === "undefined" || word === "True" || word === "False" || word === "None") {
980
- tokens.push({ text: word, type: "boolean", colorHex: COLORS.boolean, bold: true });
981
- continue;
982
- }
983
- const isJsKeyword = (!normalizedLang || ["ts", "typescript", "js", "javascript", "tsx", "jsx"].includes(normalizedLang)) && JS_KEYWORDS.has(word);
984
- const isPyKeyword = ["py", "python"].includes(normalizedLang) && PYTHON_KEYWORDS.has(word);
985
- const isBashKeyword = ["sh", "bash", "shell", "zsh"].includes(normalizedLang) && BASH_KEYWORDS.has(word);
986
- const isSqlKeyword = ["sql"].includes(normalizedLang) && SQL_KEYWORDS.has(word.toLowerCase());
987
- if (isJsKeyword || isPyKeyword || isBashKeyword || isSqlKeyword) {
988
- tokens.push({ text: word, type: "keyword", colorHex: COLORS.keyword, bold: true });
989
- continue;
990
- }
991
- if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
992
- tokens.push({ text: word, type: "type", colorHex: COLORS.type });
993
- continue;
994
- }
995
- let nextNonWs = pos;
996
- while (nextNonWs < line.length && /\s/.test(line[nextNonWs])) {
997
- nextNonWs++;
998
- }
999
- if (line[nextNonWs] === "(") {
1000
- tokens.push({ text: word, type: "function", colorHex: COLORS.function });
1001
- continue;
1002
- }
1003
- tokens.push({ text: word, type: "plain", colorHex: COLORS.plain });
1004
- continue;
1005
- }
1006
- const char = line[pos++];
1007
- if (["=", "+", "-", "*", "/", "!", ">", "<", "&", "|", "?", ":"].includes(char)) {
1008
- tokens.push({ text: char, type: "operator", colorHex: COLORS.operator });
1009
- } else {
1010
- tokens.push({ text: char, type: "punctuation", colorHex: COLORS.punctuation });
1011
- }
1012
- }
1013
- return tokens;
1014
- }
1015
- function highlightCodeToHtml(code, lang = "", theme = "github-dark") {
1016
- const lines = (code || "").split("\n");
1017
- const htmlLines = lines.map((line) => {
1018
- const tokens = tokenizeCodeLine(line, lang, theme);
1019
- return tokens.map((t) => {
1020
- const escaped = t.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1021
- if (t.type === "plain") return escaped;
1022
- return `<span style="color: #${t.colorHex};${t.bold ? " font-weight: bold;" : ""}${t.italic ? " font-style: italic;" : ""}">${escaped}</span>`;
1023
- }).join("");
1024
- });
1025
- return htmlLines.join("\n");
1026
- }
1027
-
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;
1053
+ return [{ text: " ", type: "plain", colorHex: COLORS.plain }];
1158
1054
  }
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
- `;
1055
+ const normalizedLang = (lang || "").toLowerCase().trim();
1056
+ const tokens = [];
1057
+ let pos = 0;
1058
+ if (line.trimStart().startsWith("//") || line.trimStart().startsWith("#") || line.trimStart().startsWith("--")) {
1059
+ const leadWs = ((_a = line.match(/^\s*/)) == null ? void 0 : _a[0]) || "";
1060
+ if (leadWs) {
1061
+ tokens.push({ text: leadWs, type: "plain", colorHex: COLORS.plain });
1062
+ }
1063
+ tokens.push({
1064
+ text: line.slice(leadWs.length),
1065
+ type: "comment",
1066
+ colorHex: COLORS.comment,
1067
+ italic: true
1068
+ });
1069
+ return tokens;
1197
1070
  }
1198
- if (typeof theme === "string" && THEMES[theme]) {
1199
- return THEMES[theme];
1071
+ while (pos < line.length) {
1072
+ if (/\s/.test(line[pos])) {
1073
+ let ws = "";
1074
+ while (pos < line.length && /\s/.test(line[pos])) {
1075
+ ws += line[pos++];
1076
+ }
1077
+ tokens.push({ text: ws, type: "plain", colorHex: COLORS.plain });
1078
+ continue;
1079
+ }
1080
+ if (line.slice(pos, pos + 2) === "//") {
1081
+ tokens.push({
1082
+ text: line.slice(pos),
1083
+ type: "comment",
1084
+ colorHex: COLORS.comment,
1085
+ italic: true
1086
+ });
1087
+ break;
1088
+ }
1089
+ if (line[pos] === '"' || line[pos] === "'" || line[pos] === "`") {
1090
+ const quote = line[pos];
1091
+ let str = quote;
1092
+ pos++;
1093
+ while (pos < line.length) {
1094
+ if (line[pos] === "\\" && pos + 1 < line.length) {
1095
+ str += line[pos] + line[pos + 1];
1096
+ pos += 2;
1097
+ continue;
1098
+ }
1099
+ str += line[pos];
1100
+ if (line[pos] === quote) {
1101
+ pos++;
1102
+ break;
1103
+ }
1104
+ pos++;
1105
+ }
1106
+ tokens.push({ text: str, type: "string", colorHex: COLORS.string });
1107
+ continue;
1108
+ }
1109
+ if (/\d/.test(line[pos])) {
1110
+ let num = "";
1111
+ while (pos < line.length && /[\d.a-fA-FxX]/.test(line[pos])) {
1112
+ num += line[pos++];
1113
+ }
1114
+ tokens.push({ text: num, type: "number", colorHex: COLORS.number });
1115
+ continue;
1116
+ }
1117
+ if (/[a-zA-Z_$]/.test(line[pos])) {
1118
+ let word = "";
1119
+ while (pos < line.length && /[a-zA-Z0-9_$]/.test(line[pos])) {
1120
+ word += line[pos++];
1121
+ }
1122
+ if (word === "true" || word === "false" || word === "null" || word === "undefined" || word === "True" || word === "False" || word === "None") {
1123
+ tokens.push({ text: word, type: "boolean", colorHex: COLORS.boolean, bold: true });
1124
+ continue;
1125
+ }
1126
+ const isJsKeyword = (!normalizedLang || ["ts", "typescript", "js", "javascript", "tsx", "jsx"].includes(normalizedLang)) && JS_KEYWORDS.has(word);
1127
+ const isPyKeyword = ["py", "python"].includes(normalizedLang) && PYTHON_KEYWORDS.has(word);
1128
+ const isBashKeyword = ["sh", "bash", "shell", "zsh"].includes(normalizedLang) && BASH_KEYWORDS.has(word);
1129
+ const isSqlKeyword = ["sql"].includes(normalizedLang) && SQL_KEYWORDS.has(word.toLowerCase());
1130
+ if (isJsKeyword || isPyKeyword || isBashKeyword || isSqlKeyword) {
1131
+ tokens.push({ text: word, type: "keyword", colorHex: COLORS.keyword, bold: true });
1132
+ continue;
1133
+ }
1134
+ if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
1135
+ tokens.push({ text: word, type: "type", colorHex: COLORS.type });
1136
+ continue;
1137
+ }
1138
+ let nextNonWs = pos;
1139
+ while (nextNonWs < line.length && /\s/.test(line[nextNonWs])) {
1140
+ nextNonWs++;
1141
+ }
1142
+ if (line[nextNonWs] === "(") {
1143
+ tokens.push({ text: word, type: "function", colorHex: COLORS.function });
1144
+ continue;
1145
+ }
1146
+ tokens.push({ text: word, type: "plain", colorHex: COLORS.plain });
1147
+ continue;
1148
+ }
1149
+ const char = line[pos++];
1150
+ if (["=", "+", "-", "*", "/", "!", ">", "<", "&", "|", "?", ":"].includes(char)) {
1151
+ tokens.push({ text: char, type: "operator", colorHex: COLORS.operator });
1152
+ } else {
1153
+ tokens.push({ text: char, type: "punctuation", colorHex: COLORS.punctuation });
1154
+ }
1200
1155
  }
1201
- return THEME_CORPORATE;
1156
+ return tokens;
1157
+ }
1158
+ function highlightCodeToHtml(code, lang = "", theme = "github-dark") {
1159
+ const lines = (code || "").split("\n");
1160
+ const htmlLines = lines.map((line) => {
1161
+ const tokens = tokenizeCodeLine(line, lang, theme);
1162
+ return tokens.map((t) => {
1163
+ const escaped = t.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
1164
+ if (t.type === "plain") return escaped;
1165
+ return `<span style="color: #${t.colorHex};${t.bold ? " font-weight: bold;" : ""}${t.italic ? " font-style: italic;" : ""}">${escaped}</span>`;
1166
+ }).join("");
1167
+ });
1168
+ return htmlLines.join("\n");
1202
1169
  }
1203
1170
 
1204
1171
  // src/config/resolveConfig.ts
@@ -1245,15 +1212,45 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
1245
1212
  if (/^[0-9.]+$/.test(str)) return `${str}pt`;
1246
1213
  return str;
1247
1214
  }
1248
- 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 || "");
1215
+ function replaceDocumentTokens(template = "", meta = {}) {
1216
+ if (!template) return "";
1217
+ const currentYear = meta.year ? String(meta.year) : (/* @__PURE__ */ new Date()).getFullYear().toString();
1218
+ const tokenMap = {
1219
+ title: meta.title ? String(meta.title) : "",
1220
+ subtitle: meta.subtitle ? String(meta.subtitle) : "",
1221
+ author: meta.author ? Array.isArray(meta.author) ? meta.author.join(", ") : String(meta.author) : "",
1222
+ version: meta.version ? String(meta.version) : "",
1223
+ date: meta.date ? String(meta.date) : "",
1224
+ company: meta.company ? String(meta.company) : "",
1225
+ year: currentYear
1226
+ };
1227
+ if (meta.metadata && typeof meta.metadata === "object") {
1228
+ for (const [key, val] of Object.entries(meta.metadata)) {
1229
+ if (val !== void 0 && val !== null) {
1230
+ tokenMap[key.toLowerCase()] = String(val);
1231
+ }
1232
+ }
1233
+ }
1234
+ for (const [key, val] of Object.entries(meta)) {
1235
+ if (val !== void 0 && val !== null && typeof val !== "object") {
1236
+ tokenMap[key.toLowerCase()] = String(val);
1237
+ }
1238
+ }
1239
+ return template.replace(/\{([a-zA-Z0-9_\-]+)\}/gi, (match, tokenKey) => {
1240
+ const lowerKey = tokenKey.toLowerCase();
1241
+ if (lowerKey in tokenMap) {
1242
+ return tokenMap[lowerKey];
1243
+ }
1244
+ return match;
1245
+ });
1250
1246
  }
1251
- function normalizeWatermark(rawWatermark) {
1247
+ function normalizeWatermark(rawWatermark, tokens) {
1252
1248
  if (!rawWatermark) {
1253
1249
  return void 0;
1254
1250
  }
1255
1251
  if (typeof rawWatermark === "string") {
1256
- const text = rawWatermark.trim();
1252
+ let text = rawWatermark.trim();
1253
+ if (tokens) text = replaceDocumentTokens(text, tokens);
1257
1254
  if (!text) return void 0;
1258
1255
  return {
1259
1256
  text,
@@ -1266,8 +1263,10 @@ function normalizeWatermark(rawWatermark) {
1266
1263
  }
1267
1264
  if (typeof rawWatermark === "object") {
1268
1265
  if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
1266
+ let text = rawWatermark.text.trim();
1267
+ if (tokens) text = replaceDocumentTokens(text, tokens);
1269
1268
  return {
1270
- text: rawWatermark.text.trim(),
1269
+ text,
1271
1270
  color: rawWatermark.color || "#94a3b8",
1272
1271
  opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
1273
1272
  fontSize: rawWatermark.fontSize || 54,
@@ -1383,6 +1382,174 @@ function normalizeSignatures(raw, meta = {}) {
1383
1382
  spacingBeforeTwip
1384
1383
  };
1385
1384
  }
1385
+ function normalizeCoverPage(rawCover, tokenCtx = {}) {
1386
+ if (!rawCover) return void 0;
1387
+ const cfg = typeof rawCover === "object" ? rawCover : {};
1388
+ if (cfg.enabled === false) return void 0;
1389
+ const preset = cfg.preset || "modern";
1390
+ const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title ? String(tokenCtx.title) : "Document Title";
1391
+ const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle ? String(tokenCtx.subtitle) : void 0;
1392
+ const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
1393
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
1394
+ const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
1395
+ let dateStr;
1396
+ if (typeof cfg.date === "string") {
1397
+ dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
1398
+ } else if (cfg.date === true) {
1399
+ dateStr = tokenCtx.date ? String(tokenCtx.date) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1400
+ } else {
1401
+ dateStr = tokenCtx.date ? String(tokenCtx.date) : void 0;
1402
+ }
1403
+ const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1404
+ const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1405
+ const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1406
+ const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1407
+ const logoWidth = cfg.logoWidth;
1408
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1409
+ const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1410
+ const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1411
+ const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
1412
+ const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
1413
+ const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
1414
+ const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
1415
+ const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
1416
+ const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
1417
+ const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
1418
+ const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
1419
+ let socialMap;
1420
+ if (cfg.social && typeof cfg.social === "object") {
1421
+ socialMap = {};
1422
+ for (const [k, v] of Object.entries(cfg.social)) {
1423
+ if (typeof v === "string") {
1424
+ socialMap[k] = replaceDocumentTokens(v, tokenCtx);
1425
+ }
1426
+ }
1427
+ }
1428
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
1429
+ const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : void 0;
1430
+ return {
1431
+ enabled: true,
1432
+ preset,
1433
+ title,
1434
+ subtitle,
1435
+ author,
1436
+ company,
1437
+ version,
1438
+ date: dateStr,
1439
+ badge,
1440
+ badgeColor,
1441
+ badgeTextColor,
1442
+ logo,
1443
+ logoWidth,
1444
+ bgGradient,
1445
+ backgroundColor,
1446
+ textColor,
1447
+ titleColor,
1448
+ subtitleColor,
1449
+ accentColor,
1450
+ footerText,
1451
+ address,
1452
+ email,
1453
+ phone,
1454
+ website,
1455
+ social: socialMap,
1456
+ copyright
1457
+ };
1458
+ }
1459
+ function normalizeBackCover(rawBack, tokenCtx = {}) {
1460
+ if (!rawBack) return void 0;
1461
+ const cfg = typeof rawBack === "object" ? rawBack : {};
1462
+ if (cfg.enabled === false) return void 0;
1463
+ const preset = cfg.preset || "modern";
1464
+ const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
1465
+ const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
1466
+ const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
1467
+ const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
1468
+ const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
1469
+ const date = typeof cfg.date === "string" ? replaceDocumentTokens(cfg.date, tokenCtx) : tokenCtx.date ? String(tokenCtx.date) : void 0;
1470
+ const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
1471
+ const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
1472
+ const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
1473
+ const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
1474
+ let socialMap;
1475
+ if (cfg.social && typeof cfg.social === "object") {
1476
+ socialMap = {};
1477
+ for (const [k, v] of Object.entries(cfg.social)) {
1478
+ if (typeof v === "string") {
1479
+ socialMap[k] = replaceDocumentTokens(v, tokenCtx);
1480
+ }
1481
+ }
1482
+ }
1483
+ const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
1484
+ const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
1485
+ const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
1486
+ const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
1487
+ const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
1488
+ const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
1489
+ const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
1490
+ const logoWidth = cfg.logoWidth;
1491
+ const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1492
+ const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
1493
+ const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
1494
+ const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
1495
+ const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
1496
+ const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
1497
+ return {
1498
+ enabled: true,
1499
+ preset,
1500
+ title,
1501
+ subtitle,
1502
+ author,
1503
+ company,
1504
+ version,
1505
+ date,
1506
+ address,
1507
+ email,
1508
+ phone,
1509
+ website,
1510
+ social: socialMap,
1511
+ copyright,
1512
+ footerText,
1513
+ badge,
1514
+ badgeColor,
1515
+ badgeTextColor,
1516
+ logo,
1517
+ logoWidth,
1518
+ bgGradient,
1519
+ backgroundColor,
1520
+ textColor,
1521
+ titleColor,
1522
+ subtitleColor,
1523
+ accentColor
1524
+ };
1525
+ }
1526
+ function normalizeNumberHeadings(raw) {
1527
+ if (raw === void 0 || raw === false) return void 0;
1528
+ if (raw === true) {
1529
+ return { enabled: true, depth: 3, skipH1: false, prefix: "" };
1530
+ }
1531
+ if (typeof raw === "object") {
1532
+ const obj = raw;
1533
+ if (obj.enabled === false) return void 0;
1534
+ return {
1535
+ enabled: true,
1536
+ depth: obj.depth ?? 3,
1537
+ skipH1: obj.skipH1 ?? false,
1538
+ prefix: obj.prefix ?? ""
1539
+ };
1540
+ }
1541
+ return void 0;
1542
+ }
1543
+ function normalizeSecurity(raw) {
1544
+ if (!raw) return void 0;
1545
+ const sec = raw;
1546
+ if (!sec.userPassword && !sec.ownerPassword && !sec.permissions) return void 0;
1547
+ return {
1548
+ userPassword: sec.userPassword,
1549
+ ownerPassword: sec.ownerPassword,
1550
+ permissions: sec.permissions
1551
+ };
1552
+ }
1386
1553
  function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1387
1554
  const configMeta = userConfig.metadata || {};
1388
1555
  const mergedMeta = { ...configMeta, ...frontmatter };
@@ -1393,7 +1560,15 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1393
1560
  const version = mergedMeta.version || void 0;
1394
1561
  const company = mergedMeta.company || void 0;
1395
1562
  const lang = mergedMeta.lang || "en";
1396
- const tokenContext = { title, subtitle, author, version, date, company };
1563
+ const tokenContext = {
1564
+ ...mergedMeta,
1565
+ title,
1566
+ subtitle,
1567
+ author,
1568
+ version,
1569
+ date,
1570
+ company
1571
+ };
1397
1572
  const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
1398
1573
  const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
1399
1574
  const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
@@ -1422,9 +1597,18 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1422
1597
  const footer = normalizeHeaderFooter(rawFooter, tokenContext);
1423
1598
  const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
1424
1599
  const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
1425
- const watermark = normalizeWatermark(rawWatermark);
1600
+ const watermark = normalizeWatermark(rawWatermark, tokenContext);
1426
1601
  const rawSignatures = mergedMeta.signatures || userConfig.signatures;
1427
1602
  const signatures = normalizeSignatures(rawSignatures, tokenContext);
1603
+ const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
1604
+ const coverPage = normalizeCoverPage(rawCover, tokenContext);
1605
+ const rawBack = mergedMeta.backCover !== void 0 ? mergedMeta.backCover : userConfig.backCover;
1606
+ const backCover = normalizeBackCover(rawBack, tokenContext);
1607
+ const rawNumberHeadings = mergedMeta.numberHeadings !== void 0 ? mergedMeta.numberHeadings : userConfig.numberHeadings;
1608
+ const numberHeadings = normalizeNumberHeadings(rawNumberHeadings);
1609
+ const rawSecurity = mergedMeta.security || userConfig.security;
1610
+ const security = normalizeSecurity(rawSecurity);
1611
+ const math = mergedMeta.math !== false && userConfig.math !== false;
1428
1612
  const cssList = [];
1429
1613
  const addCss = (item) => {
1430
1614
  if (!item) return;
@@ -1454,6 +1638,11 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1454
1638
  toc,
1455
1639
  signatures,
1456
1640
  watermark,
1641
+ coverPage,
1642
+ backCover,
1643
+ numberHeadings,
1644
+ security,
1645
+ math,
1457
1646
  css: cssList,
1458
1647
  embedImages,
1459
1648
  bundleHtml,
@@ -1461,11 +1650,50 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
1461
1650
  };
1462
1651
  }
1463
1652
 
1653
+ // src/core/math/mathRenderer.ts
1654
+ import katex from "katex";
1655
+ function renderMathToHtml(latex, displayMode = false) {
1656
+ try {
1657
+ return katex.renderToString(latex.trim(), {
1658
+ displayMode,
1659
+ throwOnError: false,
1660
+ output: "htmlAndMathml",
1661
+ strict: false
1662
+ });
1663
+ } catch {
1664
+ return `<span class="katex-fallback">${latex}</span>`;
1665
+ }
1666
+ }
1667
+ var KATEX_INLINE_CSS = `
1668
+ .katex { font: normal 1.21em KaTeX_Main, Times New Roman, serif; line-height: 1.2; text-indent: 0; text-rendering: auto; border-color: currentColor; }
1669
+ .katex * { -ms-high-contrast-adjust: none !important; }
1670
+ .katex .katex-html { display: inline-block; }
1671
+ .katex .katex-mathml { clip: rect(1px, 1px, 1px, 1px); border: 0; height: 1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
1672
+ .katex-display { display: block; margin: 1em 0; text-align: center; }
1673
+ .katex-display > .katex { display: inline-block; text-align: initial; }
1674
+ .katex .base { position: relative; white-space: nowrap; width: min-content; }
1675
+ .katex .strut { display: inline-block; }
1676
+ .katex .mord { display: inline-block; }
1677
+ .katex .mbin { display: inline-block; }
1678
+ .katex .mrel { display: inline-block; }
1679
+ .katex .mopen { display: inline-block; }
1680
+ .katex .mclose { display: inline-block; }
1681
+ .katex .mpunct { display: inline-block; }
1682
+ .katex .minner { display: inline-block; }
1683
+ .katex .mop { display: inline-block; }
1684
+ .katex .frac-line { width: 100%; border-bottom-style: solid; }
1685
+ .katex .vlist-t { display: inline-table; table-layout: fixed; }
1686
+ .katex .vlist-r { display: table-row; }
1687
+ .katex .vlist { display: table-cell; vertical-align: bottom; position: relative; }
1688
+ .katex .msupsub { text-align: left; }
1689
+ .katex .sqrt > .root { margin-left: 0.27777778em; margin-right: -0.55555556em; }
1690
+ `;
1691
+
1464
1692
  // src/core/html/htmlBuilder.ts
1465
1693
  function escapeHtml(str) {
1466
1694
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1467
1695
  }
1468
- async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1696
+ async function renderInlinesToHtml(spans = [], baseDir = process.cwd(), tokens) {
1469
1697
  let result = "";
1470
1698
  for (const span of spans) {
1471
1699
  if (span.type === "image" && span.url) {
@@ -1479,107 +1707,81 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1479
1707
  continue;
1480
1708
  }
1481
1709
  if (span.type === "link" && span.url) {
1482
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1710
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1483
1711
  const title = span.title ? ` title="${escapeHtml(span.title)}"` : "";
1484
1712
  result += `<a href="${escapeHtml(span.url)}"${title}>${inner}</a>`;
1485
1713
  continue;
1486
1714
  }
1487
1715
  if (span.type === "bold") {
1488
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1716
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1489
1717
  result += `<strong>${inner}</strong>`;
1490
1718
  continue;
1491
1719
  }
1492
1720
  if (span.type === "italic") {
1493
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1721
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1494
1722
  result += `<em>${inner}</em>`;
1495
1723
  continue;
1496
1724
  }
1497
1725
  if (span.type === "strikethrough") {
1498
- const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1726
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
1499
1727
  result += `<del>${inner}</del>`;
1500
1728
  continue;
1501
1729
  }
1502
1730
  if (span.type === "code") {
1503
1731
  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
- `;
1732
+ continue;
1554
1733
  }
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
- `;
1734
+ if (span.type === "mathInline") {
1735
+ result += renderMathToHtml(span.content, false);
1736
+ continue;
1568
1737
  }
1569
- bodyHtml += ` </ul>
1570
- </nav>
1571
- `;
1738
+ if (span.type === "footnoteRef") {
1739
+ const id = escapeHtml(span.footnoteId || span.content);
1740
+ result += `<sup><a href="#fn-${id}" id="fnref-${id}" class="markforge-fnref">[${escapeHtml(span.content)}]</a></sup>`;
1741
+ continue;
1742
+ }
1743
+ if (span.type === "htmlInline") {
1744
+ result += span.content;
1745
+ continue;
1746
+ }
1747
+ const content = tokens ? replaceDocumentTokens(span.content, tokens) : span.content;
1748
+ result += escapeHtml(content);
1572
1749
  }
1573
- for (const node of doc.nodes) {
1750
+ return result;
1751
+ }
1752
+ async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd(), tokens) {
1753
+ let bodyHtml = "";
1754
+ const tokenCtx = tokens || resolved;
1755
+ for (const node of nodes) {
1574
1756
  if (node.type === "heading") {
1575
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
1757
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1576
1758
  bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
1577
1759
  `;
1578
1760
  continue;
1579
1761
  }
1580
1762
  if (node.type === "paragraph") {
1581
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
1763
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1582
1764
  bodyHtml += ` <p>${inner}</p>
1765
+ `;
1766
+ continue;
1767
+ }
1768
+ if (node.type === "mathBlock") {
1769
+ bodyHtml += ` <div class="math-block">${renderMathToHtml(node.text || "", true)}</div>
1770
+ `;
1771
+ continue;
1772
+ }
1773
+ if (node.type === "columns") {
1774
+ const cols = node.columnsCount || 2;
1775
+ const gap = node.columnGap || "1.5rem";
1776
+ let colChildrenHtml = "";
1777
+ for (const col of node.children || []) {
1778
+ const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir, tokenCtx);
1779
+ colChildrenHtml += ` <div class="markforge-col">
1780
+ ${colInner} </div>
1781
+ `;
1782
+ }
1783
+ bodyHtml += ` <div class="markforge-columns" style="--cols: ${cols}; --col-gap: ${gap};">
1784
+ ${colChildrenHtml} </div>
1583
1785
  `;
1584
1786
  continue;
1585
1787
  }
@@ -1599,7 +1801,7 @@ ${escapeHtml(node.text || "")}
1599
1801
  continue;
1600
1802
  }
1601
1803
  if (node.type === "callout") {
1602
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
1804
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1603
1805
  const CALLOUT_STYLES = {
1604
1806
  NOTE: { bg: "#ECFDFD", border: "#33CDCF", titleColor: "#009DA0" },
1605
1807
  TIP: { bg: "#ecfdf5", border: "#10b981", titleColor: "#10b981" },
@@ -1619,7 +1821,7 @@ ${escapeHtml(node.text || "")}
1619
1821
  continue;
1620
1822
  }
1621
1823
  if (node.type === "blockquote") {
1622
- const inner = await renderInlinesToHtml(node.inlines, baseDir);
1824
+ const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
1623
1825
  bodyHtml += ` <blockquote>${inner}</blockquote>
1624
1826
  `;
1625
1827
  continue;
@@ -1633,7 +1835,7 @@ ${escapeHtml(node.text || "")}
1633
1835
  for (const cell of row.children || []) {
1634
1836
  const tag = cell.isHeader ? "th" : "td";
1635
1837
  const align = cell.align ? ` align="${cell.align}"` : "";
1636
- const inner = await renderInlinesToHtml(cell.inlines, baseDir);
1838
+ const inner = await renderInlinesToHtml(cell.inlines, baseDir, tokenCtx);
1637
1839
  bodyHtml += ` <${tag}${align}>${inner}</${tag}>
1638
1840
  `;
1639
1841
  }
@@ -1649,7 +1851,7 @@ ${escapeHtml(node.text || "")}
1649
1851
  bodyHtml += ` <${tag}>
1650
1852
  `;
1651
1853
  for (const item of node.children) {
1652
- const inner = await renderInlinesToHtml(item.inlines, baseDir);
1854
+ const inner = await renderInlinesToHtml(item.inlines, baseDir, tokenCtx);
1653
1855
  bodyHtml += ` <li>${inner}</li>
1654
1856
  `;
1655
1857
  }
@@ -1668,6 +1870,446 @@ ${escapeHtml(node.text || "")}
1668
1870
  continue;
1669
1871
  }
1670
1872
  }
1873
+ return bodyHtml;
1874
+ }
1875
+ async function renderCoverPageHtml(cover, baseDir = process.cwd()) {
1876
+ let logoHtml = "";
1877
+ if (cover.logo) {
1878
+ const resolvedLogo = await resolveImage(cover.logo, baseDir);
1879
+ const src = resolvedLogo ? resolvedLogo.dataUri : cover.logo;
1880
+ 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;";
1881
+ logoHtml = `<div class="cover-logo"><img src="${src}" alt="Logo" style="${widthStyle} object-fit: contain;" /></div>`;
1882
+ }
1883
+ const badgeHtml = cover.badge ? `<div class="cover-badge" style="${cover.badgeColor ? `background-color: ${cover.badgeColor};` : ""}${cover.badgeTextColor ? `color: ${cover.badgeTextColor};` : ""}">${escapeHtml(cover.badge)}</div>` : "";
1884
+ const titleHtml = `<h1 class="cover-title">${escapeHtml(cover.title)}</h1>`;
1885
+ const subtitleHtml = cover.subtitle ? `<div class="cover-subtitle">${escapeHtml(cover.subtitle)}</div>` : "";
1886
+ const metaItems = [];
1887
+ 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>`);
1888
+ 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>`);
1889
+ 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>`);
1890
+ 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>`);
1891
+ const metaHtml = metaItems.length > 0 ? `<div class="cover-meta">${metaItems.join("\n")}</div>` : "";
1892
+ const footerHtml = cover.footerText ? `<div class="cover-footer-text">${escapeHtml(cover.footerText)}</div>` : "";
1893
+ const css = `
1894
+ .markforge-cover {
1895
+ min-height: 100vh;
1896
+ box-sizing: border-box;
1897
+ display: flex;
1898
+ flex-direction: column;
1899
+ justify-content: space-between;
1900
+ padding: 4rem 3.5rem;
1901
+ page-break-after: always;
1902
+ break-after: page;
1903
+ position: relative;
1904
+ z-index: 2;
1905
+ background: ${cover.bgGradient || "#FFFFFF"};
1906
+ -webkit-print-color-adjust: exact;
1907
+ print-color-adjust: exact;
1908
+ color: ${cover.textColor || "#0F172A"};
1909
+ }
1910
+ .markforge-cover.cover-modern {
1911
+ border-top: 8px solid #0D998D;
1912
+ }
1913
+ .markforge-cover.cover-corporate-split {
1914
+ border-left: 12px solid #0D998D;
1915
+ }
1916
+ .markforge-cover.cover-card {
1917
+ background: #F8FAFC;
1918
+ }
1919
+ .cover-top {
1920
+ display: flex;
1921
+ justify-content: space-between;
1922
+ align-items: flex-start;
1923
+ width: 100%;
1924
+ }
1925
+ .cover-badge {
1926
+ display: inline-block;
1927
+ padding: 0.35rem 0.85rem;
1928
+ font-size: 0.78rem;
1929
+ font-weight: 700;
1930
+ letter-spacing: 0.08em;
1931
+ text-transform: uppercase;
1932
+ background-color: #ECFDFD;
1933
+ color: #0D998D;
1934
+ border-radius: 4px;
1935
+ border: 1px solid #33CDCF;
1936
+ }
1937
+ .cover-body {
1938
+ margin: auto 0;
1939
+ }
1940
+ .cover-title {
1941
+ font-size: 2.8rem;
1942
+ font-weight: 800;
1943
+ line-height: 1.15;
1944
+ margin: 0 0 1rem 0;
1945
+ color: inherit;
1946
+ }
1947
+ .cover-subtitle {
1948
+ font-size: 1.35rem;
1949
+ font-weight: 400;
1950
+ color: #64748B;
1951
+ margin: 0 0 2rem 0;
1952
+ line-height: 1.4;
1953
+ }
1954
+ .cover-meta {
1955
+ display: flex;
1956
+ flex-direction: column;
1957
+ gap: 0.5rem;
1958
+ border-top: 1.5px solid #E2E8F0;
1959
+ padding-top: 1.5rem;
1960
+ max-width: 480px;
1961
+ }
1962
+ .cover-meta-item {
1963
+ font-size: 0.92rem;
1964
+ display: flex;
1965
+ gap: 0.75rem;
1966
+ }
1967
+ .cover-meta-label {
1968
+ font-weight: 600;
1969
+ color: #64748B;
1970
+ min-width: 110px;
1971
+ }
1972
+ .cover-meta-value {
1973
+ font-weight: 500;
1974
+ color: #0F172A;
1975
+ }
1976
+ .cover-bottom {
1977
+ display: flex;
1978
+ justify-content: space-between;
1979
+ align-items: flex-end;
1980
+ font-size: 0.82rem;
1981
+ color: #94A3B8;
1982
+ }
1983
+ @media print {
1984
+ .markforge-cover {
1985
+ page-break-after: always;
1986
+ break-after: page;
1987
+ height: 100vh;
1988
+ min-height: 100vh;
1989
+ max-height: 100vh;
1990
+ box-sizing: border-box;
1991
+ overflow: hidden;
1992
+ margin: 0;
1993
+ -webkit-print-color-adjust: exact;
1994
+ print-color-adjust: exact;
1995
+ }
1996
+ }
1997
+ `;
1998
+ const html = ` <section class="markforge-cover cover-${cover.preset}">
1999
+ <div class="cover-top">
2000
+ ${logoHtml}
2001
+ ${badgeHtml}
2002
+ </div>
2003
+ <div class="cover-body">
2004
+ ${titleHtml}
2005
+ ${subtitleHtml}
2006
+ ${metaHtml}
2007
+ </div>
2008
+ <div class="cover-bottom">
2009
+ ${footerHtml}
2010
+ </div>
2011
+ </section>
2012
+ `;
2013
+ return { html, css };
2014
+ }
2015
+ async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
2016
+ let logoHtml = "";
2017
+ if (backCover.logo) {
2018
+ const resolved = await resolveImage(backCover.logo, baseDir);
2019
+ const src = resolved ? resolved.dataUri : backCover.logo;
2020
+ const widthStyle = backCover.logoWidth ? `style="width: ${typeof backCover.logoWidth === "number" ? `${backCover.logoWidth}px` : backCover.logoWidth}; max-width: 100%;"` : `style="max-width: 160px; height: auto;"`;
2021
+ logoHtml = `<div class="back-logo"><img src="${src}" alt="Brand Logo" ${widthStyle} /></div>`;
2022
+ }
2023
+ const badgeHtml = backCover.badge ? `<div class="back-badge" style="${backCover.badgeColor ? `background-color: ${backCover.badgeColor};` : ""}${backCover.badgeTextColor ? `color: ${backCover.badgeTextColor};` : ""}">${escapeHtml(backCover.badge)}</div>` : "";
2024
+ const titleHtml = `<h1 class="back-title">${escapeHtml(backCover.title)}</h1>`;
2025
+ const subtitleHtml = backCover.subtitle ? `<div class="back-subtitle">${escapeHtml(backCover.subtitle)}</div>` : "";
2026
+ const contactItems = [];
2027
+ 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>`);
2028
+ 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>`);
2029
+ 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>`);
2030
+ 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>`);
2031
+ 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>`);
2032
+ if (backCover.social) {
2033
+ for (const [network, url] of Object.entries(backCover.social)) {
2034
+ if (url) {
2035
+ 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>`);
2036
+ }
2037
+ }
2038
+ }
2039
+ const contactHtml = contactItems.length > 0 ? `<div class="back-contact-grid">${contactItems.join("\n")}</div>` : "";
2040
+ const copyrightHtml = backCover.copyright ? `<div class="back-copyright">${escapeHtml(backCover.copyright)}</div>` : "";
2041
+ const isDark = backCover.preset === "corporate";
2042
+ const css = `
2043
+ .markforge-back-cover {
2044
+ min-height: 100vh;
2045
+ box-sizing: border-box;
2046
+ display: flex;
2047
+ flex-direction: column;
2048
+ justify-content: space-between;
2049
+ padding: 4rem 3.5rem;
2050
+ page-break-before: always;
2051
+ break-before: page;
2052
+ position: relative;
2053
+ z-index: 2;
2054
+ background: ${backCover.bgGradient || (isDark ? "#0F172A" : "#FFFFFF")};
2055
+ color: ${backCover.textColor || (isDark ? "#F8FAFC" : "#0F172A")};
2056
+ }
2057
+ .markforge-back-cover.back-modern {
2058
+ border-bottom: 8px solid #0D998D;
2059
+ }
2060
+ .markforge-back-cover.back-corporate {
2061
+ border-left: 12px solid #33CDCF;
2062
+ }
2063
+ .markforge-back-cover.back-card {
2064
+ background: #F8FAFC;
2065
+ }
2066
+ .back-top {
2067
+ display: flex;
2068
+ justify-content: space-between;
2069
+ align-items: flex-start;
2070
+ width: 100%;
2071
+ }
2072
+ .back-badge {
2073
+ display: inline-block;
2074
+ padding: 0.35rem 0.85rem;
2075
+ font-size: 0.78rem;
2076
+ font-weight: 700;
2077
+ letter-spacing: 0.08em;
2078
+ text-transform: uppercase;
2079
+ background-color: #ECFDFD;
2080
+ color: #0D998D;
2081
+ border-radius: 4px;
2082
+ border: 1px solid #33CDCF;
2083
+ }
2084
+ .back-body {
2085
+ margin: auto 0;
2086
+ }
2087
+ .back-title {
2088
+ font-size: 2.6rem;
2089
+ font-weight: 800;
2090
+ line-height: 1.15;
2091
+ margin: 0 0 0.75rem 0;
2092
+ color: inherit;
2093
+ }
2094
+ .back-subtitle {
2095
+ font-size: 1.25rem;
2096
+ font-weight: 400;
2097
+ color: ${isDark ? "#94A3B8" : "#64748B"};
2098
+ margin: 0 0 2rem 0;
2099
+ line-height: 1.4;
2100
+ }
2101
+ .back-contact-grid {
2102
+ display: flex;
2103
+ flex-direction: column;
2104
+ gap: 0.6rem;
2105
+ border-top: 1.5px solid ${isDark ? "#334155" : "#E2E8F0"};
2106
+ padding-top: 1.5rem;
2107
+ max-width: 540px;
2108
+ }
2109
+ .back-contact-item {
2110
+ font-size: 0.92rem;
2111
+ display: flex;
2112
+ gap: 0.75rem;
2113
+ }
2114
+ .back-contact-label {
2115
+ font-weight: 600;
2116
+ color: ${isDark ? "#94A3B8" : "#64748B"};
2117
+ min-width: 110px;
2118
+ }
2119
+ .back-contact-link {
2120
+ color: #0D998D;
2121
+ text-decoration: none;
2122
+ font-weight: 600;
2123
+ }
2124
+ .back-contact-link:hover {
2125
+ text-decoration: underline;
2126
+ }
2127
+ .back-copyright {
2128
+ font-size: 0.82rem;
2129
+ color: ${isDark ? "#64748B" : "#94A3B8"};
2130
+ border-top: 1px solid ${isDark ? "#1E293B" : "#F1F5F9"};
2131
+ padding-top: 1rem;
2132
+ margin-top: 2rem;
2133
+ }
2134
+ @media print {
2135
+ .markforge-back-cover {
2136
+ page: back-cover-page;
2137
+ page-break-before: always;
2138
+ break-before: page;
2139
+ page-break-after: avoid;
2140
+ break-after: avoid;
2141
+ min-height: 100vh;
2142
+ height: 100vh;
2143
+ max-height: 100vh;
2144
+ margin: 0;
2145
+ box-sizing: border-box;
2146
+ overflow: hidden;
2147
+ -webkit-print-color-adjust: exact;
2148
+ print-color-adjust: exact;
2149
+ }
2150
+ }
2151
+ `;
2152
+ const html = ` <section class="markforge-back-cover back-${backCover.preset}">
2153
+ <div class="back-top">
2154
+ ${logoHtml}
2155
+ ${badgeHtml}
2156
+ </div>
2157
+ <div class="back-body">
2158
+ ${titleHtml}
2159
+ ${subtitleHtml}
2160
+ ${contactHtml}
2161
+ </div>
2162
+ ${copyrightHtml}
2163
+ </section>
2164
+ `;
2165
+ return { html, css };
2166
+ }
2167
+ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
2168
+ var _a, _b;
2169
+ const resolved = resolveDocumentConfig(doc.metadata, config);
2170
+ const baseThemeCss = generateThemeCss(resolved.theme);
2171
+ let customCss = "";
2172
+ for (const cssPath of resolved.css) {
2173
+ const fullCssPath = path2.isAbsolute(cssPath) ? cssPath : path2.resolve(baseDir, cssPath);
2174
+ if (fs2.existsSync(fullCssPath)) {
2175
+ customCss += `
2176
+ /* Custom CSS: ${cssPath} */
2177
+ ` + fs2.readFileSync(fullCssPath, "utf-8");
2178
+ }
2179
+ }
2180
+ const inlinedCss = doc.inlinedStyles.join("\n");
2181
+ const extraCss = `
2182
+ .markforge-columns {
2183
+ display: grid;
2184
+ grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
2185
+ gap: var(--col-gap, 1.5rem);
2186
+ margin: 1.5rem 0;
2187
+ }
2188
+ .markforge-col {
2189
+ min-width: 0;
2190
+ }
2191
+ .markforge-fnref {
2192
+ text-decoration: none;
2193
+ font-size: 0.8em;
2194
+ vertical-align: super;
2195
+ color: #0D998D;
2196
+ font-weight: 700;
2197
+ }
2198
+ .markforge-footnotes {
2199
+ margin-top: 3rem;
2200
+ padding-top: 1rem;
2201
+ font-size: 0.88rem;
2202
+ color: #64748B;
2203
+ }
2204
+ .markforge-footnotes hr {
2205
+ border: 0;
2206
+ border-top: 1px solid #E2E8F0;
2207
+ margin-bottom: 1rem;
2208
+ }
2209
+ .markforge-fn-return {
2210
+ text-decoration: none;
2211
+ color: #0D998D;
2212
+ }
2213
+ .math-block {
2214
+ margin: 1.5rem 0;
2215
+ text-align: center;
2216
+ overflow-x: auto;
2217
+ }
2218
+ `;
2219
+ let coverHtml = "";
2220
+ let coverCss = "";
2221
+ if (resolved.coverPage && resolved.coverPage.enabled) {
2222
+ const coverRes = await renderCoverPageHtml(resolved.coverPage, baseDir);
2223
+ coverHtml = coverRes.html;
2224
+ coverCss = coverRes.css;
2225
+ }
2226
+ let backHtml = "";
2227
+ let backCss = "";
2228
+ if (resolved.backCover && resolved.backCover.enabled) {
2229
+ const backRes = await renderBackCoverHtml(resolved.backCover, baseDir);
2230
+ backHtml = backRes.html;
2231
+ backCss = backRes.css;
2232
+ }
2233
+ let bodyHtml = "";
2234
+ if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
2235
+ bodyHtml += ` <header class="document-header">
2236
+ `;
2237
+ bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
2238
+ `;
2239
+ if (resolved.subtitle) {
2240
+ bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
2241
+ `;
2242
+ }
2243
+ if (resolved.author || resolved.date || resolved.version) {
2244
+ bodyHtml += ` <div class="document-meta">
2245
+ `;
2246
+ if (resolved.author) {
2247
+ bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
2248
+ `;
2249
+ }
2250
+ if (resolved.version) {
2251
+ bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
2252
+ `;
2253
+ }
2254
+ if (resolved.date) {
2255
+ bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
2256
+ `;
2257
+ }
2258
+ bodyHtml += ` </div>
2259
+ `;
2260
+ }
2261
+ bodyHtml += ` </header>
2262
+ `;
2263
+ }
2264
+ if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
2265
+ applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
2266
+ }
2267
+ if (resolved.toc && doc.tocEntries.length > 0) {
2268
+ bodyHtml += ` <nav class="table-of-contents">
2269
+ `;
2270
+ bodyHtml += ` <h2>Table of Contents</h2>
2271
+ <ul>
2272
+ `;
2273
+ for (const entry of doc.tocEntries) {
2274
+ const indent = " ".repeat(entry.level);
2275
+ bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
2276
+ `;
2277
+ }
2278
+ bodyHtml += ` </ul>
2279
+ </nav>
2280
+ `;
2281
+ }
2282
+ const mergedTokens = {
2283
+ ...config.metadata,
2284
+ ...doc.metadata,
2285
+ ...resolved,
2286
+ title: resolved.title,
2287
+ subtitle: resolved.subtitle,
2288
+ author: resolved.author,
2289
+ version: resolved.version,
2290
+ date: resolved.date,
2291
+ company: resolved.company
2292
+ };
2293
+ const nodesHtml = await renderNodesToHtml(doc.nodes, resolved, baseDir, mergedTokens);
2294
+ bodyHtml += ` <main class="markforge-content-body">
2295
+ ${nodesHtml} </main>
2296
+ `;
2297
+ let footnotesHtml = "";
2298
+ if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
2299
+ let fnListHtml = "";
2300
+ for (const def of doc.footnoteDefs) {
2301
+ const defInner = await renderInlinesToHtml(def.inlines, baseDir, mergedTokens);
2302
+ fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">&#8617;</a></li>
2303
+ `;
2304
+ }
2305
+ footnotesHtml = `
2306
+ <footer class="markforge-footnotes">
2307
+ <hr />
2308
+ <ol>
2309
+ ${fnListHtml} </ol>
2310
+ </footer>
2311
+ `;
2312
+ }
1671
2313
  let watermarkCss = "";
1672
2314
  let watermarkHtml = "";
1673
2315
  if (resolved.watermark) {
@@ -1677,12 +2319,16 @@ ${escapeHtml(node.text || "")}
1677
2319
  position: fixed;
1678
2320
  top: 0;
1679
2321
  left: 0;
1680
- width: 100vw;
1681
- height: 100vh;
2322
+ right: 0;
2323
+ bottom: 0;
2324
+ width: 100%;
2325
+ height: 100%;
1682
2326
  pointer-events: none;
1683
2327
  z-index: 0;
1684
2328
  user-select: none;
1685
2329
  -webkit-user-select: none;
2330
+ -webkit-print-color-adjust: exact;
2331
+ print-color-adjust: exact;
1686
2332
  }
1687
2333
  .document-container {
1688
2334
  position: relative;
@@ -1690,13 +2336,7 @@ ${escapeHtml(node.text || "")}
1690
2336
  }
1691
2337
  @media print {
1692
2338
  .document-watermark {
1693
- position: fixed;
1694
- top: 0;
1695
- left: 0;
1696
- width: 100vw;
1697
- height: 100vh;
1698
- -webkit-print-color-adjust: exact;
1699
- print-color-adjust: exact;
2339
+ display: none !important;
1700
2340
  }
1701
2341
  }
1702
2342
  `;
@@ -1705,16 +2345,16 @@ ${escapeHtml(node.text || "")}
1705
2345
  (function() {
1706
2346
  try {
1707
2347
  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);
2348
+ var dpr = 2;
2349
+ var width = 1200;
2350
+ var height = 1600;
2351
+ canvas.width = width * dpr;
2352
+ canvas.height = height * dpr;
1713
2353
  var ctx = canvas.getContext('2d');
1714
2354
  if (ctx) {
1715
2355
  ctx.scale(dpr, dpr);
1716
2356
  ctx.translate(width / 2, height / 2);
1717
- ctx.rotate((${wm.rotate} * Math.PI) / 180);
2357
+ ctx.rotate((-Math.abs(${wm.rotate || 45}) * Math.PI) / 180);
1718
2358
  ctx.textAlign = 'center';
1719
2359
  ctx.textBaseline = 'middle';
1720
2360
  ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
@@ -1741,10 +2381,6 @@ ${escapeHtml(node.text || "")}
1741
2381
  if (resolved.signatures && resolved.signatures.items.length > 0) {
1742
2382
  const sig = resolved.signatures;
1743
2383
  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
2384
  signaturesCss = `
1749
2385
  .markforge-signatures {
1750
2386
  margin-top: ${sig.spacingBefore};
@@ -1856,6 +2492,10 @@ ${itemCards}
1856
2492
  <style>
1857
2493
  ${THEME_COMPONENTS}
1858
2494
  ${baseThemeCss}
2495
+ ${KATEX_INLINE_CSS}
2496
+ ${extraCss}
2497
+ ${coverCss}
2498
+ ${backCss}
1859
2499
  ${customCss}
1860
2500
  ${inlinedCss}
1861
2501
  ${watermarkCss}
@@ -1863,14 +2503,102 @@ ${signaturesCss}
1863
2503
  </style>
1864
2504
  </head>
1865
2505
  <body>
1866
- ${watermarkHtml} <div class="document-container">
1867
- ${bodyHtml}${signaturesHtml} </div>
2506
+ ${watermarkHtml}${coverHtml} <div class="document-container">
2507
+ ${bodyHtml}${footnotesHtml}${signaturesHtml} </div>
1868
2508
  ${mermaidScript}
1869
- </body>
2509
+ ${backHtml}</body>
1870
2510
  </html>`;
1871
2511
  }
1872
2512
 
1873
2513
  // src/core/pdf/pdfBuilder.ts
2514
+ import * as fs3 from "fs";
2515
+ import * as path3 from "path";
2516
+ import * as os from "os";
2517
+ import { pathToFileURL } from "url";
2518
+ import { spawnSync } from "child_process";
2519
+ import { PDFDocument } from "pdf-lib";
2520
+ import { encryptPDF } from "@pdfsmaller/pdf-encrypt";
2521
+ function escapeXml(str) {
2522
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
2523
+ }
2524
+ function generateWatermarkPngBuffer(chromePath, wm) {
2525
+ const tmpHtml = path3.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.html`);
2526
+ const tmpPng = path3.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
2527
+ try {
2528
+ const text = escapeXml(wm.text.toUpperCase());
2529
+ const fontSize = (wm.fontSize || 52) * 1.5;
2530
+ const color = wm.color || "#E11D48";
2531
+ const opacity = wm.opacity !== void 0 ? wm.opacity : 0.12;
2532
+ const rotate = wm.rotate !== void 0 ? wm.rotate : -45;
2533
+ const html = `<!DOCTYPE html>
2534
+ <html>
2535
+ <head>
2536
+ <meta charset="utf-8">
2537
+ <style>
2538
+ html, body {
2539
+ margin: 0;
2540
+ padding: 0;
2541
+ width: 1200px;
2542
+ height: 1600px;
2543
+ background: transparent;
2544
+ overflow: hidden;
2545
+ }
2546
+ .wm-box {
2547
+ width: 1200px;
2548
+ height: 1600px;
2549
+ display: flex;
2550
+ align-items: center;
2551
+ justify-content: center;
2552
+ transform: rotate(${rotate}deg);
2553
+ }
2554
+ .wm-text {
2555
+ font-family: system-ui, -apple-system, sans-serif;
2556
+ font-weight: 900;
2557
+ font-size: ${fontSize}px;
2558
+ color: ${color};
2559
+ opacity: ${opacity};
2560
+ letter-spacing: 0.15em;
2561
+ text-transform: uppercase;
2562
+ white-space: nowrap;
2563
+ }
2564
+ </style>
2565
+ </head>
2566
+ <body>
2567
+ <div class="wm-box"><span class="wm-text">${text}</span></div>
2568
+ </body>
2569
+ </html>`;
2570
+ fs3.writeFileSync(tmpHtml, html, "utf8");
2571
+ const fileUrl = pathToFileURL(tmpHtml).href;
2572
+ const isWin = process.platform === "win32";
2573
+ spawnSync(
2574
+ chromePath,
2575
+ [
2576
+ "--headless=new",
2577
+ "--disable-gpu",
2578
+ "--disable-sync",
2579
+ "--disable-extensions",
2580
+ ...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
2581
+ `--screenshot=${tmpPng}`,
2582
+ "--window-size=1200,1600",
2583
+ "--default-background-color=00000000",
2584
+ fileUrl
2585
+ ],
2586
+ { timeout: 15e3, windowsHide: true }
2587
+ );
2588
+ if (fs3.existsSync(tmpPng) && fs3.statSync(tmpPng).size > 0) {
2589
+ return fs3.readFileSync(tmpPng);
2590
+ }
2591
+ return null;
2592
+ } catch {
2593
+ return null;
2594
+ } finally {
2595
+ try {
2596
+ if (fs3.existsSync(tmpHtml)) fs3.unlinkSync(tmpHtml);
2597
+ if (fs3.existsSync(tmpPng)) fs3.unlinkSync(tmpPng);
2598
+ } catch {
2599
+ }
2600
+ }
2601
+ }
1874
2602
  function findChromeExecutable() {
1875
2603
  if (process.env.CHROME_PATH && fs3.existsSync(process.env.CHROME_PATH)) {
1876
2604
  return process.env.CHROME_PATH;
@@ -1935,7 +2663,7 @@ function findChromeExecutable() {
1935
2663
  return null;
1936
2664
  }
1937
2665
  function injectPagedMediaStyles(html, config, metadata) {
1938
- var _a, _b, _c, _d, _e, _f;
2666
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
1939
2667
  const resolved = resolveDocumentConfig(metadata || {}, config);
1940
2668
  const size = resolved.paperSize;
1941
2669
  const orientation = resolved.orientation;
@@ -1979,6 +2707,66 @@ function injectPagedMediaStyles(html, config, metadata) {
1979
2707
  ${fontStyle}
1980
2708
  }`;
1981
2709
  };
2710
+ const coverPageCss = ((_a = resolved.coverPage) == null ? void 0 : _a.enabled) ? `
2711
+ @page :first {
2712
+ margin-top: 0;
2713
+ margin-bottom: 0;
2714
+ margin-left: 0;
2715
+ margin-right: 0;
2716
+ background-image: none !important;
2717
+ @top-left { content: none; }
2718
+ @top-center { content: none; }
2719
+ @top-right { content: none; }
2720
+ @bottom-left { content: none; }
2721
+ @bottom-center { content: none; }
2722
+ @bottom-right { content: none; }
2723
+ }` : "";
2724
+ const backCoverCss = ((_b = resolved.backCover) == null ? void 0 : _b.enabled) ? `
2725
+ @page back-cover-page {
2726
+ size: ${size} ${orientation};
2727
+ margin: 0;
2728
+ background-image: none !important;
2729
+ @top-left { content: none; }
2730
+ @top-center { content: none; }
2731
+ @top-right { content: none; }
2732
+ @bottom-left { content: none; }
2733
+ @bottom-center { content: none; }
2734
+ @bottom-right { content: none; }
2735
+ }
2736
+ .markforge-back-cover {
2737
+ page: back-cover-page;
2738
+ min-height: 100vh;
2739
+ height: 100vh;
2740
+ box-sizing: border-box;
2741
+ break-before: page;
2742
+ break-after: avoid;
2743
+ }` : "";
2744
+ const tocPageCss = resolved.toc ? `
2745
+ @page toc-page {
2746
+ size: ${size} ${orientation};
2747
+ margin-top: ${top};
2748
+ margin-bottom: ${bottom};
2749
+ margin-left: ${left};
2750
+ margin-right: ${right};
2751
+ ${buildZoneCss("top-left", (_c = resolved.header) == null ? void 0 : _c.left)}
2752
+ ${buildZoneCss("top-center", (_d = resolved.header) == null ? void 0 : _d.center)}
2753
+ ${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
2754
+ ${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
2755
+ ${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
2756
+ @bottom-right {
2757
+ content: counter(page, lower-roman);
2758
+ font-size: 9pt;
2759
+ color: #94a3b8;
2760
+ }
2761
+ }
2762
+ .table-of-contents {
2763
+ page: toc-page;
2764
+ page-break-after: always;
2765
+ break-after: page;
2766
+ }
2767
+ .markforge-content-body {
2768
+ counter-reset: page 1;
2769
+ }` : "";
1982
2770
  const pagedCss = `
1983
2771
  @page {
1984
2772
  size: ${size} ${orientation};
@@ -1986,15 +2774,19 @@ function injectPagedMediaStyles(html, config, metadata) {
1986
2774
  margin-bottom: ${bottom};
1987
2775
  margin-left: ${left};
1988
2776
  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)}
2777
+ ${buildZoneCss("top-left", (_h = resolved.header) == null ? void 0 : _h.left)}
2778
+ ${buildZoneCss("top-center", (_i = resolved.header) == null ? void 0 : _i.center)}
2779
+ ${buildZoneCss("top-right", (_j = resolved.header) == null ? void 0 : _j.right)}
2780
+ ${buildZoneCss("bottom-left", (_k = resolved.footer) == null ? void 0 : _k.left)}
2781
+ ${buildZoneCss("bottom-center", (_l = resolved.footer) == null ? void 0 : _l.center)}
2782
+ ${buildZoneCss("bottom-right", (_m = resolved.footer) == null ? void 0 : _m.right, true)}
1995
2783
  }
2784
+ ${coverPageCss}
2785
+ ${tocPageCss}
2786
+ ${backCoverCss}
1996
2787
  @media print {
1997
2788
  body { padding: 0; }
2789
+ .document-watermark { display: none !important; }
1998
2790
  h1, h2, h3, pre, table, blockquote, .callout {
1999
2791
  break-inside: avoid;
2000
2792
  }
@@ -2042,15 +2834,17 @@ startxref
2042
2834
  return Buffer.from(pdfBody, "utf-8");
2043
2835
  }
2044
2836
  async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2837
+ var _a, _b, _c;
2045
2838
  const baseHtml = await buildHtmlDocument(doc, config, baseDir);
2046
2839
  const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
2047
2840
  const chromePath = findChromeExecutable();
2048
2841
  if (chromePath) {
2049
2842
  const tmpId = Math.random().toString(36).substring(2, 9);
2050
2843
  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}`);
2844
+ const tmpHtml = path3.join(tmpDir, `markforge_${tmpId}.html`);
2845
+ const tmpPdf = path3.join(tmpDir, `markforge_${tmpId}.pdf`);
2846
+ const tmpProfile = path3.join(tmpDir, `markforge_prof_${tmpId}`);
2847
+ const isWin = process.platform === "win32";
2054
2848
  const isolatedFlags = [
2055
2849
  `--user-data-dir=${tmpProfile}`,
2056
2850
  "--no-first-run",
@@ -2061,7 +2855,6 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2061
2855
  "--disable-default-apps",
2062
2856
  "--disable-extensions",
2063
2857
  "--disable-domain-reliability",
2064
- "--disable-client-side-phishing-detection",
2065
2858
  "--disable-breakpad",
2066
2859
  "--disable-component-extensions-with-background-pages",
2067
2860
  "--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
@@ -2070,12 +2863,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2070
2863
  "--mute-audio",
2071
2864
  "--no-service-autorun",
2072
2865
  "--disable-gpu",
2073
- "--no-sandbox",
2074
- "--disable-setuid-sandbox",
2075
- "--allow-file-access-from-files",
2076
- "--disable-web-security",
2866
+ ...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
2077
2867
  "--force-color-profile=srgb",
2078
- "--no-pdf-header-footer"
2868
+ "--no-pdf-header-footer",
2869
+ "--window-size=1200,1600"
2079
2870
  ];
2080
2871
  try {
2081
2872
  fs3.writeFileSync(tmpHtml, pagedHtml, "utf-8");
@@ -2090,7 +2881,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2090
2881
  `--print-to-pdf=${tmpPdf}`,
2091
2882
  fileUrl
2092
2883
  ],
2093
- { timeout: 3e4 }
2884
+ { timeout: 3e4, windowsHide: true }
2094
2885
  );
2095
2886
  if ((res.status !== 0 || !fs3.existsSync(tmpPdf)) && chromePath) {
2096
2887
  res = spawnSync(
@@ -2101,12 +2892,79 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2101
2892
  `--print-to-pdf=${tmpPdf}`,
2102
2893
  fileUrl
2103
2894
  ],
2104
- { timeout: 3e4 }
2895
+ { timeout: 3e4, windowsHide: true }
2105
2896
  );
2106
2897
  }
2107
2898
  if (fs3.existsSync(tmpPdf) && fs3.statSync(tmpPdf).size > 0) {
2108
2899
  const pdfBuffer = fs3.readFileSync(tmpPdf);
2109
- return pdfBuffer;
2900
+ try {
2901
+ const pdfDoc = await PDFDocument.load(pdfBuffer);
2902
+ const resolved = resolveDocumentConfig(doc.metadata, config);
2903
+ if (resolved.title) pdfDoc.setTitle(resolved.title);
2904
+ if (resolved.author) pdfDoc.setAuthor(resolved.author);
2905
+ if (resolved.subtitle) pdfDoc.setSubject(resolved.subtitle);
2906
+ pdfDoc.setCreator("MarkForge Enterprise Document Generator");
2907
+ pdfDoc.setProducer("MarkForge (by Ma'sum)");
2908
+ pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
2909
+ if (((_a = resolved.backCover) == null ? void 0 : _a.enabled) && pdfDoc.getPageCount() > 2) {
2910
+ pdfDoc.removePage(pdfDoc.getPageCount() - 1);
2911
+ }
2912
+ if (resolved.watermark) {
2913
+ const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
2914
+ if (wmPng) {
2915
+ const embeddedPng = await pdfDoc.embedPng(wmPng);
2916
+ const totalPages = pdfDoc.getPageCount();
2917
+ const pages = pdfDoc.getPages();
2918
+ const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
2919
+ const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? totalPages - 1 : totalPages;
2920
+ for (let i = startPageIndex; i < endPageIndex; i++) {
2921
+ const page = pages[i];
2922
+ const { width, height } = page.getSize();
2923
+ page.drawImage(embeddedPng, {
2924
+ x: 0,
2925
+ y: 0,
2926
+ width,
2927
+ height
2928
+ });
2929
+ }
2930
+ }
2931
+ }
2932
+ const savedBytes = await pdfDoc.save();
2933
+ let finalBuffer = Buffer.from(savedBytes);
2934
+ if (resolved.security) {
2935
+ const sec = resolved.security;
2936
+ const hasUserPassword = typeof sec.userPassword === "string" && sec.userPassword.length > 0;
2937
+ const hasOwnerPassword = typeof sec.ownerPassword === "string" && sec.ownerPassword.length > 0;
2938
+ if (hasUserPassword || hasOwnerPassword) {
2939
+ try {
2940
+ const userPass = sec.userPassword ?? "";
2941
+ const ownerPass = sec.ownerPassword ?? userPass;
2942
+ const perms = sec.permissions;
2943
+ const encryptedBytes = await encryptPDF(
2944
+ new Uint8Array(finalBuffer),
2945
+ userPass,
2946
+ {
2947
+ ownerPassword: ownerPass,
2948
+ algorithm: "AES-256",
2949
+ allowPrinting: (perms == null ? void 0 : perms.printing) !== "none",
2950
+ allowHighQualityPrint: (perms == null ? void 0 : perms.printing) === "highResolution",
2951
+ allowModifying: (perms == null ? void 0 : perms.modifying) ?? true,
2952
+ allowCopying: (perms == null ? void 0 : perms.copying) ?? true,
2953
+ allowAnnotating: (perms == null ? void 0 : perms.annotating) ?? true,
2954
+ allowFillingForms: (perms == null ? void 0 : perms.fillingForms) ?? true,
2955
+ allowExtraction: (perms == null ? void 0 : perms.contentAccessibility) ?? true,
2956
+ allowAssembly: (perms == null ? void 0 : perms.documentAssembly) ?? true
2957
+ }
2958
+ );
2959
+ finalBuffer = Buffer.from(encryptedBytes);
2960
+ } catch {
2961
+ }
2962
+ }
2963
+ }
2964
+ return finalBuffer;
2965
+ } catch {
2966
+ return pdfBuffer;
2967
+ }
2110
2968
  }
2111
2969
  } catch {
2112
2970
  } finally {
@@ -2121,7 +2979,39 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
2121
2979
  return createFallbackPdfBuffer(doc.metadata.title || "MarkForge Document");
2122
2980
  }
2123
2981
 
2982
+ // src/core/docx/docxBuilder.ts
2983
+ import {
2984
+ Document,
2985
+ Paragraph,
2986
+ TextRun,
2987
+ HeadingLevel,
2988
+ Table,
2989
+ TableRow,
2990
+ TableCell,
2991
+ WidthType,
2992
+ BorderStyle,
2993
+ AlignmentType,
2994
+ ImageRun,
2995
+ Header,
2996
+ Footer,
2997
+ PageNumber,
2998
+ Packer,
2999
+ PageOrientation,
3000
+ convertInchesToTwip,
3001
+ convertMillimetersToTwip,
3002
+ ShadingType,
3003
+ ExternalHyperlink,
3004
+ TabStopType,
3005
+ PageBreak,
3006
+ NumberFormat
3007
+ } from "docx";
3008
+
2124
3009
  // src/core/mermaid/mermaidRenderer.ts
3010
+ import { spawnSync as spawnSync2 } from "child_process";
3011
+ import * as fs4 from "fs";
3012
+ import * as os2 from "os";
3013
+ import * as path4 from "path";
3014
+ import { pathToFileURL as pathToFileURL2 } from "url";
2125
3015
  async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
2126
3016
  const chromePath = findChromeExecutable();
2127
3017
  if (!chromePath) {
@@ -2129,8 +3019,8 @@ async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
2129
3019
  }
2130
3020
  const tmpId = Math.random().toString(36).substring(2, 9);
2131
3021
  const tmpDir = os2.tmpdir();
2132
- const tmpHtml = path6.join(tmpDir, `mermaid_${tmpId}.html`);
2133
- const tmpScreenshot = path6.join(tmpDir, `mermaid_${tmpId}.png`);
3022
+ const tmpHtml = path4.join(tmpDir, `mermaid_${tmpId}.html`);
3023
+ const tmpScreenshot = path4.join(tmpDir, `mermaid_${tmpId}.png`);
2134
3024
  const htmlContent = `<!DOCTYPE html>
2135
3025
  <html>
2136
3026
  <head>
@@ -2338,6 +3228,29 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
2338
3228
  );
2339
3229
  continue;
2340
3230
  }
3231
+ if (span.type === "mathInline") {
3232
+ runs.push(
3233
+ new TextRun({
3234
+ text: span.content,
3235
+ font: "Cambria Math",
3236
+ italics: true,
3237
+ size: options.size,
3238
+ color: options.color || "0F172A"
3239
+ })
3240
+ );
3241
+ continue;
3242
+ }
3243
+ if (span.type === "footnoteRef") {
3244
+ runs.push(
3245
+ new TextRun({
3246
+ text: `[${span.content}]`,
3247
+ superScript: true,
3248
+ color: "009DA0",
3249
+ bold: true
3250
+ })
3251
+ );
3252
+ continue;
3253
+ }
2341
3254
  runs.push(
2342
3255
  new TextRun({
2343
3256
  text: span.content,
@@ -2352,7 +3265,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
2352
3265
  return runs;
2353
3266
  }
2354
3267
  async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2355
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
3268
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2356
3269
  const resolved = resolveDocumentConfig(doc.metadata, config);
2357
3270
  const docElements = [];
2358
3271
  const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
@@ -2363,7 +3276,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2363
3276
  const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
2364
3277
  const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
2365
3278
  const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
2366
- if (resolved.title) {
3279
+ if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
2367
3280
  docElements.push(
2368
3281
  new Paragraph({
2369
3282
  children: [
@@ -2424,6 +3337,9 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2424
3337
  );
2425
3338
  }
2426
3339
  }
3340
+ if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
3341
+ applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
3342
+ }
2427
3343
  if (resolved.toc) {
2428
3344
  const headingNodes = doc.nodes.filter(
2429
3345
  (n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
@@ -2493,7 +3409,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2493
3409
  ]
2494
3410
  });
2495
3411
  docElements.push(tocCard);
2496
- docElements.push(new Paragraph({ spacing: { after: 200 } }));
3412
+ docElements.push(
3413
+ new Paragraph({
3414
+ children: [new PageBreak()]
3415
+ })
3416
+ );
2497
3417
  }
2498
3418
  }
2499
3419
  for (const node of doc.nodes) {
@@ -2503,7 +3423,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2503
3423
  font: defaultFont,
2504
3424
  size: 34,
2505
3425
  // 17pt
2506
- color: textHex,
3426
+ color: primaryDarkHex,
2507
3427
  bold: true
2508
3428
  });
2509
3429
  docElements.push(
@@ -2549,7 +3469,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2549
3469
  font: defaultFont,
2550
3470
  size: 22,
2551
3471
  // 11pt
2552
- color: textHex,
3472
+ color: primaryDarkHex,
2553
3473
  bold: true
2554
3474
  });
2555
3475
  docElements.push(
@@ -2779,7 +3699,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2779
3699
  }
2780
3700
  if (node.type === "table" && node.children) {
2781
3701
  const tableRows = [];
2782
- const numCols = ((_b = (_a = node.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) || 1;
3702
+ const numCols = ((_d = (_c = node.children[0]) == null ? void 0 : _c.children) == null ? void 0 : _d.length) || 1;
2783
3703
  const colWidth = Math.floor(9e3 / numCols);
2784
3704
  for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
2785
3705
  const rowNode = node.children[rowIdx];
@@ -2789,7 +3709,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2789
3709
  if (rowNode.children) {
2790
3710
  for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
2791
3711
  const cellNode = rowNode.children[colIdx];
2792
- const align = (_c = node.align) == null ? void 0 : _c[colIdx];
3712
+ const align = (_e = node.align) == null ? void 0 : _e[colIdx];
2793
3713
  let alignment = AlignmentType.LEFT;
2794
3714
  if (align === "center") alignment = AlignmentType.CENTER;
2795
3715
  if (align === "right") alignment = AlignmentType.RIGHT;
@@ -2928,6 +3848,113 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2928
3848
  }
2929
3849
  continue;
2930
3850
  }
3851
+ if (node.type === "mathBlock") {
3852
+ docElements.push(
3853
+ new Paragraph({
3854
+ alignment: AlignmentType.CENTER,
3855
+ children: [
3856
+ new TextRun({
3857
+ text: node.text || "",
3858
+ font: "Cambria Math",
3859
+ italics: true,
3860
+ size: 24,
3861
+ // 12pt
3862
+ color: textHex
3863
+ })
3864
+ ],
3865
+ spacing: { before: 180, after: 180 },
3866
+ shading: { fill: cardBgHex, type: ShadingType.CLEAR },
3867
+ border: {
3868
+ top: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
3869
+ bottom: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
3870
+ left: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
3871
+ right: { style: BorderStyle.SINGLE, size: 4, color: borderHex }
3872
+ }
3873
+ })
3874
+ );
3875
+ continue;
3876
+ }
3877
+ if (node.type === "columns") {
3878
+ const cols = node.columnsCount || 2;
3879
+ const contentWidth = Math.max(
3880
+ 1e3,
3881
+ resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
3882
+ );
3883
+ const cellWidthDxa = Math.floor(contentWidth / cols);
3884
+ const cells = [];
3885
+ for (const col of node.children || []) {
3886
+ const colParagraphs = [];
3887
+ for (const childNode of col.children || []) {
3888
+ if (childNode.type === "heading") {
3889
+ const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, bold: true, size: 24, color: primaryDarkHex });
3890
+ colParagraphs.push(new Paragraph({ children: runs, spacing: { before: 120, after: 60 } }));
3891
+ } else if (childNode.type === "paragraph") {
3892
+ const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
3893
+ colParagraphs.push(new Paragraph({ children: runs, spacing: { after: 100 } }));
3894
+ } else if (childNode.type === "list" && childNode.children) {
3895
+ for (const item of childNode.children) {
3896
+ const runs = await convertInlinesToTextRuns(item.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
3897
+ colParagraphs.push(new Paragraph({ children: [new TextRun({ text: "\u2022 ", font: defaultFont, color: primaryHex }), ...runs], spacing: { after: 40 } }));
3898
+ }
3899
+ }
3900
+ }
3901
+ if (colParagraphs.length === 0) colParagraphs.push(new Paragraph({}));
3902
+ cells.push(
3903
+ new TableCell({
3904
+ width: { size: cellWidthDxa, type: WidthType.DXA },
3905
+ borders: {
3906
+ top: { style: BorderStyle.NONE, size: 0, color: "auto" },
3907
+ bottom: { style: BorderStyle.NONE, size: 0, color: "auto" },
3908
+ left: { style: BorderStyle.NONE, size: 0, color: "auto" },
3909
+ right: { style: BorderStyle.NONE, size: 0, color: "auto" }
3910
+ },
3911
+ margins: { top: 60, bottom: 60, left: 100, right: 100 },
3912
+ children: colParagraphs
3913
+ })
3914
+ );
3915
+ }
3916
+ docElements.push(
3917
+ new Table({
3918
+ width: { size: 100, type: WidthType.PERCENTAGE },
3919
+ rows: [new TableRow({ children: cells })]
3920
+ })
3921
+ );
3922
+ docElements.push(new Paragraph({ spacing: { after: 120 } }));
3923
+ continue;
3924
+ }
3925
+ }
3926
+ if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
3927
+ docElements.push(
3928
+ new Paragraph({
3929
+ border: {
3930
+ top: { style: BorderStyle.SINGLE, size: 4, color: borderHex, space: 8 }
3931
+ },
3932
+ spacing: { before: 360, after: 120 }
3933
+ })
3934
+ );
3935
+ for (const def of doc.footnoteDefs) {
3936
+ const defRuns = await convertInlinesToTextRuns(def.inlines, baseDir, {
3937
+ font: defaultFont,
3938
+ size: 18,
3939
+ // 9pt
3940
+ color: textMutedHex
3941
+ });
3942
+ docElements.push(
3943
+ new Paragraph({
3944
+ children: [
3945
+ new TextRun({
3946
+ text: `[${def.id}] `,
3947
+ bold: true,
3948
+ color: primaryDarkHex,
3949
+ font: defaultFont,
3950
+ size: 18
3951
+ }),
3952
+ ...defRuns
3953
+ ],
3954
+ spacing: { after: 60 }
3955
+ })
3956
+ );
3957
+ }
2931
3958
  }
2932
3959
  if (resolved.signatures && resolved.signatures.items.length > 0) {
2933
3960
  const sig = resolved.signatures;
@@ -2989,7 +4016,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
2989
4016
  const centerPos = Math.round(contentWidthTwip / 2);
2990
4017
  const rightPos = contentWidthTwip;
2991
4018
  const headerRuns = [];
2992
- if ((_d = resolved.header) == null ? void 0 : _d.left) {
4019
+ if ((_f = resolved.header) == null ? void 0 : _f.left) {
2993
4020
  headerRuns.push(
2994
4021
  new TextRun({
2995
4022
  text: resolved.header.left.text,
@@ -3002,7 +4029,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3002
4029
  );
3003
4030
  }
3004
4031
  headerRuns.push(new TextRun({ text: " " }));
3005
- if ((_e = resolved.header) == null ? void 0 : _e.center) {
4032
+ if ((_g = resolved.header) == null ? void 0 : _g.center) {
3006
4033
  headerRuns.push(
3007
4034
  new TextRun({
3008
4035
  text: resolved.header.center.text,
@@ -3015,7 +4042,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3015
4042
  );
3016
4043
  }
3017
4044
  headerRuns.push(new TextRun({ text: " " }));
3018
- if ((_f = resolved.header) == null ? void 0 : _f.right) {
4045
+ if ((_h = resolved.header) == null ? void 0 : _h.right) {
3019
4046
  headerRuns.push(
3020
4047
  new TextRun({
3021
4048
  text: resolved.header.right.text,
@@ -3027,7 +4054,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3027
4054
  })
3028
4055
  );
3029
4056
  }
3030
- const docHeader = resolved.header ? new Header2({
4057
+ const docHeader = resolved.header ? new Header({
3031
4058
  children: [
3032
4059
  new Paragraph({
3033
4060
  tabStops: [
@@ -3054,7 +4081,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3054
4081
  ]
3055
4082
  }) : void 0;
3056
4083
  const footerRuns = [];
3057
- if ((_g = resolved.footer) == null ? void 0 : _g.left) {
4084
+ if ((_i = resolved.footer) == null ? void 0 : _i.left) {
3058
4085
  footerRuns.push(
3059
4086
  new TextRun({
3060
4087
  text: resolved.footer.left.text,
@@ -3067,7 +4094,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3067
4094
  );
3068
4095
  }
3069
4096
  footerRuns.push(new TextRun({ text: " " }));
3070
- if ((_h = resolved.footer) == null ? void 0 : _h.center) {
4097
+ if ((_j = resolved.footer) == null ? void 0 : _j.center) {
3071
4098
  footerRuns.push(
3072
4099
  new TextRun({
3073
4100
  text: resolved.footer.center.text,
@@ -3080,7 +4107,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3080
4107
  );
3081
4108
  }
3082
4109
  footerRuns.push(new TextRun({ text: " " }));
3083
- if ((_i = resolved.footer) == null ? void 0 : _i.right) {
4110
+ if ((_k = resolved.footer) == null ? void 0 : _k.right) {
3084
4111
  const rZone = resolved.footer.right;
3085
4112
  const rColor = rZone.color.replace("#", "");
3086
4113
  const rSize = (rZone.fontSize || 9) * 2;
@@ -3165,6 +4192,95 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3165
4192
  ]
3166
4193
  }) : void 0;
3167
4194
  const isLandscape = resolved.orientation === "landscape";
4195
+ const docSections = [];
4196
+ if (resolved.coverPage && resolved.coverPage.enabled) {
4197
+ const coverElements = await buildDocxCoverPageElements(
4198
+ resolved.coverPage,
4199
+ defaultFont,
4200
+ textHex,
4201
+ primaryHex,
4202
+ primaryDarkHex,
4203
+ textMutedHex,
4204
+ baseDir
4205
+ );
4206
+ docSections.push({
4207
+ properties: {
4208
+ page: {
4209
+ size: {
4210
+ width: resolved.paperDimensions.widthTwip,
4211
+ height: resolved.paperDimensions.heightTwip,
4212
+ orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
4213
+ },
4214
+ margin: {
4215
+ top: resolved.margins.topTwip,
4216
+ bottom: resolved.margins.bottomTwip,
4217
+ left: resolved.margins.leftTwip,
4218
+ right: resolved.margins.rightTwip
4219
+ }
4220
+ }
4221
+ },
4222
+ headers: void 0,
4223
+ footers: void 0,
4224
+ children: coverElements
4225
+ });
4226
+ }
4227
+ docSections.push({
4228
+ properties: {
4229
+ page: {
4230
+ pageNumbers: {
4231
+ start: 1,
4232
+ formatType: NumberFormat.DECIMAL
4233
+ },
4234
+ size: {
4235
+ width: resolved.paperDimensions.widthTwip,
4236
+ height: resolved.paperDimensions.heightTwip,
4237
+ orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
4238
+ },
4239
+ margin: {
4240
+ top: resolved.margins.topTwip,
4241
+ bottom: resolved.margins.bottomTwip,
4242
+ left: resolved.margins.leftTwip,
4243
+ right: resolved.margins.rightTwip,
4244
+ header: 720,
4245
+ footer: 720
4246
+ }
4247
+ }
4248
+ },
4249
+ headers: docHeader ? { default: docHeader } : void 0,
4250
+ footers: docFooter ? { default: docFooter } : void 0,
4251
+ children: docElements
4252
+ });
4253
+ if (resolved.backCover && resolved.backCover.enabled) {
4254
+ const backElements = await buildDocxBackCoverElements(
4255
+ resolved.backCover,
4256
+ defaultFont,
4257
+ textHex,
4258
+ primaryHex,
4259
+ primaryDarkHex,
4260
+ textMutedHex,
4261
+ baseDir
4262
+ );
4263
+ docSections.push({
4264
+ properties: {
4265
+ page: {
4266
+ size: {
4267
+ width: resolved.paperDimensions.widthTwip,
4268
+ height: resolved.paperDimensions.heightTwip,
4269
+ orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
4270
+ },
4271
+ margin: {
4272
+ top: resolved.margins.topTwip,
4273
+ bottom: resolved.margins.bottomTwip,
4274
+ left: resolved.margins.leftTwip,
4275
+ right: resolved.margins.rightTwip
4276
+ }
4277
+ }
4278
+ },
4279
+ headers: void 0,
4280
+ footers: void 0,
4281
+ children: backElements
4282
+ });
4283
+ }
3168
4284
  const document = new Document({
3169
4285
  styles: {
3170
4286
  default: {
@@ -3185,33 +4301,250 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
3185
4301
  }
3186
4302
  }
3187
4303
  },
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
- ]
4304
+ sections: docSections
3212
4305
  });
3213
4306
  return await Packer.toBuffer(document);
3214
4307
  }
4308
+ async function buildDocxBackCoverElements(backCover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
4309
+ var _a;
4310
+ const elements = [];
4311
+ elements.push(new Paragraph({ spacing: { before: 1800 } }));
4312
+ if (backCover.logo) {
4313
+ const resolvedLogo = await resolveImage(backCover.logo, baseDir);
4314
+ if (resolvedLogo) {
4315
+ const logoW = typeof backCover.logoWidth === "number" ? backCover.logoWidth : 140;
4316
+ const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
4317
+ elements.push(
4318
+ new Paragraph({
4319
+ children: [
4320
+ new ImageRun({
4321
+ data: resolvedLogo.buffer,
4322
+ transformation: {
4323
+ width: logoW,
4324
+ height: Math.round(logoW * 0.75)
4325
+ },
4326
+ type: logoType
4327
+ })
4328
+ ],
4329
+ spacing: { after: 240 }
4330
+ })
4331
+ );
4332
+ }
4333
+ }
4334
+ if (backCover.badge) {
4335
+ elements.push(
4336
+ new Paragraph({
4337
+ children: [
4338
+ new TextRun({
4339
+ text: `[ ${backCover.badge.toUpperCase()} ]`,
4340
+ font: defaultFont,
4341
+ size: 20,
4342
+ bold: true,
4343
+ color: primaryDarkHex
4344
+ })
4345
+ ],
4346
+ spacing: { after: 240 }
4347
+ })
4348
+ );
4349
+ }
4350
+ elements.push(
4351
+ new Paragraph({
4352
+ children: [
4353
+ new TextRun({
4354
+ text: backCover.title,
4355
+ font: defaultFont,
4356
+ size: 52,
4357
+ // 26pt
4358
+ bold: true,
4359
+ color: textHex
4360
+ })
4361
+ ],
4362
+ spacing: { after: 140 }
4363
+ })
4364
+ );
4365
+ if (backCover.subtitle) {
4366
+ elements.push(
4367
+ new Paragraph({
4368
+ children: [
4369
+ new TextRun({
4370
+ text: backCover.subtitle,
4371
+ font: defaultFont,
4372
+ size: 24,
4373
+ // 12pt
4374
+ color: textMutedHex
4375
+ })
4376
+ ],
4377
+ spacing: { after: 480 }
4378
+ })
4379
+ );
4380
+ }
4381
+ elements.push(
4382
+ new Paragraph({
4383
+ border: {
4384
+ bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
4385
+ },
4386
+ spacing: { after: 480 }
4387
+ })
4388
+ );
4389
+ const contactRuns = [];
4390
+ if (backCover.company) contactRuns.push(new TextRun({ text: `Organization: ${backCover.company}
4391
+ `, font: defaultFont, size: 21, color: textHex }));
4392
+ if (backCover.address) contactRuns.push(new TextRun({ text: `Address: ${backCover.address}
4393
+ `, font: defaultFont, size: 21, color: textHex }));
4394
+ if (backCover.email) contactRuns.push(new TextRun({ text: `Email: ${backCover.email}
4395
+ `, font: defaultFont, size: 21, color: textHex }));
4396
+ if (backCover.phone) contactRuns.push(new TextRun({ text: `Phone: ${backCover.phone}
4397
+ `, font: defaultFont, size: 21, color: textHex }));
4398
+ if (backCover.website) contactRuns.push(new TextRun({ text: `Website: ${backCover.website}
4399
+ `, font: defaultFont, size: 21, color: textHex }));
4400
+ if (backCover.social) {
4401
+ for (const [net, url] of Object.entries(backCover.social)) {
4402
+ if (url) {
4403
+ contactRuns.push(new TextRun({ text: `${net.toUpperCase()}: ${url}
4404
+ `, font: defaultFont, size: 21, color: textHex }));
4405
+ }
4406
+ }
4407
+ }
4408
+ if (contactRuns.length > 0) {
4409
+ elements.push(
4410
+ new Paragraph({
4411
+ children: contactRuns,
4412
+ spacing: { before: 360, after: 360 }
4413
+ })
4414
+ );
4415
+ }
4416
+ if (backCover.copyright) {
4417
+ elements.push(
4418
+ new Paragraph({
4419
+ children: [
4420
+ new TextRun({
4421
+ text: backCover.copyright,
4422
+ font: defaultFont,
4423
+ size: 18,
4424
+ color: "94A3B8"
4425
+ })
4426
+ ],
4427
+ spacing: { before: 720 }
4428
+ })
4429
+ );
4430
+ }
4431
+ return elements;
4432
+ }
4433
+ async function buildDocxCoverPageElements(cover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
4434
+ var _a;
4435
+ const elements = [];
4436
+ elements.push(new Paragraph({ spacing: { before: 1800 } }));
4437
+ if (cover.logo) {
4438
+ const resolvedLogo = await resolveImage(cover.logo, baseDir);
4439
+ if (resolvedLogo) {
4440
+ const logoW = typeof cover.logoWidth === "number" ? cover.logoWidth : 140;
4441
+ const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
4442
+ elements.push(
4443
+ new Paragraph({
4444
+ children: [
4445
+ new ImageRun({
4446
+ data: resolvedLogo.buffer,
4447
+ transformation: {
4448
+ width: logoW,
4449
+ height: Math.round(logoW * 0.75)
4450
+ },
4451
+ type: logoType
4452
+ })
4453
+ ],
4454
+ spacing: { after: 240 }
4455
+ })
4456
+ );
4457
+ }
4458
+ }
4459
+ if (cover.badge) {
4460
+ elements.push(
4461
+ new Paragraph({
4462
+ children: [
4463
+ new TextRun({
4464
+ text: `[ ${cover.badge.toUpperCase()} ]`,
4465
+ font: defaultFont,
4466
+ size: 20,
4467
+ bold: true,
4468
+ color: primaryDarkHex
4469
+ })
4470
+ ],
4471
+ spacing: { after: 240 }
4472
+ })
4473
+ );
4474
+ }
4475
+ elements.push(
4476
+ new Paragraph({
4477
+ children: [
4478
+ new TextRun({
4479
+ text: cover.title,
4480
+ font: defaultFont,
4481
+ size: 56,
4482
+ // 28pt
4483
+ bold: true,
4484
+ color: textHex
4485
+ })
4486
+ ],
4487
+ spacing: { after: 140 }
4488
+ })
4489
+ );
4490
+ if (cover.subtitle) {
4491
+ elements.push(
4492
+ new Paragraph({
4493
+ children: [
4494
+ new TextRun({
4495
+ text: cover.subtitle,
4496
+ font: defaultFont,
4497
+ size: 26,
4498
+ // 13pt
4499
+ color: textMutedHex
4500
+ })
4501
+ ],
4502
+ spacing: { after: 480 }
4503
+ })
4504
+ );
4505
+ }
4506
+ elements.push(
4507
+ new Paragraph({
4508
+ border: {
4509
+ bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
4510
+ },
4511
+ spacing: { after: 480 }
4512
+ })
4513
+ );
4514
+ if (cover.company || cover.author || cover.version || cover.date) {
4515
+ const metaRuns = [];
4516
+ if (cover.company) metaRuns.push(new TextRun({ text: `Organization: ${cover.company}
4517
+ `, font: defaultFont, size: 21, color: textHex }));
4518
+ if (cover.author) metaRuns.push(new TextRun({ text: `Author: ${cover.author}
4519
+ `, font: defaultFont, size: 21, color: textHex }));
4520
+ if (cover.version) metaRuns.push(new TextRun({ text: `Version: ${cover.version}
4521
+ `, font: defaultFont, size: 21, color: textHex }));
4522
+ if (cover.date) metaRuns.push(new TextRun({ text: `Date: ${cover.date}
4523
+ `, font: defaultFont, size: 21, color: textHex }));
4524
+ elements.push(
4525
+ new Paragraph({
4526
+ children: metaRuns,
4527
+ spacing: { before: 360, after: 360 }
4528
+ })
4529
+ );
4530
+ }
4531
+ if (cover.footerText) {
4532
+ elements.push(
4533
+ new Paragraph({
4534
+ children: [
4535
+ new TextRun({
4536
+ text: cover.footerText,
4537
+ font: defaultFont,
4538
+ size: 18,
4539
+ color: "94A3B8"
4540
+ })
4541
+ ],
4542
+ spacing: { before: 720 }
4543
+ })
4544
+ );
4545
+ }
4546
+ return elements;
4547
+ }
3215
4548
  async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
3216
4549
  const cellParagraphs = [];
3217
4550
  if (item.title) {
@@ -3347,126 +4680,9 @@ function createEmptyDocxCell(widthDxa) {
3347
4680
  });
3348
4681
  }
3349
4682
 
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
4683
  export {
3471
- App
4684
+ parseMarkdownDocument,
4685
+ buildHtmlDocument,
4686
+ buildPdfDocument,
4687
+ buildDocxDocument
3472
4688
  };