@masumdev/markforge 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2475 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ DEFAULT_CONFIG: () => DEFAULT_CONFIG,
34
+ MARKFORGE_VERSION: () => MARKFORGE_VERSION,
35
+ SYNTAX_COLORS: () => SYNTAX_COLORS,
36
+ THEMES: () => THEMES,
37
+ THEME_ACADEMIC: () => THEME_ACADEMIC,
38
+ THEME_DEFAULT: () => THEME_DEFAULT,
39
+ buildDocxDocument: () => buildDocxDocument,
40
+ buildHtmlDocument: () => buildHtmlDocument,
41
+ buildPdfDocument: () => buildPdfDocument,
42
+ compileMarkdown: () => compileMarkdown,
43
+ defineConfig: () => defineConfig,
44
+ escapeHtml: () => escapeHtml,
45
+ findChromeExecutable: () => findChromeExecutable,
46
+ formatServerTimestamp: () => formatServerTimestamp,
47
+ getMarkforgeVersion: () => getMarkforgeVersion,
48
+ getMimeType: () => getMimeType,
49
+ highlightCodeToHtml: () => highlightCodeToHtml,
50
+ injectPagedMediaStyles: () => injectPagedMediaStyles,
51
+ inlineHtmlImages: () => inlineHtmlImages,
52
+ loadConfig: () => loadConfig,
53
+ markforge: () => compileMarkdown,
54
+ parseInlineSpans: () => parseInlineSpans,
55
+ parseMarginToTwip: () => parseMarginToTwip,
56
+ parseMarkdownDocument: () => parseMarkdownDocument,
57
+ renderInlinesToHtml: () => renderInlinesToHtml,
58
+ renderMermaidToPng: () => renderMermaidToPng,
59
+ resolveImage: () => resolveImage,
60
+ slugify: () => slugify,
61
+ tokenizeCodeLine: () => tokenizeCodeLine
62
+ });
63
+ module.exports = __toCommonJS(src_exports);
64
+
65
+ // src/core/engine.ts
66
+ var fs6 = __toESM(require("fs"));
67
+ var path6 = __toESM(require("path"));
68
+
69
+ // src/core/parser.ts
70
+ var import_gray_matter = __toESM(require("gray-matter"));
71
+ function parseInlineSpans(text) {
72
+ const spans = [];
73
+ let remaining = text;
74
+ while (remaining.length > 0) {
75
+ const imgMatch = remaining.match(/^!\[([^\]]*)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)(?:\{([^}]+)\})?/);
76
+ if (imgMatch) {
77
+ const alt = imgMatch[1];
78
+ const url = imgMatch[2];
79
+ const title = imgMatch[3];
80
+ const attrStr = imgMatch[4] || "";
81
+ let width;
82
+ let height;
83
+ if (attrStr) {
84
+ const wMatch = attrStr.match(/width=([^\s}]+)/i);
85
+ const hMatch = attrStr.match(/height=([^\s}]+)/i);
86
+ if (wMatch) width = wMatch[1];
87
+ if (hMatch) height = hMatch[1];
88
+ }
89
+ spans.push({
90
+ type: "image",
91
+ content: alt,
92
+ url,
93
+ alt,
94
+ title,
95
+ width,
96
+ height
97
+ });
98
+ remaining = remaining.slice(imgMatch[0].length);
99
+ continue;
100
+ }
101
+ const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/);
102
+ if (linkMatch) {
103
+ spans.push({
104
+ type: "link",
105
+ content: linkMatch[1],
106
+ url: linkMatch[2],
107
+ title: linkMatch[3],
108
+ children: parseInlineSpans(linkMatch[1])
109
+ });
110
+ remaining = remaining.slice(linkMatch[0].length);
111
+ continue;
112
+ }
113
+ const boldItalicMatch = remaining.match(/^(\*\*\*|___)(.+?)\1/);
114
+ if (boldItalicMatch) {
115
+ spans.push({
116
+ type: "bold",
117
+ content: boldItalicMatch[2],
118
+ children: [
119
+ {
120
+ type: "italic",
121
+ content: boldItalicMatch[2],
122
+ children: parseInlineSpans(boldItalicMatch[2])
123
+ }
124
+ ]
125
+ });
126
+ remaining = remaining.slice(boldItalicMatch[0].length);
127
+ continue;
128
+ }
129
+ const boldMatch = remaining.match(/^(\*\*|__)(.+?)\1/);
130
+ if (boldMatch) {
131
+ spans.push({
132
+ type: "bold",
133
+ content: boldMatch[2],
134
+ children: parseInlineSpans(boldMatch[2])
135
+ });
136
+ remaining = remaining.slice(boldMatch[0].length);
137
+ continue;
138
+ }
139
+ const italicMatch = remaining.match(/^(\*|_)([^*_]+?)\1/);
140
+ if (italicMatch) {
141
+ spans.push({
142
+ type: "italic",
143
+ content: italicMatch[2],
144
+ children: parseInlineSpans(italicMatch[2])
145
+ });
146
+ remaining = remaining.slice(italicMatch[0].length);
147
+ continue;
148
+ }
149
+ const strikeMatch = remaining.match(/^~~(.+?)~~/);
150
+ if (strikeMatch) {
151
+ spans.push({
152
+ type: "strikethrough",
153
+ content: strikeMatch[1],
154
+ children: parseInlineSpans(strikeMatch[1])
155
+ });
156
+ remaining = remaining.slice(strikeMatch[0].length);
157
+ continue;
158
+ }
159
+ const codeMatch = remaining.match(/^`([^`]+)`/);
160
+ if (codeMatch) {
161
+ spans.push({
162
+ type: "code",
163
+ content: codeMatch[1]
164
+ });
165
+ remaining = remaining.slice(codeMatch[0].length);
166
+ continue;
167
+ }
168
+ const htmlTagMatch = remaining.match(/^<(\w+)([^>]*)>(.*?)<\/\1>/i) || remaining.match(/^<(\w+)([^>]*)\/?>/i);
169
+ if (htmlTagMatch) {
170
+ const fullTag = htmlTagMatch[0];
171
+ const tag = htmlTagMatch[1].toLowerCase();
172
+ const attrs = htmlTagMatch[2] || "";
173
+ const innerText = htmlTagMatch[3] || "";
174
+ if (tag === "img") {
175
+ const srcMatch = attrs.match(/src=["']([^"']+)["']/i);
176
+ const altMatch = attrs.match(/alt=["']([^"']+)["']/i);
177
+ const widthMatch = attrs.match(/width=["']?(\d+%?|\d+px)?["']?/i);
178
+ const heightMatch = attrs.match(/height=["']?(\d+%?|\d+px)?["']?/i);
179
+ if (srcMatch) {
180
+ spans.push({
181
+ type: "image",
182
+ content: altMatch ? altMatch[1] : "",
183
+ url: srcMatch[1],
184
+ alt: altMatch ? altMatch[1] : "",
185
+ width: widthMatch ? widthMatch[1] : void 0,
186
+ height: heightMatch ? heightMatch[1] : void 0
187
+ });
188
+ remaining = remaining.slice(fullTag.length);
189
+ continue;
190
+ }
191
+ }
192
+ spans.push({
193
+ type: "htmlInline",
194
+ content: innerText || fullTag,
195
+ children: innerText ? parseInlineSpans(innerText) : void 0
196
+ });
197
+ remaining = remaining.slice(fullTag.length);
198
+ continue;
199
+ }
200
+ const nextSpecial = remaining.search(/[\*\_\[\!`~<]/);
201
+ if (nextSpecial === -1) {
202
+ spans.push({
203
+ type: "text",
204
+ content: remaining
205
+ });
206
+ break;
207
+ } else if (nextSpecial === 0) {
208
+ spans.push({
209
+ type: "text",
210
+ content: remaining[0]
211
+ });
212
+ remaining = remaining.slice(1);
213
+ } else {
214
+ spans.push({
215
+ type: "text",
216
+ content: remaining.slice(0, nextSpecial)
217
+ });
218
+ remaining = remaining.slice(nextSpecial);
219
+ }
220
+ }
221
+ return spans;
222
+ }
223
+ function slugify(text) {
224
+ return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
225
+ }
226
+ function parseMarkdownDocument(rawMarkdown) {
227
+ const { data: frontmatter, content } = (0, import_gray_matter.default)(rawMarkdown);
228
+ const metadata = frontmatter || {};
229
+ const inlinedStyles = [];
230
+ const cleanContent = content.replace(/<style[^>]*>([\s\S]*?)<\/style>/gi, (_, css) => {
231
+ inlinedStyles.push(css.trim());
232
+ return "";
233
+ });
234
+ const lines = cleanContent.split(/\r?\n/);
235
+ const nodes = [];
236
+ const tocEntries = [];
237
+ let i = 0;
238
+ while (i < lines.length) {
239
+ const line = lines[i];
240
+ if (!line.trim()) {
241
+ i++;
242
+ continue;
243
+ }
244
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
245
+ if (headingMatch) {
246
+ const level = headingMatch[1].length;
247
+ const text = headingMatch[2].trim();
248
+ const id = slugify(text);
249
+ nodes.push({
250
+ type: "heading",
251
+ level,
252
+ id,
253
+ text,
254
+ inlines: parseInlineSpans(text)
255
+ });
256
+ tocEntries.push({ id, text, level });
257
+ i++;
258
+ continue;
259
+ }
260
+ if (/^(\*{3,}|-{3,}|_{3,})\s*$/.test(line)) {
261
+ nodes.push({ type: "thematicBreak" });
262
+ i++;
263
+ continue;
264
+ }
265
+ const codeBlockMatch = line.match(/^```(\w+)?/);
266
+ if (codeBlockMatch) {
267
+ const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
268
+ const codeLines = [];
269
+ i++;
270
+ while (i < lines.length && !lines[i].startsWith("```")) {
271
+ codeLines.push(lines[i]);
272
+ i++;
273
+ }
274
+ if (i < lines.length) i++;
275
+ const codeText = codeLines.join("\n");
276
+ if (language === "mermaid") {
277
+ nodes.push({
278
+ type: "mermaid",
279
+ language: "mermaid",
280
+ text: codeText
281
+ });
282
+ } else {
283
+ nodes.push({
284
+ type: "codeBlock",
285
+ language,
286
+ text: codeText
287
+ });
288
+ }
289
+ continue;
290
+ }
291
+ const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
292
+ if (calloutMatch) {
293
+ const calloutType = calloutMatch[1].toUpperCase();
294
+ const blockLines = [];
295
+ i++;
296
+ while (i < lines.length && lines[i].startsWith(">")) {
297
+ blockLines.push(lines[i].replace(/^>\s?/, ""));
298
+ i++;
299
+ }
300
+ const blockText = blockLines.join("\n");
301
+ nodes.push({
302
+ type: "callout",
303
+ calloutType,
304
+ text: blockText,
305
+ inlines: parseInlineSpans(blockText)
306
+ });
307
+ continue;
308
+ }
309
+ if (line.startsWith(">")) {
310
+ const quoteLines = [];
311
+ while (i < lines.length && lines[i].startsWith(">")) {
312
+ quoteLines.push(lines[i].replace(/^>\s?/, ""));
313
+ i++;
314
+ }
315
+ const quoteText = quoteLines.join("\n");
316
+ nodes.push({
317
+ type: "blockquote",
318
+ text: quoteText,
319
+ inlines: parseInlineSpans(quoteText)
320
+ });
321
+ continue;
322
+ }
323
+ if (line.trim().startsWith("|") && line.includes("|")) {
324
+ const tableLines = [];
325
+ while (i < lines.length && lines[i].trim().startsWith("|")) {
326
+ tableLines.push(lines[i].trim());
327
+ i++;
328
+ }
329
+ if (tableLines.length >= 2) {
330
+ const headerRow = tableLines[0].split("|").slice(1, -1).map((c) => c.trim());
331
+ const alignRow = tableLines[1].split("|").slice(1, -1).map((c) => c.trim());
332
+ const align = alignRow.map((col) => {
333
+ if (col.startsWith(":") && col.endsWith(":")) return "center";
334
+ if (col.endsWith(":")) return "right";
335
+ if (col.startsWith(":")) return "left";
336
+ return null;
337
+ });
338
+ const rows = [];
339
+ rows.push({
340
+ type: "tableRow",
341
+ isHeader: true,
342
+ children: headerRow.map((cellText) => ({
343
+ type: "tableCell",
344
+ isHeader: true,
345
+ text: cellText,
346
+ inlines: parseInlineSpans(cellText)
347
+ }))
348
+ });
349
+ for (let r = 2; r < tableLines.length; r++) {
350
+ const cells = tableLines[r].split("|").slice(1, -1).map((c) => c.trim());
351
+ rows.push({
352
+ type: "tableRow",
353
+ isHeader: false,
354
+ children: cells.map((cellText) => ({
355
+ type: "tableCell",
356
+ isHeader: false,
357
+ text: cellText,
358
+ inlines: parseInlineSpans(cellText)
359
+ }))
360
+ });
361
+ }
362
+ nodes.push({
363
+ type: "table",
364
+ align,
365
+ children: rows
366
+ });
367
+ continue;
368
+ }
369
+ }
370
+ const listMatch = line.match(/^(\s*)([-*+]|\d+\.)\s+(.+)$/);
371
+ if (listMatch) {
372
+ const listItems = [];
373
+ const isOrdered = /^\d+\./.test(listMatch[2]);
374
+ while (i < lines.length) {
375
+ const itemMatch = lines[i].match(/^(\s*)([-*+]|\d+\.)\s+(.+)$/);
376
+ if (!itemMatch) break;
377
+ let itemText = itemMatch[3].trim();
378
+ let checked = void 0;
379
+ const taskMatch = itemText.match(/^\[([ xX])\]\s+(.*)$/);
380
+ if (taskMatch) {
381
+ checked = taskMatch[1].toLowerCase() === "x";
382
+ itemText = taskMatch[2];
383
+ }
384
+ listItems.push({
385
+ type: "listItem",
386
+ checked,
387
+ text: itemText,
388
+ inlines: parseInlineSpans(itemText)
389
+ });
390
+ i++;
391
+ }
392
+ nodes.push({
393
+ type: "list",
394
+ ordered: isOrdered,
395
+ children: listItems
396
+ });
397
+ continue;
398
+ }
399
+ if (line.trim().startsWith("<") && !line.trim().startsWith("<!--")) {
400
+ const htmlLines = [];
401
+ while (i < lines.length && lines[i].trim().length > 0) {
402
+ htmlLines.push(lines[i]);
403
+ i++;
404
+ }
405
+ const rawHtml = htmlLines.join("\n");
406
+ nodes.push({
407
+ type: "htmlBlock",
408
+ rawHtml,
409
+ text: rawHtml
410
+ });
411
+ continue;
412
+ }
413
+ const paraLines = [];
414
+ 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+/)) {
415
+ paraLines.push(lines[i]);
416
+ i++;
417
+ }
418
+ const paraText = paraLines.join(" ");
419
+ nodes.push({
420
+ type: "paragraph",
421
+ text: paraText,
422
+ inlines: parseInlineSpans(paraText)
423
+ });
424
+ }
425
+ return {
426
+ metadata,
427
+ content: cleanContent,
428
+ nodes,
429
+ tocEntries,
430
+ inlinedStyles
431
+ };
432
+ }
433
+
434
+ // src/core/docx/docxBuilder.ts
435
+ var import_docx = require("docx");
436
+
437
+ // src/core/imageResolver.ts
438
+ var fs = __toESM(require("fs"));
439
+ var path = __toESM(require("path"));
440
+ var memoryImageCache = /* @__PURE__ */ new Map();
441
+ function getMimeType(filePathOrUrl) {
442
+ const clean = filePathOrUrl.split("?")[0].toLowerCase();
443
+ if (clean.endsWith(".png")) return "image/png";
444
+ if (clean.endsWith(".jpg") || clean.endsWith(".jpeg")) return "image/jpeg";
445
+ if (clean.endsWith(".gif")) return "image/gif";
446
+ if (clean.endsWith(".svg")) return "image/svg+xml";
447
+ if (clean.endsWith(".webp")) return "image/webp";
448
+ if (clean.endsWith(".bmp")) return "image/bmp";
449
+ return "image/png";
450
+ }
451
+ async function resolveImage(src, baseDir = process.cwd()) {
452
+ const cacheKey = `${baseDir}::${src}`;
453
+ if (memoryImageCache.has(cacheKey)) {
454
+ return memoryImageCache.get(cacheKey);
455
+ }
456
+ try {
457
+ if (src.startsWith("data:")) {
458
+ const parts = src.split(",");
459
+ const meta = parts[0];
460
+ const base64Data = parts[1];
461
+ const mimeMatch = meta.match(/data:([^;]+)/);
462
+ const mimeType2 = mimeMatch ? mimeMatch[1] : "image/png";
463
+ const buffer2 = Buffer.from(base64Data, "base64");
464
+ const isSvg2 = mimeType2 === "image/svg+xml";
465
+ const resolved2 = {
466
+ src,
467
+ buffer: buffer2,
468
+ mimeType: mimeType2,
469
+ dataUri: src,
470
+ isSvg: isSvg2
471
+ };
472
+ memoryImageCache.set(cacheKey, resolved2);
473
+ return resolved2;
474
+ }
475
+ if (src.startsWith("http://") || src.startsWith("https://")) {
476
+ const response = await fetch(src, { signal: AbortSignal.timeout(1e4) });
477
+ if (!response.ok) {
478
+ return null;
479
+ }
480
+ const arrayBuffer = await response.arrayBuffer();
481
+ const buffer2 = Buffer.from(arrayBuffer);
482
+ const contentType = response.headers.get("content-type") || getMimeType(src);
483
+ const mimeType2 = contentType.split(";")[0].trim();
484
+ const dataUri2 = `data:${mimeType2};base64,${buffer2.toString("base64")}`;
485
+ const isSvg2 = mimeType2 === "image/svg+xml";
486
+ const resolved2 = {
487
+ src,
488
+ buffer: buffer2,
489
+ mimeType: mimeType2,
490
+ dataUri: dataUri2,
491
+ isSvg: isSvg2
492
+ };
493
+ memoryImageCache.set(cacheKey, resolved2);
494
+ return resolved2;
495
+ }
496
+ const localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
497
+ if (!fs.existsSync(localPath)) {
498
+ return null;
499
+ }
500
+ const buffer = fs.readFileSync(localPath);
501
+ const mimeType = getMimeType(localPath);
502
+ const dataUri = `data:${mimeType};base64,${buffer.toString("base64")}`;
503
+ const isSvg = mimeType === "image/svg+xml";
504
+ const resolved = {
505
+ src,
506
+ buffer,
507
+ mimeType,
508
+ dataUri,
509
+ isSvg
510
+ };
511
+ memoryImageCache.set(cacheKey, resolved);
512
+ return resolved;
513
+ } catch {
514
+ return null;
515
+ }
516
+ }
517
+ async function inlineHtmlImages(html, baseDir = process.cwd()) {
518
+ const imgRegex = /<img([^>]+)src=["']([^"']+)["']([^>]*)>/gi;
519
+ const matches = Array.from(html.matchAll(imgRegex));
520
+ let inlinedHtml = html;
521
+ for (const match of matches) {
522
+ const fullTag = match[0];
523
+ const beforeSrc = match[1];
524
+ const src = match[2];
525
+ const afterSrc = match[3];
526
+ const resolved = await resolveImage(src, baseDir);
527
+ if (resolved) {
528
+ const replacement = `<img${beforeSrc}src="${resolved.dataUri}"${afterSrc}>`;
529
+ inlinedHtml = inlinedHtml.replace(fullTag, replacement);
530
+ }
531
+ }
532
+ return inlinedHtml;
533
+ }
534
+
535
+ // src/core/syntax/syntaxHighlighter.ts
536
+ var SYNTAX_COLORS = {
537
+ keyword: "FF7B72",
538
+ // Bright coral-red — keywords (import, def, return...)
539
+ string: "E07C4F",
540
+ // Warm orange — strings (visible on dark bg)
541
+ comment: "8B949E",
542
+ // Muted gray — comments (italic)
543
+ number: "79C0FF",
544
+ // Sky blue — numbers
545
+ boolean: "FF9580",
546
+ // Salmon — true / false / None
547
+ function: "D2A8FF",
548
+ // Soft purple — function names
549
+ type: "7EE787",
550
+ // Bright green — Types / Classes
551
+ operator: "FF7B72",
552
+ // Same as keyword — = + - * / > <
553
+ punctuation: "8B9AC0",
554
+ // Steel blue-gray — () [] {} , . ;
555
+ plain: "E2E8F0"
556
+ // Light gray — identifiers / plain text
557
+ };
558
+ var SYNTAX_COLORS_LIGHT = {
559
+ keyword: "D73A49",
560
+ // Crimson red — keywords
561
+ string: "0A7E5C",
562
+ // Forest teal — strings (readable on white)
563
+ comment: "6A737D",
564
+ // Muted gray — comments
565
+ number: "005CC5",
566
+ // Cobalt blue — numbers
567
+ boolean: "D73A49",
568
+ // Crimson — true/false/None
569
+ function: "6F42C1",
570
+ // Purple — function names
571
+ type: "22863A",
572
+ // Forest green — Types / Classes
573
+ operator: "D73A49",
574
+ // Crimson — operators
575
+ punctuation: "586069",
576
+ // Dark gray — punctuation
577
+ plain: "24292E"
578
+ // Near-black — identifiers
579
+ };
580
+ var JS_KEYWORDS = /* @__PURE__ */ new Set([
581
+ "import",
582
+ "from",
583
+ "export",
584
+ "default",
585
+ "const",
586
+ "let",
587
+ "var",
588
+ "function",
589
+ "async",
590
+ "await",
591
+ "return",
592
+ "if",
593
+ "else",
594
+ "for",
595
+ "while",
596
+ "switch",
597
+ "case",
598
+ "break",
599
+ "continue",
600
+ "new",
601
+ "this",
602
+ "typeof",
603
+ "instanceof",
604
+ "class",
605
+ "extends",
606
+ "implements",
607
+ "interface",
608
+ "type",
609
+ "enum",
610
+ "as",
611
+ "try",
612
+ "catch",
613
+ "finally",
614
+ "throw",
615
+ "void",
616
+ "yield",
617
+ "static",
618
+ "readonly",
619
+ "private",
620
+ "public",
621
+ "protected"
622
+ ]);
623
+ var PYTHON_KEYWORDS = /* @__PURE__ */ new Set([
624
+ "def",
625
+ "class",
626
+ "import",
627
+ "from",
628
+ "as",
629
+ "return",
630
+ "if",
631
+ "elif",
632
+ "else",
633
+ "for",
634
+ "while",
635
+ "in",
636
+ "is",
637
+ "not",
638
+ "and",
639
+ "or",
640
+ "try",
641
+ "except",
642
+ "finally",
643
+ "raise",
644
+ "with",
645
+ "yield",
646
+ "lambda",
647
+ "global",
648
+ "nonlocal",
649
+ "pass",
650
+ "break",
651
+ "continue"
652
+ ]);
653
+ var BASH_KEYWORDS = /* @__PURE__ */ new Set([
654
+ "echo",
655
+ "cd",
656
+ "ls",
657
+ "mkdir",
658
+ "rm",
659
+ "cp",
660
+ "mv",
661
+ "cat",
662
+ "grep",
663
+ "find",
664
+ "curl",
665
+ "wget",
666
+ "npm",
667
+ "bun",
668
+ "pnpm",
669
+ "yarn",
670
+ "git",
671
+ "export",
672
+ "source",
673
+ "if",
674
+ "then",
675
+ "else",
676
+ "elif",
677
+ "fi",
678
+ "for",
679
+ "do",
680
+ "done",
681
+ "case",
682
+ "esac",
683
+ "sudo",
684
+ "chmod",
685
+ "chown",
686
+ "exit"
687
+ ]);
688
+ var SQL_KEYWORDS = /* @__PURE__ */ new Set([
689
+ "select",
690
+ "from",
691
+ "where",
692
+ "insert",
693
+ "into",
694
+ "values",
695
+ "update",
696
+ "set",
697
+ "delete",
698
+ "create",
699
+ "table",
700
+ "drop",
701
+ "alter",
702
+ "join",
703
+ "inner",
704
+ "left",
705
+ "right",
706
+ "on",
707
+ "group",
708
+ "by",
709
+ "order",
710
+ "asc",
711
+ "desc",
712
+ "having",
713
+ "limit",
714
+ "and",
715
+ "or",
716
+ "not",
717
+ "null",
718
+ "primary",
719
+ "key",
720
+ "foreign",
721
+ "references"
722
+ ]);
723
+ function tokenizeCodeLine(line, lang = "", theme = "dark") {
724
+ var _a;
725
+ const COLORS = theme === "light" ? SYNTAX_COLORS_LIGHT : SYNTAX_COLORS;
726
+ if (!line) {
727
+ return [{ text: " ", type: "plain", colorHex: COLORS.plain }];
728
+ }
729
+ const normalizedLang = (lang || "").toLowerCase().trim();
730
+ const tokens = [];
731
+ let pos = 0;
732
+ if (line.trimStart().startsWith("//") || line.trimStart().startsWith("#") || line.trimStart().startsWith("--")) {
733
+ const leadWs = ((_a = line.match(/^\s*/)) == null ? void 0 : _a[0]) || "";
734
+ if (leadWs) {
735
+ tokens.push({ text: leadWs, type: "plain", colorHex: COLORS.plain });
736
+ }
737
+ tokens.push({
738
+ text: line.slice(leadWs.length),
739
+ type: "comment",
740
+ colorHex: COLORS.comment,
741
+ italic: true
742
+ });
743
+ return tokens;
744
+ }
745
+ while (pos < line.length) {
746
+ if (/\s/.test(line[pos])) {
747
+ let ws = "";
748
+ while (pos < line.length && /\s/.test(line[pos])) {
749
+ ws += line[pos++];
750
+ }
751
+ tokens.push({ text: ws, type: "plain", colorHex: COLORS.plain });
752
+ continue;
753
+ }
754
+ if (line.slice(pos, pos + 2) === "//") {
755
+ tokens.push({
756
+ text: line.slice(pos),
757
+ type: "comment",
758
+ colorHex: COLORS.comment,
759
+ italic: true
760
+ });
761
+ break;
762
+ }
763
+ if (line[pos] === '"' || line[pos] === "'" || line[pos] === "`") {
764
+ const quote = line[pos];
765
+ let str = quote;
766
+ pos++;
767
+ while (pos < line.length) {
768
+ if (line[pos] === "\\" && pos + 1 < line.length) {
769
+ str += line[pos] + line[pos + 1];
770
+ pos += 2;
771
+ continue;
772
+ }
773
+ str += line[pos];
774
+ if (line[pos] === quote) {
775
+ pos++;
776
+ break;
777
+ }
778
+ pos++;
779
+ }
780
+ tokens.push({ text: str, type: "string", colorHex: COLORS.string });
781
+ continue;
782
+ }
783
+ if (/\d/.test(line[pos])) {
784
+ let num = "";
785
+ while (pos < line.length && /[\d.a-fA-FxX]/.test(line[pos])) {
786
+ num += line[pos++];
787
+ }
788
+ tokens.push({ text: num, type: "number", colorHex: COLORS.number });
789
+ continue;
790
+ }
791
+ if (/[a-zA-Z_$]/.test(line[pos])) {
792
+ let word = "";
793
+ while (pos < line.length && /[a-zA-Z0-9_$]/.test(line[pos])) {
794
+ word += line[pos++];
795
+ }
796
+ if (word === "true" || word === "false" || word === "null" || word === "undefined" || word === "True" || word === "False" || word === "None") {
797
+ tokens.push({ text: word, type: "boolean", colorHex: COLORS.boolean, bold: true });
798
+ continue;
799
+ }
800
+ const isJsKeyword = (!normalizedLang || ["ts", "typescript", "js", "javascript", "tsx", "jsx"].includes(normalizedLang)) && JS_KEYWORDS.has(word);
801
+ const isPyKeyword = ["py", "python"].includes(normalizedLang) && PYTHON_KEYWORDS.has(word);
802
+ const isBashKeyword = ["sh", "bash", "shell", "zsh"].includes(normalizedLang) && BASH_KEYWORDS.has(word);
803
+ const isSqlKeyword = ["sql"].includes(normalizedLang) && SQL_KEYWORDS.has(word.toLowerCase());
804
+ if (isJsKeyword || isPyKeyword || isBashKeyword || isSqlKeyword) {
805
+ tokens.push({ text: word, type: "keyword", colorHex: COLORS.keyword, bold: true });
806
+ continue;
807
+ }
808
+ let nextNonWs = pos;
809
+ while (nextNonWs < line.length && /\s/.test(line[nextNonWs])) {
810
+ nextNonWs++;
811
+ }
812
+ if (line[nextNonWs] === "(") {
813
+ tokens.push({ text: word, type: "function", colorHex: COLORS.function });
814
+ continue;
815
+ }
816
+ if (/^[A-Z][a-zA-Z0-9_$]*$/.test(word)) {
817
+ tokens.push({ text: word, type: "type", colorHex: COLORS.type });
818
+ continue;
819
+ }
820
+ tokens.push({ text: word, type: "plain", colorHex: COLORS.plain });
821
+ continue;
822
+ }
823
+ const char = line[pos++];
824
+ if (["=", "+", "-", "*", "/", "!", ">", "<", "&", "|", "?", ":"].includes(char)) {
825
+ tokens.push({ text: char, type: "operator", colorHex: COLORS.operator });
826
+ } else {
827
+ tokens.push({ text: char, type: "punctuation", colorHex: COLORS.punctuation });
828
+ }
829
+ }
830
+ return tokens;
831
+ }
832
+ function highlightCodeToHtml(code, lang = "") {
833
+ const lines = (code || "").split("\n");
834
+ const htmlLines = lines.map((line) => {
835
+ const tokens = tokenizeCodeLine(line, lang);
836
+ return tokens.map((t) => {
837
+ const escaped = t.text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
838
+ if (t.type === "plain") return escaped;
839
+ return `<span style="color: #${t.colorHex};${t.bold ? " font-weight: bold;" : ""}${t.italic ? " font-style: italic;" : ""}">${escaped}</span>`;
840
+ }).join("");
841
+ });
842
+ return htmlLines.join("\n");
843
+ }
844
+
845
+ // src/core/mermaid/mermaidRenderer.ts
846
+ var import_node_child_process2 = require("child_process");
847
+ var fs4 = __toESM(require("fs"));
848
+ var os2 = __toESM(require("os"));
849
+ var path4 = __toESM(require("path"));
850
+
851
+ // src/core/pdf/pdfBuilder.ts
852
+ var fs3 = __toESM(require("fs"));
853
+ var path3 = __toESM(require("path"));
854
+ var os = __toESM(require("os"));
855
+ var import_node_child_process = require("child_process");
856
+
857
+ // src/core/html/htmlBuilder.ts
858
+ var fs2 = __toESM(require("fs"));
859
+ var path2 = __toESM(require("path"));
860
+
861
+ // src/core/html/htmlThemes.ts
862
+ var THEME_COMPONENTS = `
863
+ /* Fallback CSS variables \u2014 overridden by each named theme's :root block */
864
+ :root {
865
+ --mf-bg: #ffffff;
866
+ --mf-text: #0f172a;
867
+ --mf-text-muted: #64748b;
868
+ --mf-primary: #33CDCF;
869
+ --mf-primary-dark: #009DA0;
870
+ --mf-primary-light: #ECFDFD;
871
+ --mf-border: #e2e8f0;
872
+ --mf-card-bg: #f8fafc;
873
+ --mf-code-bg: #0f172a;
874
+ --mf-code-text: #f8fafc;
875
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
876
+ --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
877
+ }
878
+
879
+ /* Document header */
880
+ .document-header { margin-bottom: 2.5rem; padding-bottom: 1.5rem; border-bottom: 2px solid var(--mf-border); }
881
+ .document-title { font-size: 2.5rem; font-weight: 800; margin: 0 0 0.5rem 0; }
882
+ .document-subtitle { font-size: 1.25rem; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
883
+ .document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
884
+
885
+ /* Table of Contents */
886
+ .table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; }
887
+ .table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
888
+ .table-of-contents ul { list-style: none; padding: 0; margin: 0; }
889
+ .table-of-contents li { padding: 0.25rem 0; }
890
+ .table-of-contents a { color: var(--mf-primary-dark); text-decoration: none; }
891
+ .table-of-contents a:hover { text-decoration: underline; }
892
+
893
+ /* Links */
894
+ a { color: var(--mf-primary-dark); font-weight: 600; text-decoration: none; }
895
+ a:hover { text-decoration: underline; }
896
+
897
+ /* Blockquote */
898
+ 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; }
899
+
900
+ /* Callout / Alert Boxes */
901
+ .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; }
902
+ .callout-title { font-weight: 700; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 0.4rem; }
903
+ .callout-NOTE { border-color: #33CDCF; background: #ECFDFD; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
904
+ .callout-NOTE .callout-title { color: #009DA0; }
905
+ .callout-TIP { border-color: #10b981; background: #ecfdf5; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
906
+ .callout-TIP .callout-title { color: #10b981; }
907
+ .callout-WARNING { border-color: #f59e0b; background: #fffbeb; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
908
+ .callout-WARNING .callout-title { color: #f59e0b; }
909
+ .callout-CAUTION { border-color: #ef4444; background: #fef2f2; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
910
+ .callout-CAUTION .callout-title { color: #ef4444; }
911
+ .callout-IMPORTANT { border-color: #8b5cf6; background: #f5f3ff; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
912
+ .callout-IMPORTANT .callout-title { color: #8b5cf6; }
913
+
914
+ /* Inline Code */
915
+ code { font-family: var(--mf-font-mono); font-size: 0.88em; background-color: #f1f5f9; padding: 0.2em 0.4em; border-radius: 4px; color: #0f172a; }
916
+
917
+ /* Code Blocks */
918
+ 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; }
919
+ pre code { background-color: transparent; color: inherit; padding: 0; }
920
+
921
+ /* Tables */
922
+ table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; font-size: 0.95rem; }
923
+ th, td { border: 1px solid var(--mf-border); padding: 0.75rem 1rem; text-align: left; }
924
+ th { background-color: var(--mf-card-bg); font-weight: 600; color: #0f172a; }
925
+ tr:nth-child(even) { background-color: var(--mf-card-bg); }
926
+
927
+ /* Images */
928
+ img { max-width: 100%; height: auto; border-radius: 6px; margin: 1rem 0; }
929
+
930
+ /* Dividers */
931
+ hr { border: none; border-top: 1px solid var(--mf-border); margin: 2rem 0; }
932
+
933
+ /* Print / PDF */
934
+ @media print {
935
+ body { padding: 0; }
936
+ .document-container { max-width: 100%; }
937
+ pre, table, blockquote, .callout { break-inside: avoid; }
938
+ /* Remove scrollbars \u2014 PDF has no scrolling */
939
+ pre { overflow: visible; white-space: pre-wrap; word-break: break-all; }
940
+ }
941
+ `;
942
+ var THEME_DEFAULT = `
943
+ :root {
944
+ --mf-bg: #ffffff;
945
+ --mf-text: #0f172a;
946
+ --mf-text-muted: #64748b;
947
+ --mf-primary: #33CDCF;
948
+ --mf-primary-dark: #009DA0;
949
+ --mf-primary-light: #ECFDFD;
950
+ --mf-border: #e2e8f0;
951
+ --mf-card-bg: #f8fafc;
952
+ --mf-code-bg: #0f172a;
953
+ --mf-code-text: #f8fafc;
954
+ --mf-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
955
+ --mf-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
956
+ }
957
+ 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; }
958
+ .document-container { max-width: 860px; margin: 0 auto; }
959
+ 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; }
960
+ h1 { font-size: 2.2rem; border-bottom: 2px solid #33CDCF; padding-bottom: 0.5rem; }
961
+ h2 { font-size: 1.6rem; color: #009DA0; border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
962
+ h3 { font-size: 1.3rem; }
963
+ h4 { font-size: 1.1rem; }
964
+ p { margin: 0.8rem 0; }
965
+ `;
966
+ var THEME_ACADEMIC = `
967
+ :root {
968
+ --mf-bg: #ffffff;
969
+ --mf-text: #1a1a1a;
970
+ --mf-text-muted: #555;
971
+ --mf-primary: #33CDCF;
972
+ --mf-primary-dark: #009DA0;
973
+ --mf-primary-light: #ECFDFD;
974
+ --mf-border: #ccc;
975
+ --mf-card-bg: #f9f9f9;
976
+ --mf-code-bg: #1e1e1e;
977
+ --mf-code-text: #d4d4d4;
978
+ --mf-font-family: "Merriweather", "Georgia", "Times New Roman", serif;
979
+ --mf-font-mono: "Courier New", Courier, monospace;
980
+ }
981
+ body { font-family: var(--mf-font-family); font-size: 16px; line-height: 1.8; padding: 3rem; color: var(--mf-text); }
982
+ .document-container { max-width: 780px; margin: 0 auto; text-align: justify; }
983
+ h1, h2, h3 { font-family: "Times New Roman", Times, serif; font-weight: bold; text-align: left; }
984
+ h1 { font-size: 2rem; border-bottom: 1px solid #000; padding-bottom: 0.3rem; }
985
+ h2 { font-size: 1.4rem; border-bottom: 1px solid #ccc; padding-bottom: 0.2rem; }
986
+ h3 { font-size: 1.2rem; }
987
+ p { margin: 0.9rem 0; }
988
+ `;
989
+ var THEMES = {
990
+ default: THEME_DEFAULT,
991
+ academic: THEME_ACADEMIC,
992
+ github: THEME_DEFAULT,
993
+ corporate: THEME_DEFAULT,
994
+ minimal: THEME_DEFAULT,
995
+ dracula: THEME_DEFAULT
996
+ };
997
+
998
+ // src/core/html/htmlBuilder.ts
999
+ function escapeHtml(str) {
1000
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
1001
+ }
1002
+ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
1003
+ let result = "";
1004
+ for (const span of spans) {
1005
+ if (span.type === "image" && span.url) {
1006
+ const resolved = await resolveImage(span.url, baseDir);
1007
+ const src = resolved ? resolved.dataUri : span.url;
1008
+ const alt = escapeHtml(span.alt || "");
1009
+ const title = span.title ? ` title="${escapeHtml(span.title)}"` : "";
1010
+ const width = span.width ? ` width="${span.width}"` : "";
1011
+ const height = span.height ? ` height="${span.height}"` : "";
1012
+ result += `<img src="${src}" alt="${alt}"${title}${width}${height} />`;
1013
+ continue;
1014
+ }
1015
+ if (span.type === "link" && span.url) {
1016
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1017
+ const title = span.title ? ` title="${escapeHtml(span.title)}"` : "";
1018
+ result += `<a href="${escapeHtml(span.url)}"${title}>${inner}</a>`;
1019
+ continue;
1020
+ }
1021
+ if (span.type === "bold") {
1022
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1023
+ result += `<strong>${inner}</strong>`;
1024
+ continue;
1025
+ }
1026
+ if (span.type === "italic") {
1027
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1028
+ result += `<em>${inner}</em>`;
1029
+ continue;
1030
+ }
1031
+ if (span.type === "strikethrough") {
1032
+ const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
1033
+ result += `<del>${inner}</del>`;
1034
+ continue;
1035
+ }
1036
+ if (span.type === "code") {
1037
+ result += `<code>${escapeHtml(span.content)}</code>`;
1038
+ continue;
1039
+ }
1040
+ if (span.type === "htmlInline") {
1041
+ result += span.content;
1042
+ continue;
1043
+ }
1044
+ result += escapeHtml(span.content);
1045
+ }
1046
+ return result;
1047
+ }
1048
+ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
1049
+ var _a;
1050
+ const metadata = { ...config.metadata, ...doc.metadata };
1051
+ const themeName = metadata.theme || config.theme || "default";
1052
+ const baseThemeCss = THEMES[themeName] || THEMES.default;
1053
+ let customCss = "";
1054
+ if (config.css) {
1055
+ const cssList = Array.isArray(config.css) ? config.css : [config.css];
1056
+ for (const cssPath of cssList) {
1057
+ const fullCssPath = path2.isAbsolute(cssPath) ? cssPath : path2.resolve(baseDir, cssPath);
1058
+ if (fs2.existsSync(fullCssPath)) {
1059
+ customCss += `
1060
+ /* Custom CSS: ${cssPath} */
1061
+ ` + fs2.readFileSync(fullCssPath, "utf-8");
1062
+ }
1063
+ }
1064
+ }
1065
+ const inlinedCss = doc.inlinedStyles.join("\n");
1066
+ let bodyHtml = "";
1067
+ if (metadata.title) {
1068
+ bodyHtml += ` <header class="document-header">
1069
+ `;
1070
+ bodyHtml += ` <h1 class="document-title">${escapeHtml(metadata.title)}</h1>
1071
+ `;
1072
+ if (metadata.subtitle) {
1073
+ bodyHtml += ` <div class="document-subtitle">${escapeHtml(metadata.subtitle)}</div>
1074
+ `;
1075
+ }
1076
+ if (metadata.author || metadata.date) {
1077
+ bodyHtml += ` <div class="document-meta">
1078
+ `;
1079
+ if (metadata.author) {
1080
+ const authors = Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author;
1081
+ bodyHtml += ` <span>Author: ${escapeHtml(authors)}</span>
1082
+ `;
1083
+ }
1084
+ if (metadata.date) {
1085
+ bodyHtml += ` <span>Date: ${escapeHtml(metadata.date)}</span>
1086
+ `;
1087
+ }
1088
+ bodyHtml += ` </div>
1089
+ `;
1090
+ }
1091
+ bodyHtml += ` </header>
1092
+ `;
1093
+ }
1094
+ if (config.toc || metadata.toc) {
1095
+ if (doc.tocEntries.length > 0) {
1096
+ bodyHtml += ` <nav class="table-of-contents">
1097
+ `;
1098
+ bodyHtml += ` <h2>Table of Contents</h2>
1099
+ <ul>
1100
+ `;
1101
+ for (const entry of doc.tocEntries) {
1102
+ const indent = " ".repeat(entry.level);
1103
+ bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
1104
+ `;
1105
+ }
1106
+ bodyHtml += ` </ul>
1107
+ </nav>
1108
+ `;
1109
+ }
1110
+ }
1111
+ for (const node of doc.nodes) {
1112
+ if (node.type === "heading") {
1113
+ const inner = await renderInlinesToHtml(node.inlines, baseDir);
1114
+ bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
1115
+ `;
1116
+ continue;
1117
+ }
1118
+ if (node.type === "paragraph") {
1119
+ const inner = await renderInlinesToHtml(node.inlines, baseDir);
1120
+ bodyHtml += ` <p>${inner}</p>
1121
+ `;
1122
+ continue;
1123
+ }
1124
+ if (node.type === "codeBlock") {
1125
+ const lang = node.language || "";
1126
+ const langClass = lang ? ` class="language-${escapeHtml(lang)}"` : "";
1127
+ const highlighted = highlightCodeToHtml(node.text || "", lang);
1128
+ bodyHtml += ` <pre><code${langClass}>${highlighted}</code></pre>
1129
+ `;
1130
+ continue;
1131
+ }
1132
+ if (node.type === "mermaid") {
1133
+ bodyHtml += ` <div class="mermaid">
1134
+ ${escapeHtml(node.text || "")}
1135
+ </div>
1136
+ `;
1137
+ continue;
1138
+ }
1139
+ if (node.type === "callout") {
1140
+ const inner = await renderInlinesToHtml(node.inlines, baseDir);
1141
+ const CALLOUT_STYLES = {
1142
+ NOTE: { bg: "#ECFDFD", border: "#33CDCF", titleColor: "#009DA0" },
1143
+ TIP: { bg: "#ecfdf5", border: "#10b981", titleColor: "#10b981" },
1144
+ WARNING: { bg: "#fffbeb", border: "#f59e0b", titleColor: "#f59e0b" },
1145
+ CAUTION: { bg: "#fef2f2", border: "#ef4444", titleColor: "#ef4444" },
1146
+ IMPORTANT: { bg: "#f5f3ff", border: "#8b5cf6", titleColor: "#8b5cf6" }
1147
+ };
1148
+ const cs = CALLOUT_STYLES[node.calloutType ?? "NOTE"] ?? CALLOUT_STYLES["NOTE"];
1149
+ bodyHtml += ` <div class="callout callout-${node.calloutType}" style="border-left:4px solid ${cs.border};background:${cs.bg};border-radius:6px;padding:1rem 1.2rem;margin:1.2rem 0;">
1150
+ `;
1151
+ bodyHtml += ` <div class="callout-title" style="font-weight:700;font-size:0.85rem;text-transform:uppercase;letter-spacing:0.05em;margin-bottom:0.4rem;color:${cs.titleColor};">${node.calloutType}</div>
1152
+ `;
1153
+ bodyHtml += ` <div class="callout-body">${inner}</div>
1154
+ `;
1155
+ bodyHtml += ` </div>
1156
+ `;
1157
+ continue;
1158
+ }
1159
+ if (node.type === "blockquote") {
1160
+ const inner = await renderInlinesToHtml(node.inlines, baseDir);
1161
+ bodyHtml += ` <blockquote>${inner}</blockquote>
1162
+ `;
1163
+ continue;
1164
+ }
1165
+ if (node.type === "table" && node.children) {
1166
+ bodyHtml += ` <table>
1167
+ `;
1168
+ for (const row of node.children) {
1169
+ bodyHtml += ` <tr>
1170
+ `;
1171
+ if (row.children) {
1172
+ for (let colIdx = 0; colIdx < row.children.length; colIdx++) {
1173
+ const cell = row.children[colIdx];
1174
+ const tag = row.isHeader ? "th" : "td";
1175
+ const align = ((_a = node.align) == null ? void 0 : _a[colIdx]) ? ` style="text-align: ${node.align[colIdx]}"` : "";
1176
+ const cellInner = await renderInlinesToHtml(cell.inlines, baseDir);
1177
+ bodyHtml += ` <${tag}${align}>${cellInner}</${tag}>
1178
+ `;
1179
+ }
1180
+ }
1181
+ bodyHtml += ` </tr>
1182
+ `;
1183
+ }
1184
+ bodyHtml += ` </table>
1185
+ `;
1186
+ continue;
1187
+ }
1188
+ if (node.type === "list" && node.children) {
1189
+ const tag = node.ordered ? "ol" : "ul";
1190
+ bodyHtml += ` <${tag}>
1191
+ `;
1192
+ for (const item of node.children) {
1193
+ const itemInner = await renderInlinesToHtml(item.inlines, baseDir);
1194
+ let prefix = "";
1195
+ if (item.checked !== void 0) {
1196
+ prefix = `<input type="checkbox" disabled ${item.checked ? "checked" : ""}/> `;
1197
+ }
1198
+ bodyHtml += ` <li>${prefix}${itemInner}</li>
1199
+ `;
1200
+ }
1201
+ bodyHtml += ` </${tag}>
1202
+ `;
1203
+ continue;
1204
+ }
1205
+ if (node.type === "htmlBlock") {
1206
+ bodyHtml += ` ${node.rawHtml}
1207
+ `;
1208
+ continue;
1209
+ }
1210
+ if (node.type === "thematicBreak") {
1211
+ bodyHtml += ` <hr />
1212
+ `;
1213
+ continue;
1214
+ }
1215
+ }
1216
+ const wmConfig = config.watermark ?? metadata.watermark;
1217
+ let watermarkHtml = "";
1218
+ let watermarkCss = "";
1219
+ if (wmConfig) {
1220
+ const wmText = typeof wmConfig === "string" ? wmConfig : wmConfig.text;
1221
+ const opacity = typeof wmConfig === "object" && wmConfig.opacity !== void 0 ? wmConfig.opacity : 0.12;
1222
+ const rotate = typeof wmConfig === "object" && wmConfig.rotate !== void 0 ? wmConfig.rotate : -45;
1223
+ const color = typeof wmConfig === "object" && wmConfig.color ? wmConfig.color : "#94A3B8";
1224
+ const fontSize = typeof wmConfig === "object" && wmConfig.fontSize ? `${wmConfig.fontSize}pt` : "52pt";
1225
+ watermarkCss = `
1226
+ .document-watermark {
1227
+ position: fixed;
1228
+ top: 50%;
1229
+ left: 50%;
1230
+ transform: translate(-50%, -50%) rotate(${rotate}deg);
1231
+ font-size: ${fontSize};
1232
+ font-weight: 800;
1233
+ color: ${color};
1234
+ opacity: ${opacity};
1235
+ pointer-events: none;
1236
+ user-select: none;
1237
+ z-index: 9999;
1238
+ text-transform: uppercase;
1239
+ letter-spacing: 0.15em;
1240
+ white-space: nowrap;
1241
+ }
1242
+ `;
1243
+ watermarkHtml = ` <div class="document-watermark">${escapeHtml(wmText)}</div>
1244
+ `;
1245
+ }
1246
+ const hasMermaid = doc.nodes.some((n) => n.type === "mermaid");
1247
+ const mermaidScript = hasMermaid ? `<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
1248
+ <script>
1249
+ mermaid.initialize({
1250
+ startOnLoad: true,
1251
+ theme: 'neutral',
1252
+ themeVariables: {
1253
+ primaryColor: '#33CDCF',
1254
+ primaryTextColor: '#0F172A',
1255
+ primaryBorderColor: '#009DA0',
1256
+ lineColor: '#009DA0',
1257
+ secondaryColor: '#ECFDFD',
1258
+ tertiaryColor: '#F8FAFC'
1259
+ }
1260
+ });
1261
+ </script>` : "";
1262
+ const documentTitle = metadata.title || "MarkForge Document";
1263
+ return `<!DOCTYPE html>
1264
+ <html lang="${metadata.lang || "en"}">
1265
+ <head>
1266
+ <meta charset="UTF-8">
1267
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1268
+ <title>${escapeHtml(documentTitle)}</title>
1269
+ <style>
1270
+ ${THEME_COMPONENTS}
1271
+ ${baseThemeCss}
1272
+ ${customCss}
1273
+ ${inlinedCss}
1274
+ ${watermarkCss}
1275
+ </style>
1276
+ </head>
1277
+ <body>
1278
+ ${watermarkHtml} <div class="document-container">
1279
+ ${bodyHtml} </div>
1280
+ ${mermaidScript}
1281
+ </body>
1282
+ </html>`;
1283
+ }
1284
+
1285
+ // src/core/pdf/pdfBuilder.ts
1286
+ function findChromeExecutable() {
1287
+ if (process.env.CHROME_PATH && fs3.existsSync(process.env.CHROME_PATH)) {
1288
+ return process.env.CHROME_PATH;
1289
+ }
1290
+ if (process.env.PUPPETEER_EXECUTABLE_PATH && fs3.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
1291
+ return process.env.PUPPETEER_EXECUTABLE_PATH;
1292
+ }
1293
+ const candidates = [
1294
+ // Linux
1295
+ "/usr/bin/google-chrome",
1296
+ "/usr/bin/google-chrome-stable",
1297
+ "/usr/bin/chromium",
1298
+ "/usr/bin/chromium-browser",
1299
+ "/snap/bin/chromium",
1300
+ // macOS
1301
+ "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
1302
+ "/Applications/Chromium.app/Contents/MacOS/Chromium",
1303
+ // Windows
1304
+ "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
1305
+ "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
1306
+ ];
1307
+ for (const candidate of candidates) {
1308
+ try {
1309
+ if (fs3.existsSync(candidate)) {
1310
+ return candidate;
1311
+ }
1312
+ } catch {
1313
+ }
1314
+ }
1315
+ try {
1316
+ const isWin = process.platform === "win32";
1317
+ const cmd = isWin ? "where" : "which";
1318
+ for (const name of ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser"]) {
1319
+ const res = (0, import_node_child_process.spawnSync)(cmd, [name], { encoding: "utf-8" });
1320
+ if (res.status === 0 && res.stdout.trim()) {
1321
+ const binPath = res.stdout.split(/\r?\n/)[0].trim();
1322
+ if (fs3.existsSync(binPath)) return binPath;
1323
+ }
1324
+ }
1325
+ } catch {
1326
+ }
1327
+ return null;
1328
+ }
1329
+ function injectPagedMediaStyles(html, config, metadata) {
1330
+ var _a, _b, _c, _d;
1331
+ const merged = { ...config.metadata, ...metadata };
1332
+ const orientation = merged.orientation || config.orientation || "portrait";
1333
+ const size = merged.paperSize || config.paperSize || "A4";
1334
+ const margins = merged.margins || config.margins || {};
1335
+ const top = margins.top || ((_a = config.margins) == null ? void 0 : _a.top) || "2.5cm";
1336
+ const bottom = margins.bottom || ((_b = config.margins) == null ? void 0 : _b.bottom) || "2.5cm";
1337
+ const left = margins.left || ((_c = config.margins) == null ? void 0 : _c.left) || "2.5cm";
1338
+ const right = margins.right || ((_d = config.margins) == null ? void 0 : _d.right) || "2.5cm";
1339
+ const headerCfg = merged.header;
1340
+ const footerCfg = merged.footer;
1341
+ const headerLeft = (headerCfg == null ? void 0 : headerCfg.left) ?? "";
1342
+ const headerCenter = (headerCfg == null ? void 0 : headerCfg.center) ?? "";
1343
+ const headerRight = (headerCfg == null ? void 0 : headerCfg.right) ?? "";
1344
+ const footerLeft = ((footerCfg == null ? void 0 : footerCfg.left) ?? "").replace("{page}", "").replace("{pages}", "").trim();
1345
+ const esc = (s) => s.replace(/"/g, '"').replace(/\\/g, "\\\\");
1346
+ const pagedCss = `
1347
+ @page {
1348
+ size: ${size} ${orientation};
1349
+ margin-top: ${top};
1350
+ margin-bottom: ${bottom};
1351
+ margin-left: ${left};
1352
+ margin-right: ${right};
1353
+ ${headerLeft ? `@top-left { content: "${esc(headerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1354
+ ${headerCenter ? `@top-center { content: "${esc(headerCenter)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1355
+ ${headerRight ? `@top-right { content: "${esc(headerRight)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1356
+ ${footerLeft ? `@bottom-left { content: "${esc(footerLeft)}"; font-size: 9pt; color: #94a3b8; }` : ""}
1357
+ @bottom-right {
1358
+ content: "Page " counter(page) " of " counter(pages);
1359
+ font-size: 9pt;
1360
+ color: #94a3b8;
1361
+ }
1362
+ }
1363
+ @media print {
1364
+ body { padding: 0; }
1365
+ h1, h2, h3, pre, table, blockquote, .callout {
1366
+ break-inside: avoid;
1367
+ }
1368
+ }
1369
+ `;
1370
+ return html.replace("</head>", `<style>${pagedCss}</style></head>`);
1371
+ }
1372
+ function createFallbackPdfBuffer(title = "Document") {
1373
+ const streamContent = `BT /F1 18 Tf 50 750 Td (${title}) Tj ET
1374
+ BT /F1 12 Tf 50 720 Td (Generated via MarkForge Fallback Renderer) Tj ET`;
1375
+ const streamLength = Buffer.byteLength(streamContent, "utf-8");
1376
+ const pdfBody = `%PDF-1.4
1377
+ 1 0 obj
1378
+ << /Type /Catalog /Pages 2 0 R >>
1379
+ endobj
1380
+ 2 0 obj
1381
+ << /Type /Pages /Kids [3 0 R] /Count 1 >>
1382
+ endobj
1383
+ 3 0 obj
1384
+ << /Type /Page /Parent 2 0 R /MediaBox [0 0 595.28 841.89] /Contents 4 0 R /Resources << /Font << /F1 5 0 R >> >> >>
1385
+ endobj
1386
+ 4 0 obj
1387
+ << /Length ${streamLength} >>
1388
+ stream
1389
+ ${streamContent}
1390
+ endstream
1391
+ endobj
1392
+ 5 0 obj
1393
+ << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
1394
+ endobj
1395
+ xref
1396
+ 0 6
1397
+ 0000000000 65535 f
1398
+ 0000000009 00000 n
1399
+ 0000000058 00000 n
1400
+ 0000000115 00000 n
1401
+ 0000000266 00000 n
1402
+ 0000000373 00000 n
1403
+ trailer
1404
+ << /Size 6 /Root 1 0 R >>
1405
+ startxref
1406
+ 453
1407
+ %%EOF
1408
+ `;
1409
+ return Buffer.from(pdfBody, "utf-8");
1410
+ }
1411
+ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
1412
+ const baseHtml = await buildHtmlDocument(doc, config, baseDir);
1413
+ const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
1414
+ const chromePath = findChromeExecutable();
1415
+ if (chromePath) {
1416
+ const tmpId = Math.random().toString(36).substring(2, 9);
1417
+ const tmpDir = os.tmpdir();
1418
+ const tmpHtml = path3.join(tmpDir, `markforge_${tmpId}.html`);
1419
+ const tmpPdf = path3.join(tmpDir, `markforge_${tmpId}.pdf`);
1420
+ try {
1421
+ fs3.writeFileSync(tmpHtml, pagedHtml, "utf-8");
1422
+ let res = (0, import_node_child_process.spawnSync)(
1423
+ chromePath,
1424
+ [
1425
+ "--headless=new",
1426
+ "--disable-gpu",
1427
+ "--no-sandbox",
1428
+ "--disable-setuid-sandbox",
1429
+ "--allow-file-access-from-files",
1430
+ "--force-color-profile=srgb",
1431
+ "--run-all-compositor-stages-before-draw",
1432
+ "--virtual-time-budget=8000",
1433
+ "--no-pdf-header-footer",
1434
+ `--print-to-pdf=${tmpPdf}`,
1435
+ tmpHtml
1436
+ ],
1437
+ { timeout: 3e4 }
1438
+ );
1439
+ if ((res.status !== 0 || !fs3.existsSync(tmpPdf)) && chromePath) {
1440
+ res = (0, import_node_child_process.spawnSync)(
1441
+ chromePath,
1442
+ [
1443
+ "--headless",
1444
+ "--disable-gpu",
1445
+ "--no-sandbox",
1446
+ "--disable-setuid-sandbox",
1447
+ "--allow-file-access-from-files",
1448
+ "--force-color-profile=srgb",
1449
+ "--no-pdf-header-footer",
1450
+ `--print-to-pdf=${tmpPdf}`,
1451
+ tmpHtml
1452
+ ],
1453
+ { timeout: 3e4 }
1454
+ );
1455
+ }
1456
+ if (fs3.existsSync(tmpPdf) && fs3.statSync(tmpPdf).size > 0) {
1457
+ const pdfBuffer = fs3.readFileSync(tmpPdf);
1458
+ return pdfBuffer;
1459
+ }
1460
+ } catch {
1461
+ } finally {
1462
+ try {
1463
+ if (fs3.existsSync(tmpHtml)) fs3.unlinkSync(tmpHtml);
1464
+ if (fs3.existsSync(tmpPdf)) fs3.unlinkSync(tmpPdf);
1465
+ } catch {
1466
+ }
1467
+ }
1468
+ }
1469
+ return createFallbackPdfBuffer(doc.metadata.title || "MarkForge Document");
1470
+ }
1471
+
1472
+ // src/core/mermaid/mermaidRenderer.ts
1473
+ async function renderMermaidToPng(mermaidCode, _baseDir = process.cwd()) {
1474
+ const chromePath = findChromeExecutable();
1475
+ if (!chromePath) {
1476
+ return null;
1477
+ }
1478
+ const tmpId = Math.random().toString(36).substring(2, 9);
1479
+ const tmpDir = os2.tmpdir();
1480
+ const tmpHtml = path4.join(tmpDir, `mermaid_${tmpId}.html`);
1481
+ const tmpScreenshot = path4.join(tmpDir, `mermaid_${tmpId}.png`);
1482
+ const htmlContent = `<!DOCTYPE html>
1483
+ <html>
1484
+ <head>
1485
+ <meta charset="utf-8">
1486
+ <script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
1487
+ <style>
1488
+ body {
1489
+ margin: 0;
1490
+ padding: 16px;
1491
+ background: #ffffff;
1492
+ display: inline-block;
1493
+ }
1494
+ .mermaid {
1495
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
1496
+ }
1497
+ </style>
1498
+ </head>
1499
+ <body>
1500
+ <div id="container" class="mermaid">
1501
+ ${mermaidCode}
1502
+ </div>
1503
+ <script>
1504
+ mermaid.initialize({
1505
+ startOnLoad: true,
1506
+ theme: 'neutral',
1507
+ themeVariables: {
1508
+ primaryColor: '#33CDCF',
1509
+ primaryTextColor: '#0F172A',
1510
+ primaryBorderColor: '#009DA0',
1511
+ lineColor: '#009DA0',
1512
+ secondaryColor: '#ECFDFD',
1513
+ tertiaryColor: '#F8FAFC'
1514
+ }
1515
+ });
1516
+ </script>
1517
+ </body>
1518
+ </html>`;
1519
+ try {
1520
+ fs4.writeFileSync(tmpHtml, htmlContent, "utf-8");
1521
+ const res = (0, import_node_child_process2.spawnSync)(
1522
+ chromePath,
1523
+ [
1524
+ "--headless",
1525
+ "--disable-gpu",
1526
+ "--no-sandbox",
1527
+ "--disable-setuid-sandbox",
1528
+ "--window-size=1200,800",
1529
+ `--screenshot=${tmpScreenshot}`,
1530
+ tmpHtml
1531
+ ],
1532
+ { timeout: 15e3 }
1533
+ );
1534
+ if (res.status === 0 && fs4.existsSync(tmpScreenshot)) {
1535
+ const buffer = fs4.readFileSync(tmpScreenshot);
1536
+ return buffer;
1537
+ }
1538
+ } catch {
1539
+ } finally {
1540
+ try {
1541
+ if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
1542
+ if (fs4.existsSync(tmpScreenshot)) fs4.unlinkSync(tmpScreenshot);
1543
+ } catch {
1544
+ }
1545
+ }
1546
+ return null;
1547
+ }
1548
+
1549
+ // src/core/docx/docxBuilder.ts
1550
+ function parseMarginToTwip(margin, defaultTwip = 1440) {
1551
+ if (typeof margin === "number") return margin;
1552
+ if (!margin) return defaultTwip;
1553
+ const str = margin.trim().toLowerCase();
1554
+ if (str.endsWith("cm")) {
1555
+ const cm = parseFloat(str);
1556
+ return Math.round((0, import_docx.convertMillimetersToTwip)(cm * 10));
1557
+ }
1558
+ if (str.endsWith("mm")) {
1559
+ const mm = parseFloat(str);
1560
+ return Math.round((0, import_docx.convertMillimetersToTwip)(mm));
1561
+ }
1562
+ if (str.endsWith("in") || str.endsWith("inch")) {
1563
+ const inch = parseFloat(str);
1564
+ return Math.round((0, import_docx.convertInchesToTwip)(inch));
1565
+ }
1566
+ if (str.endsWith("pt")) {
1567
+ const pt = parseFloat(str);
1568
+ return Math.round(pt * 20);
1569
+ }
1570
+ const val = parseFloat(str);
1571
+ return isNaN(val) ? defaultTwip : Math.round(val);
1572
+ }
1573
+ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd()) {
1574
+ const runs = [];
1575
+ for (const span of spans) {
1576
+ if (span.type === "image" && span.url) {
1577
+ const resolved = await resolveImage(span.url, baseDir);
1578
+ if (resolved) {
1579
+ let width = 500;
1580
+ let height = 300;
1581
+ if (span.width) width = parseInt(String(span.width), 10) || 500;
1582
+ if (span.height) height = parseInt(String(span.height), 10) || 300;
1583
+ runs.push(
1584
+ new import_docx.ImageRun({
1585
+ data: resolved.buffer,
1586
+ transformation: {
1587
+ width: Math.min(width, 550),
1588
+ height: Math.min(height, 400)
1589
+ },
1590
+ type: "png"
1591
+ })
1592
+ );
1593
+ }
1594
+ continue;
1595
+ }
1596
+ if (span.type === "link" && span.url) {
1597
+ runs.push(
1598
+ new import_docx.ExternalHyperlink({
1599
+ children: [
1600
+ new import_docx.TextRun({
1601
+ text: span.content,
1602
+ style: "Hyperlink",
1603
+ color: "0969DA",
1604
+ underline: {}
1605
+ })
1606
+ ],
1607
+ link: span.url
1608
+ })
1609
+ );
1610
+ continue;
1611
+ }
1612
+ if (span.type === "bold") {
1613
+ runs.push(
1614
+ new import_docx.TextRun({
1615
+ text: span.content,
1616
+ bold: true
1617
+ })
1618
+ );
1619
+ continue;
1620
+ }
1621
+ if (span.type === "italic") {
1622
+ runs.push(
1623
+ new import_docx.TextRun({
1624
+ text: span.content,
1625
+ italics: true
1626
+ })
1627
+ );
1628
+ continue;
1629
+ }
1630
+ if (span.type === "strikethrough") {
1631
+ runs.push(
1632
+ new import_docx.TextRun({
1633
+ text: span.content,
1634
+ strike: true
1635
+ })
1636
+ );
1637
+ continue;
1638
+ }
1639
+ if (span.type === "code") {
1640
+ runs.push(
1641
+ new import_docx.TextRun({
1642
+ text: ` ${span.content} `,
1643
+ font: "Consolas",
1644
+ shading: {
1645
+ type: import_docx.ShadingType.CLEAR,
1646
+ fill: "F1F5F9",
1647
+ color: "0F172A"
1648
+ }
1649
+ })
1650
+ );
1651
+ continue;
1652
+ }
1653
+ if (span.type === "htmlInline") {
1654
+ let colorHex;
1655
+ let bgHex;
1656
+ let isBold = false;
1657
+ let isItalic = false;
1658
+ if (span.style) {
1659
+ if (span.style.color) {
1660
+ colorHex = span.style.color.replace("#", "").trim();
1661
+ }
1662
+ if (span.style.background || span.style["background-color"]) {
1663
+ bgHex = (span.style.background || span.style["background-color"]).replace("#", "").trim();
1664
+ }
1665
+ if (span.style["font-weight"] === "bold" || span.style["font-weight"] === "700") {
1666
+ isBold = true;
1667
+ }
1668
+ if (span.style["font-style"] === "italic") {
1669
+ isItalic = true;
1670
+ }
1671
+ }
1672
+ if (span.children && span.children.length > 0) {
1673
+ const childRuns = await convertInlinesToTextRuns(span.children, baseDir);
1674
+ for (const child of childRuns) {
1675
+ if (child instanceof import_docx.TextRun) {
1676
+ runs.push(child);
1677
+ } else {
1678
+ runs.push(child);
1679
+ }
1680
+ }
1681
+ continue;
1682
+ }
1683
+ runs.push(
1684
+ new import_docx.TextRun({
1685
+ text: span.content,
1686
+ color: colorHex,
1687
+ bold: isBold,
1688
+ italics: isItalic,
1689
+ shading: bgHex ? { type: import_docx.ShadingType.CLEAR, fill: bgHex } : void 0
1690
+ })
1691
+ );
1692
+ continue;
1693
+ }
1694
+ runs.push(
1695
+ new import_docx.TextRun({
1696
+ text: span.content
1697
+ })
1698
+ );
1699
+ }
1700
+ return runs;
1701
+ }
1702
+ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
1703
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1704
+ const metadata = { ...config.metadata, ...doc.metadata };
1705
+ const docElements = [];
1706
+ if (metadata.title) {
1707
+ docElements.push(
1708
+ new import_docx.Paragraph({
1709
+ text: metadata.title,
1710
+ heading: import_docx.HeadingLevel.TITLE,
1711
+ spacing: { before: 200, after: 120 }
1712
+ })
1713
+ );
1714
+ if (metadata.subtitle) {
1715
+ docElements.push(
1716
+ new import_docx.Paragraph({
1717
+ children: [
1718
+ new import_docx.TextRun({
1719
+ text: metadata.subtitle,
1720
+ italics: true,
1721
+ color: "64748B",
1722
+ size: 24
1723
+ // 12pt
1724
+ })
1725
+ ],
1726
+ spacing: { after: 180 }
1727
+ })
1728
+ );
1729
+ }
1730
+ if (metadata.author || metadata.date) {
1731
+ const metaParts = [];
1732
+ if (metadata.author) metaParts.push(`Author: ${Array.isArray(metadata.author) ? metadata.author.join(", ") : metadata.author}`);
1733
+ if (metadata.date) metaParts.push(`Date: ${metadata.date}`);
1734
+ docElements.push(
1735
+ new import_docx.Paragraph({
1736
+ children: [
1737
+ new import_docx.TextRun({
1738
+ text: metaParts.join(" | "),
1739
+ color: "94A3B8",
1740
+ size: 20
1741
+ // 10pt
1742
+ })
1743
+ ],
1744
+ spacing: { after: 360 },
1745
+ border: {
1746
+ bottom: {
1747
+ color: "E2E8F0",
1748
+ space: 10,
1749
+ style: import_docx.BorderStyle.SINGLE,
1750
+ size: 6
1751
+ }
1752
+ }
1753
+ })
1754
+ );
1755
+ }
1756
+ }
1757
+ for (const node of doc.nodes) {
1758
+ if (node.type === "heading") {
1759
+ let headingLevel = import_docx.HeadingLevel.HEADING_1;
1760
+ if (node.level === 2) headingLevel = import_docx.HeadingLevel.HEADING_2;
1761
+ if (node.level === 3) headingLevel = import_docx.HeadingLevel.HEADING_3;
1762
+ if (node.level === 4) headingLevel = import_docx.HeadingLevel.HEADING_4;
1763
+ if (node.level === 5) headingLevel = import_docx.HeadingLevel.HEADING_5;
1764
+ if (node.level === 6) headingLevel = import_docx.HeadingLevel.HEADING_6;
1765
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1766
+ docElements.push(
1767
+ new import_docx.Paragraph({
1768
+ heading: headingLevel,
1769
+ children: runs,
1770
+ spacing: { before: 240, after: 120 }
1771
+ })
1772
+ );
1773
+ continue;
1774
+ }
1775
+ if (node.type === "paragraph") {
1776
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1777
+ docElements.push(
1778
+ new import_docx.Paragraph({
1779
+ children: runs,
1780
+ spacing: { before: 60, after: 140 }
1781
+ })
1782
+ );
1783
+ continue;
1784
+ }
1785
+ if (node.type === "codeBlock") {
1786
+ const codeLines = (node.text || "").split("\n");
1787
+ const codeParagraphs = codeLines.map((l) => {
1788
+ const tokens = tokenizeCodeLine(l, node.language, "light");
1789
+ const textRuns = tokens.map(
1790
+ (t) => new import_docx.TextRun({
1791
+ text: t.text,
1792
+ font: "Consolas",
1793
+ size: 19,
1794
+ // 9.5pt
1795
+ color: t.colorHex,
1796
+ bold: t.bold,
1797
+ italics: t.italic
1798
+ })
1799
+ );
1800
+ return new import_docx.Paragraph({
1801
+ children: textRuns,
1802
+ spacing: { before: 20, after: 20 }
1803
+ });
1804
+ });
1805
+ const codeTable = new import_docx.Table({
1806
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
1807
+ columnWidths: [9e3],
1808
+ rows: [
1809
+ new import_docx.TableRow({
1810
+ children: [
1811
+ new import_docx.TableCell({
1812
+ width: { size: 9e3, type: import_docx.WidthType.DXA },
1813
+ shading: { fill: "F8FAFC", type: import_docx.ShadingType.CLEAR },
1814
+ margins: { top: 140, bottom: 140, left: 180, right: 180 },
1815
+ borders: {
1816
+ top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
1817
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
1818
+ left: { style: import_docx.BorderStyle.SINGLE, size: 8, color: "33CDCF" },
1819
+ right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" }
1820
+ },
1821
+ children: codeParagraphs
1822
+ })
1823
+ ]
1824
+ })
1825
+ ]
1826
+ });
1827
+ docElements.push(codeTable);
1828
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 120 } }));
1829
+ continue;
1830
+ }
1831
+ if (node.type === "callout") {
1832
+ let borderColor = "33CDCF";
1833
+ let bgFill = "ECFDFD";
1834
+ let title = "NOTE";
1835
+ if (node.calloutType === "TIP") {
1836
+ borderColor = "10B981";
1837
+ bgFill = "ECFDF5";
1838
+ title = "TIP";
1839
+ } else if (node.calloutType === "WARNING") {
1840
+ borderColor = "F59E0B";
1841
+ bgFill = "FFFBEB";
1842
+ title = "WARNING";
1843
+ } else if (node.calloutType === "CAUTION") {
1844
+ borderColor = "EF4444";
1845
+ bgFill = "FEF2F2";
1846
+ title = "CAUTION";
1847
+ } else if (node.calloutType === "IMPORTANT") {
1848
+ borderColor = "8B5CF6";
1849
+ bgFill = "F5F3FF";
1850
+ title = "IMPORTANT";
1851
+ }
1852
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1853
+ const calloutTable = new import_docx.Table({
1854
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
1855
+ columnWidths: [9e3],
1856
+ rows: [
1857
+ new import_docx.TableRow({
1858
+ children: [
1859
+ new import_docx.TableCell({
1860
+ width: { size: 9e3, type: import_docx.WidthType.DXA },
1861
+ shading: { fill: bgFill, type: import_docx.ShadingType.CLEAR },
1862
+ margins: { top: 120, bottom: 120, left: 160, right: 160 },
1863
+ borders: {
1864
+ top: { style: import_docx.BorderStyle.NONE },
1865
+ bottom: { style: import_docx.BorderStyle.NONE },
1866
+ right: { style: import_docx.BorderStyle.NONE },
1867
+ left: { style: import_docx.BorderStyle.SINGLE, size: 16, color: borderColor }
1868
+ },
1869
+ children: [
1870
+ new import_docx.Paragraph({
1871
+ children: [
1872
+ new import_docx.TextRun({
1873
+ text: `[${title}]`,
1874
+ bold: true,
1875
+ color: borderColor,
1876
+ size: 20
1877
+ })
1878
+ ],
1879
+ spacing: { after: 60 }
1880
+ }),
1881
+ new import_docx.Paragraph({
1882
+ children: runs
1883
+ })
1884
+ ]
1885
+ })
1886
+ ]
1887
+ })
1888
+ ]
1889
+ });
1890
+ docElements.push(calloutTable);
1891
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 120 } }));
1892
+ continue;
1893
+ }
1894
+ if (node.type === "blockquote") {
1895
+ const quoteParagraphs = [];
1896
+ const lines = (node.text || "").split("\n\n").filter(Boolean);
1897
+ if (lines.length > 0) {
1898
+ for (const lineText of lines) {
1899
+ const spans = parseInlineSpans(lineText.replace(/^>\s?/, "").trim());
1900
+ const lineRuns = await convertInlinesToTextRuns(spans, baseDir);
1901
+ quoteParagraphs.push(
1902
+ new import_docx.Paragraph({
1903
+ children: lineRuns,
1904
+ spacing: { before: 40, after: 40 }
1905
+ })
1906
+ );
1907
+ }
1908
+ } else {
1909
+ const runs = await convertInlinesToTextRuns(node.inlines, baseDir);
1910
+ quoteParagraphs.push(new import_docx.Paragraph({ children: runs }));
1911
+ }
1912
+ const quoteTable = new import_docx.Table({
1913
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
1914
+ columnWidths: [9e3],
1915
+ rows: [
1916
+ new import_docx.TableRow({
1917
+ children: [
1918
+ new import_docx.TableCell({
1919
+ width: { size: 9e3, type: import_docx.WidthType.DXA },
1920
+ shading: { fill: "F8FAFC", type: import_docx.ShadingType.CLEAR },
1921
+ margins: { top: 100, bottom: 100, left: 140, right: 140 },
1922
+ borders: {
1923
+ top: { style: import_docx.BorderStyle.NONE },
1924
+ bottom: { style: import_docx.BorderStyle.NONE },
1925
+ right: { style: import_docx.BorderStyle.NONE },
1926
+ left: { style: import_docx.BorderStyle.SINGLE, size: 12, color: "33CDCF" }
1927
+ },
1928
+ children: quoteParagraphs
1929
+ })
1930
+ ]
1931
+ })
1932
+ ]
1933
+ });
1934
+ docElements.push(quoteTable);
1935
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 120 } }));
1936
+ continue;
1937
+ }
1938
+ if (node.type === "mermaid") {
1939
+ const pngBuffer = await renderMermaidToPng(node.text || "", baseDir);
1940
+ if (pngBuffer) {
1941
+ docElements.push(
1942
+ new import_docx.Paragraph({
1943
+ alignment: import_docx.AlignmentType.CENTER,
1944
+ children: [
1945
+ new import_docx.ImageRun({
1946
+ data: pngBuffer,
1947
+ transformation: {
1948
+ width: 550,
1949
+ height: 320
1950
+ },
1951
+ type: "png"
1952
+ })
1953
+ ],
1954
+ spacing: { before: 120, after: 120 }
1955
+ })
1956
+ );
1957
+ } else {
1958
+ docElements.push(
1959
+ new import_docx.Paragraph({
1960
+ children: [
1961
+ new import_docx.TextRun({ text: "[Mermaid Diagram: " + (node.text || "").slice(0, 40) + "...]", bold: true, color: "33CDCF" })
1962
+ ],
1963
+ spacing: { before: 60, after: 60 }
1964
+ })
1965
+ );
1966
+ }
1967
+ continue;
1968
+ }
1969
+ if (node.type === "table" && node.children) {
1970
+ const tableRows = [];
1971
+ const numCols = ((_b = (_a = node.children[0]) == null ? void 0 : _a.children) == null ? void 0 : _b.length) || 1;
1972
+ const colWidth = Math.floor(9e3 / numCols);
1973
+ for (const rowNode of node.children) {
1974
+ const cells = [];
1975
+ if (rowNode.children) {
1976
+ for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
1977
+ const cellNode = rowNode.children[colIdx];
1978
+ const align = (_c = node.align) == null ? void 0 : _c[colIdx];
1979
+ let alignment = import_docx.AlignmentType.LEFT;
1980
+ if (align === "center") alignment = import_docx.AlignmentType.CENTER;
1981
+ if (align === "right") alignment = import_docx.AlignmentType.RIGHT;
1982
+ const runs = await convertInlinesToTextRuns(cellNode.inlines, baseDir);
1983
+ cells.push(
1984
+ new import_docx.TableCell({
1985
+ width: { size: colWidth, type: import_docx.WidthType.DXA },
1986
+ shading: rowNode.isHeader ? { fill: "F1F5F9", type: import_docx.ShadingType.CLEAR } : void 0,
1987
+ margins: { top: 100, bottom: 100, left: 120, right: 120 },
1988
+ borders: {
1989
+ top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
1990
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
1991
+ left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" },
1992
+ right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "CBD5E1" }
1993
+ },
1994
+ children: [
1995
+ new import_docx.Paragraph({
1996
+ alignment,
1997
+ children: rowNode.isHeader ? runs.map((r) => r instanceof import_docx.TextRun ? new import_docx.TextRun({ ...r, bold: true, color: "0F172A" }) : r) : runs
1998
+ })
1999
+ ]
2000
+ })
2001
+ );
2002
+ }
2003
+ }
2004
+ tableRows.push(
2005
+ new import_docx.TableRow({
2006
+ tableHeader: rowNode.isHeader,
2007
+ children: cells
2008
+ })
2009
+ );
2010
+ }
2011
+ const docxTable = new import_docx.Table({
2012
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
2013
+ columnWidths: Array(numCols).fill(colWidth),
2014
+ rows: tableRows
2015
+ });
2016
+ docElements.push(docxTable);
2017
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 140 } }));
2018
+ continue;
2019
+ }
2020
+ if (node.type === "list" && node.children) {
2021
+ for (const item of node.children) {
2022
+ const runs = await convertInlinesToTextRuns(item.inlines, baseDir);
2023
+ let prefix = "";
2024
+ if (item.checked !== void 0) {
2025
+ prefix = item.checked ? "[X] " : "[ ] ";
2026
+ }
2027
+ docElements.push(
2028
+ new import_docx.Paragraph({
2029
+ bullet: node.ordered ? void 0 : { level: 0 },
2030
+ children: [
2031
+ ...prefix ? [new import_docx.TextRun({ text: prefix, bold: true, font: "Consolas" })] : [],
2032
+ ...runs
2033
+ ],
2034
+ spacing: { before: 40, after: 40 }
2035
+ })
2036
+ );
2037
+ }
2038
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 80 } }));
2039
+ continue;
2040
+ }
2041
+ if (node.type === "thematicBreak") {
2042
+ docElements.push(
2043
+ new import_docx.Paragraph({
2044
+ spacing: { before: 180, after: 180 },
2045
+ border: {
2046
+ bottom: {
2047
+ color: "E2E8F0",
2048
+ space: 4,
2049
+ style: import_docx.BorderStyle.SINGLE,
2050
+ size: 6
2051
+ }
2052
+ }
2053
+ })
2054
+ );
2055
+ continue;
2056
+ }
2057
+ if (node.type === "htmlBlock" && node.rawHtml) {
2058
+ const isCard = node.rawHtml.includes("card") || node.rawHtml.includes("box") || node.rawHtml.includes("metric");
2059
+ const cleanInner = node.rawHtml.replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<\/p>/gi, "\n\n").replace(/<\/div>/gi, "\n").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, " ").replace(/[ \t]+/g, " ").replace(/\n\s+/g, "\n").trim();
2060
+ if (cleanInner) {
2061
+ const innerParagraphs = cleanInner.split("\n\n").filter(Boolean);
2062
+ const paraElements = [];
2063
+ for (const p of innerParagraphs) {
2064
+ const spans = parseInlineSpans(p.trim());
2065
+ const runs = await convertInlinesToTextRuns(spans, baseDir);
2066
+ if (runs.length > 0) {
2067
+ paraElements.push(
2068
+ new import_docx.Paragraph({
2069
+ children: runs,
2070
+ spacing: { before: 30, after: 30 }
2071
+ })
2072
+ );
2073
+ }
2074
+ }
2075
+ if (isCard && paraElements.length > 0) {
2076
+ const cardTable = new import_docx.Table({
2077
+ width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
2078
+ columnWidths: [9e3],
2079
+ rows: [
2080
+ new import_docx.TableRow({
2081
+ children: [
2082
+ new import_docx.TableCell({
2083
+ width: { size: 9e3, type: import_docx.WidthType.DXA },
2084
+ shading: { fill: "F8FAFC", type: import_docx.ShadingType.CLEAR },
2085
+ margins: { top: 140, bottom: 140, left: 180, right: 180 },
2086
+ borders: {
2087
+ top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2088
+ bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" },
2089
+ left: { style: import_docx.BorderStyle.SINGLE, size: 8, color: "33CDCF" },
2090
+ right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: "E2E8F0" }
2091
+ },
2092
+ children: paraElements
2093
+ })
2094
+ ]
2095
+ })
2096
+ ]
2097
+ });
2098
+ docElements.push(cardTable);
2099
+ docElements.push(new import_docx.Paragraph({ spacing: { after: 120 } }));
2100
+ } else {
2101
+ for (const pe of paraElements) {
2102
+ docElements.push(pe);
2103
+ }
2104
+ }
2105
+ }
2106
+ continue;
2107
+ }
2108
+ }
2109
+ const headerObj = metadata.header || config.header;
2110
+ const footerObj = metadata.footer || config.footer;
2111
+ const headerTabStops = [
2112
+ { type: import_docx.TabStopType.CENTER, position: 4513 },
2113
+ { type: import_docx.TabStopType.RIGHT, position: 9026 }
2114
+ ];
2115
+ const docHeader = headerObj ? new import_docx.Header({
2116
+ children: [
2117
+ new import_docx.Paragraph({
2118
+ tabStops: headerTabStops,
2119
+ children: [
2120
+ // Left zone
2121
+ ...headerObj.left ? [
2122
+ new import_docx.TextRun({
2123
+ text: headerObj.left.replace("{title}", metadata.title || ""),
2124
+ color: "94A3B8",
2125
+ size: 18
2126
+ })
2127
+ ] : [],
2128
+ // Center zone (tab + text)
2129
+ ...headerObj.center ? [
2130
+ new import_docx.TextRun({ text: " ", color: "94A3B8", size: 18 }),
2131
+ new import_docx.TextRun({
2132
+ text: headerObj.center.replace("{title}", metadata.title || ""),
2133
+ color: "94A3B8",
2134
+ size: 18
2135
+ })
2136
+ ] : [],
2137
+ // Right zone (tab + text) — skip extra tab if center already used one
2138
+ ...headerObj.right ? [
2139
+ new import_docx.TextRun({
2140
+ text: headerObj.left || headerObj.center ? " " : "",
2141
+ color: "94A3B8",
2142
+ size: 18
2143
+ }),
2144
+ new import_docx.TextRun({
2145
+ text: headerObj.right.replace("{title}", metadata.title || ""),
2146
+ color: "94A3B8",
2147
+ size: 18
2148
+ })
2149
+ ] : []
2150
+ ]
2151
+ })
2152
+ ]
2153
+ }) : void 0;
2154
+ const docFooter = footerObj ? new import_docx.Footer({
2155
+ children: [
2156
+ new import_docx.Paragraph({
2157
+ tabStops: headerTabStops,
2158
+ children: [
2159
+ // Left zone
2160
+ ...footerObj.left ? [
2161
+ new import_docx.TextRun({
2162
+ text: footerObj.left.replace("{page}", "").replace("{pages}", "").trim(),
2163
+ color: "94A3B8",
2164
+ size: 18
2165
+ })
2166
+ ] : [],
2167
+ // Right zone: always includes page number if right is configured or footer exists
2168
+ new import_docx.TextRun({ text: " ", color: "94A3B8", size: 18 }),
2169
+ new import_docx.TextRun({ text: "Page ", color: "94A3B8", size: 18 }),
2170
+ new import_docx.TextRun({ children: [import_docx.PageNumber.CURRENT], color: "94A3B8", size: 18 }),
2171
+ new import_docx.TextRun({ text: " of ", color: "94A3B8", size: 18 }),
2172
+ new import_docx.TextRun({ children: [import_docx.PageNumber.TOTAL_PAGES], color: "94A3B8", size: 18 })
2173
+ ]
2174
+ })
2175
+ ]
2176
+ }) : void 0;
2177
+ const topMargin = parseMarginToTwip(((_d = metadata.margins) == null ? void 0 : _d.top) || ((_e = config.margins) == null ? void 0 : _e.top), 1440);
2178
+ const bottomMargin = parseMarginToTwip(((_f = metadata.margins) == null ? void 0 : _f.bottom) || ((_g = config.margins) == null ? void 0 : _g.bottom), 1440);
2179
+ const leftMargin = parseMarginToTwip(((_h = metadata.margins) == null ? void 0 : _h.left) || ((_i = config.margins) == null ? void 0 : _i.left), 1440);
2180
+ const rightMargin = parseMarginToTwip(((_j = metadata.margins) == null ? void 0 : _j.right) || ((_k = config.margins) == null ? void 0 : _k.right), 1440);
2181
+ const isLandscape = (metadata.orientation || config.orientation) === "landscape";
2182
+ const document = new import_docx.Document({
2183
+ styles: {
2184
+ default: {
2185
+ document: {
2186
+ run: {
2187
+ font: "Segoe UI",
2188
+ size: 22,
2189
+ // 11pt
2190
+ color: "0F172A"
2191
+ }
2192
+ }
2193
+ }
2194
+ },
2195
+ sections: [
2196
+ {
2197
+ properties: {
2198
+ page: {
2199
+ size: {
2200
+ orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
2201
+ },
2202
+ margin: {
2203
+ top: topMargin,
2204
+ bottom: bottomMargin,
2205
+ left: leftMargin,
2206
+ right: rightMargin,
2207
+ header: 720,
2208
+ footer: 720
2209
+ }
2210
+ }
2211
+ },
2212
+ headers: docHeader ? { default: docHeader } : void 0,
2213
+ footers: docFooter ? { default: docFooter } : void 0,
2214
+ children: docElements
2215
+ }
2216
+ ]
2217
+ });
2218
+ return await import_docx.Packer.toBuffer(document);
2219
+ }
2220
+
2221
+ // src/config/loadConfig.ts
2222
+ var fs5 = __toESM(require("fs"));
2223
+ var path5 = __toESM(require("path"));
2224
+ var import_node_url = require("url");
2225
+ var YAML = __toESM(require("yaml"));
2226
+ var DEFAULT_CONFIG_FILENAMES = [
2227
+ "markforge.config.json",
2228
+ ".markforgerc.json",
2229
+ "markforge.config.yaml",
2230
+ "markforge.config.yml",
2231
+ ".markforgerc.yaml",
2232
+ ".markforgerc.yml",
2233
+ ".markforgerc",
2234
+ "markforge.config.ts",
2235
+ "markforge.config.js",
2236
+ "markforge.config.mjs",
2237
+ "markforge.config.cjs"
2238
+ ];
2239
+ var DEFAULT_CONFIG = {
2240
+ to: ["docx", "pdf"],
2241
+ outputDir: void 0,
2242
+ theme: "default",
2243
+ css: void 0,
2244
+ orientation: "portrait",
2245
+ paperSize: "A4",
2246
+ margins: {
2247
+ top: "2.5cm",
2248
+ bottom: "2.5cm",
2249
+ left: "2.5cm",
2250
+ right: "2.5cm"
2251
+ },
2252
+ header: void 0,
2253
+ footer: {
2254
+ right: "Page {page} of {pages}"
2255
+ },
2256
+ toc: false,
2257
+ watermark: void 0,
2258
+ embedImages: true,
2259
+ metadata: void 0,
2260
+ watch: false,
2261
+ serve: false,
2262
+ port: 4e3,
2263
+ open: false,
2264
+ bundleHtml: true,
2265
+ syntaxTheme: "github-dark"
2266
+ };
2267
+ async function loadConfig(customPath, cwd = process.cwd()) {
2268
+ let resolvedPath = null;
2269
+ if (customPath) {
2270
+ resolvedPath = path5.isAbsolute(customPath) ? customPath : path5.resolve(cwd, customPath);
2271
+ if (!fs5.existsSync(resolvedPath)) {
2272
+ throw new Error(`Configuration file not found: ${resolvedPath}`);
2273
+ }
2274
+ } else {
2275
+ for (const filename of DEFAULT_CONFIG_FILENAMES) {
2276
+ const candidate = path5.resolve(cwd, filename);
2277
+ if (fs5.existsSync(candidate)) {
2278
+ resolvedPath = candidate;
2279
+ break;
2280
+ }
2281
+ }
2282
+ }
2283
+ if (!resolvedPath) {
2284
+ return {
2285
+ config: { ...DEFAULT_CONFIG },
2286
+ configPath: null
2287
+ };
2288
+ }
2289
+ const ext = path5.extname(resolvedPath).toLowerCase();
2290
+ let userConfig = {};
2291
+ if (ext === ".json" || resolvedPath.endsWith(".markforgerc")) {
2292
+ const raw = fs5.readFileSync(resolvedPath, "utf-8");
2293
+ userConfig = JSON.parse(raw);
2294
+ } else if (ext === ".yaml" || ext === ".yml") {
2295
+ const raw = fs5.readFileSync(resolvedPath, "utf-8");
2296
+ userConfig = YAML.parse(raw);
2297
+ } else if (ext === ".ts" || ext === ".js" || ext === ".mjs" || ext === ".cjs") {
2298
+ try {
2299
+ const fileUrl = (0, import_node_url.pathToFileURL)(resolvedPath).href;
2300
+ const mod = await import(fileUrl);
2301
+ userConfig = mod.default || mod;
2302
+ } catch {
2303
+ const required = require(resolvedPath);
2304
+ userConfig = required.default || required;
2305
+ }
2306
+ }
2307
+ const mergedConfig = {
2308
+ ...DEFAULT_CONFIG,
2309
+ ...userConfig,
2310
+ margins: {
2311
+ ...DEFAULT_CONFIG.margins,
2312
+ ...userConfig.margins
2313
+ },
2314
+ header: userConfig.header || DEFAULT_CONFIG.header,
2315
+ footer: userConfig.footer || DEFAULT_CONFIG.footer,
2316
+ metadata: userConfig.metadata
2317
+ };
2318
+ return {
2319
+ config: mergedConfig,
2320
+ configPath: resolvedPath
2321
+ };
2322
+ }
2323
+
2324
+ // src/core/engine.ts
2325
+ function formatServerTimestamp(date = /* @__PURE__ */ new Date()) {
2326
+ const pad = (n) => String(n).padStart(2, "0");
2327
+ const year = date.getFullYear();
2328
+ const month = pad(date.getMonth() + 1);
2329
+ const day = pad(date.getDate());
2330
+ const hours = pad(date.getHours());
2331
+ const minutes = pad(date.getMinutes());
2332
+ const seconds = pad(date.getSeconds());
2333
+ const offsetMinutes = -date.getTimezoneOffset();
2334
+ const sign = offsetMinutes >= 0 ? "+" : "-";
2335
+ const absOffsetHours = pad(Math.floor(Math.abs(offsetMinutes) / 60));
2336
+ const absOffsetMinutes = pad(Math.abs(offsetMinutes) % 60);
2337
+ const tzString = `GMT${sign}${absOffsetHours}:${absOffsetMinutes}`;
2338
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} (${tzString})`;
2339
+ }
2340
+ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgress) {
2341
+ const startTime = Date.now();
2342
+ const config = { ...DEFAULT_CONFIG, ...userConfig };
2343
+ let rawMarkdown = "";
2344
+ let baseDir = process.cwd();
2345
+ let inputFileName = "document.md";
2346
+ let isFilePath = false;
2347
+ if (fs6.existsSync(inputFilePathOrContent)) {
2348
+ isFilePath = true;
2349
+ rawMarkdown = fs6.readFileSync(inputFilePathOrContent, "utf-8");
2350
+ baseDir = path6.dirname(path6.resolve(inputFilePathOrContent));
2351
+ inputFileName = path6.basename(inputFilePathOrContent);
2352
+ } else {
2353
+ rawMarkdown = inputFilePathOrContent;
2354
+ }
2355
+ onProgress == null ? void 0 : onProgress(`Parsing markdown AST: ${inputFileName}...`);
2356
+ const parsedDoc = parseMarkdownDocument(rawMarkdown);
2357
+ const baseName = inputFileName.replace(/\.(md|mdx|markdown)$/i, "");
2358
+ const outputDir = config.outputDir ? path6.isAbsolute(config.outputDir) ? config.outputDir : path6.resolve(process.cwd(), config.outputDir) : baseDir;
2359
+ if (!fs6.existsSync(outputDir)) {
2360
+ fs6.mkdirSync(outputDir, { recursive: true });
2361
+ }
2362
+ const formats = Array.isArray(config.to) ? config.to : [config.to || "docx", "pdf"];
2363
+ const generatedFiles = [];
2364
+ const errors = [];
2365
+ for (const fmt of formats) {
2366
+ try {
2367
+ if (fmt === "docx") {
2368
+ onProgress == null ? void 0 : onProgress(`Generating DOCX document: ${baseName}.docx...`);
2369
+ const docxBuffer = await buildDocxDocument(parsedDoc, config, baseDir);
2370
+ const docxPath = path6.join(outputDir, `${baseName}.docx`);
2371
+ fs6.writeFileSync(docxPath, docxBuffer);
2372
+ generatedFiles.push({
2373
+ format: "docx",
2374
+ filePath: docxPath,
2375
+ fileName: `${baseName}.docx`,
2376
+ sizeBytes: docxBuffer.length
2377
+ });
2378
+ } else if (fmt === "html") {
2379
+ onProgress == null ? void 0 : onProgress(`Generating HTML document: ${baseName}.html...`);
2380
+ const htmlString = await buildHtmlDocument(parsedDoc, config, baseDir);
2381
+ const htmlPath = path6.join(outputDir, `${baseName}.html`);
2382
+ fs6.writeFileSync(htmlPath, htmlString, "utf-8");
2383
+ generatedFiles.push({
2384
+ format: "html",
2385
+ filePath: htmlPath,
2386
+ fileName: `${baseName}.html`,
2387
+ sizeBytes: Buffer.byteLength(htmlString, "utf-8")
2388
+ });
2389
+ } else if (fmt === "pdf") {
2390
+ onProgress == null ? void 0 : onProgress(`Generating PDF document: ${baseName}.pdf...`);
2391
+ const pdfBuffer = await buildPdfDocument(parsedDoc, config, baseDir);
2392
+ const pdfPath = path6.join(outputDir, `${baseName}.pdf`);
2393
+ fs6.writeFileSync(pdfPath, pdfBuffer);
2394
+ generatedFiles.push({
2395
+ format: "pdf",
2396
+ filePath: pdfPath,
2397
+ fileName: `${baseName}.pdf`,
2398
+ sizeBytes: pdfBuffer.length
2399
+ });
2400
+ }
2401
+ } catch (err) {
2402
+ const errMsg = err instanceof Error ? err.message : String(err);
2403
+ errors.push(`Failed to generate ${fmt}: ${errMsg}`);
2404
+ }
2405
+ }
2406
+ const durationMs = Date.now() - startTime;
2407
+ return {
2408
+ inputFile: isFilePath ? inputFilePathOrContent : "inline-string",
2409
+ durationMs,
2410
+ metadata: parsedDoc.metadata,
2411
+ files: generatedFiles,
2412
+ errors
2413
+ };
2414
+ }
2415
+
2416
+ // src/config/defineConfig.ts
2417
+ function defineConfig(config) {
2418
+ return config;
2419
+ }
2420
+
2421
+ // src/version.ts
2422
+ var fs7 = __toESM(require("fs"));
2423
+ var path7 = __toESM(require("path"));
2424
+ var MARKFORGE_VERSION = "0.1.0";
2425
+ function getMarkforgeVersion(fromDir = __dirname) {
2426
+ try {
2427
+ let currentDir = fromDir;
2428
+ for (let i = 0; i < 5; i++) {
2429
+ const pkgJsonPath = path7.join(currentDir, "package.json");
2430
+ if (fs7.existsSync(pkgJsonPath)) {
2431
+ const pkg = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
2432
+ if (pkg.name === "@masumdev/markforge" && pkg.version) {
2433
+ return pkg.version;
2434
+ }
2435
+ }
2436
+ const parentDir = path7.dirname(currentDir);
2437
+ if (parentDir === currentDir) break;
2438
+ currentDir = parentDir;
2439
+ }
2440
+ } catch {
2441
+ }
2442
+ return MARKFORGE_VERSION;
2443
+ }
2444
+ // Annotate the CommonJS export names for ESM import in node:
2445
+ 0 && (module.exports = {
2446
+ DEFAULT_CONFIG,
2447
+ MARKFORGE_VERSION,
2448
+ SYNTAX_COLORS,
2449
+ THEMES,
2450
+ THEME_ACADEMIC,
2451
+ THEME_DEFAULT,
2452
+ buildDocxDocument,
2453
+ buildHtmlDocument,
2454
+ buildPdfDocument,
2455
+ compileMarkdown,
2456
+ defineConfig,
2457
+ escapeHtml,
2458
+ findChromeExecutable,
2459
+ formatServerTimestamp,
2460
+ getMarkforgeVersion,
2461
+ getMimeType,
2462
+ highlightCodeToHtml,
2463
+ injectPagedMediaStyles,
2464
+ inlineHtmlImages,
2465
+ loadConfig,
2466
+ markforge,
2467
+ parseInlineSpans,
2468
+ parseMarginToTwip,
2469
+ parseMarkdownDocument,
2470
+ renderInlinesToHtml,
2471
+ renderMermaidToPng,
2472
+ resolveImage,
2473
+ slugify,
2474
+ tokenizeCodeLine
2475
+ });