@masumdev/markforge 0.4.1 → 0.5.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.
@@ -0,0 +1,847 @@
1
+ #!/usr/bin/env node
2
+ try {
3
+ if (typeof globalThis !== "undefined" && (!globalThis.localStorage || typeof globalThis.localStorage.getItem !== "function")) {
4
+ Object.defineProperty(globalThis, "localStorage", {
5
+ value: { getItem: () => null, setItem: () => {}, removeItem: () => {}, clear: () => {}, key: () => null, length: 0 },
6
+ configurable: true, writable: true,
7
+ });
8
+ }
9
+ } catch {}
10
+ import {
11
+ MARKFORGE_VERSION
12
+ } from "./chunk-UNWAEVDU.mjs";
13
+ import {
14
+ applyHeadingNumbering,
15
+ buildDocxDocument,
16
+ buildHtmlDocument,
17
+ buildPdfDocument,
18
+ findChromeExecutable,
19
+ parseMarkdownDocument,
20
+ replaceDocumentTokens,
21
+ resolveDocumentConfig
22
+ } from "./chunk-H3ZUUSHP.mjs";
23
+ import {
24
+ DEFAULT_CONFIG
25
+ } from "./chunk-BPHDY6VB.mjs";
26
+ import "./chunk-YZHGBG4N.mjs";
27
+
28
+ // src/ui/App.tsx
29
+ import { useState, useEffect } from "react";
30
+ import { Box as Box4 } from "ink";
31
+
32
+ // src/ui/Header.tsx
33
+ import * as path from "path";
34
+ import { Box, Text } from "ink";
35
+ import { jsx, jsxs } from "react/jsx-runtime";
36
+ function formatDisplayPath(p) {
37
+ if (!p) return "";
38
+ try {
39
+ const rel = path.relative(process.cwd(), p);
40
+ return rel && !rel.startsWith("..") && rel.length < p.length ? `./${rel}` : p;
41
+ } catch {
42
+ return p;
43
+ }
44
+ }
45
+ var Header = ({
46
+ inputFile,
47
+ configPath,
48
+ config,
49
+ theme = "corporate",
50
+ targetFormats = ["docx", "pdf"]
51
+ }) => {
52
+ const resolvedTheme = (config == null ? void 0 : config.theme) || theme;
53
+ const themeLabel = typeof resolvedTheme === "object" ? "Custom (ThemeProps)" : String(resolvedTheme || "corporate");
54
+ const activeFeatures = [];
55
+ if (config == null ? void 0 : config.toc) activeFeatures.push("TOC");
56
+ if (config == null ? void 0 : config.numberHeadings) {
57
+ if (typeof config.numberHeadings === "object") {
58
+ const isNumEnabled = config.numberHeadings.enabled ?? true;
59
+ if (isNumEnabled) {
60
+ activeFeatures.push(`Numbering (depth ${config.numberHeadings.depth ?? 3})`);
61
+ }
62
+ } else if (config.numberHeadings === true) {
63
+ activeFeatures.push("Numbering (depth 3)");
64
+ }
65
+ }
66
+ if (config == null ? void 0 : config.coverPage) {
67
+ if (typeof config.coverPage === "object" && config.coverPage.enabled !== false) {
68
+ activeFeatures.push(`Cover (${config.coverPage.preset || "corporate"})`);
69
+ } else if (config.coverPage === true) {
70
+ activeFeatures.push("Cover (corporate)");
71
+ }
72
+ }
73
+ if (config == null ? void 0 : config.backCover) {
74
+ if (typeof config.backCover === "object" && config.backCover.enabled !== false) {
75
+ activeFeatures.push(`BackCover (${config.backCover.preset || "corporate"})`);
76
+ } else if (config.backCover === true) {
77
+ activeFeatures.push("BackCover (corporate)");
78
+ }
79
+ }
80
+ if (config == null ? void 0 : config.watermark) {
81
+ const wmText = typeof config.watermark === "object" ? config.watermark.text : config.watermark;
82
+ if (wmText) {
83
+ activeFeatures.push(`Watermark ("${wmText}")`);
84
+ }
85
+ }
86
+ if ((config == null ? void 0 : config.security) && (config.security.userPassword || config.security.ownerPassword)) {
87
+ activeFeatures.push("AES-256 Security");
88
+ }
89
+ if ((config == null ? void 0 : config.math) !== false) activeFeatures.push("KaTeX Math");
90
+ if (config == null ? void 0 : config.syntaxTheme) activeFeatures.push(`Syntax (${config.syntaxTheme})`);
91
+ const paperLabel = `${(config == null ? void 0 : config.paperSize) || "A4"} (${(config == null ? void 0 : config.orientation) || "portrait"})`;
92
+ const outputLabel = (config == null ? void 0 : config.outputDir) ? formatDisplayPath(config.outputDir) : "./ (default)";
93
+ const configLabel = configPath ? formatDisplayPath(configPath) : "(default auto-discovery)";
94
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: /* @__PURE__ */ jsxs(
95
+ Box,
96
+ {
97
+ borderStyle: "round",
98
+ borderColor: "cyan",
99
+ paddingX: 2,
100
+ paddingY: 1,
101
+ flexDirection: "column",
102
+ children: [
103
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", alignItems: "center", children: [
104
+ /* @__PURE__ */ jsxs(Box, { children: [
105
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "MARKFORGE" }),
106
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: " | Document Publishing Engine" })
107
+ ] }),
108
+ /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsxs(Text, { color: "black", backgroundColor: "cyan", bold: true, children: [
109
+ " ",
110
+ "v",
111
+ MARKFORGE_VERSION,
112
+ " "
113
+ ] }) })
114
+ ] }),
115
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
116
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", children: [
117
+ /* @__PURE__ */ jsxs(Box, { children: [
118
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Source: " }),
119
+ /* @__PURE__ */ jsx(Text, { color: "white", bold: true, children: formatDisplayPath(inputFile) })
120
+ ] }),
121
+ /* @__PURE__ */ jsxs(Box, { children: [
122
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Config: " }),
123
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: configLabel })
124
+ ] })
125
+ ] }),
126
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginTop: 0, children: [
127
+ /* @__PURE__ */ jsxs(Box, { children: [
128
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Theme: " }),
129
+ /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: themeLabel })
130
+ ] }),
131
+ targetFormats && targetFormats.length > 0 && /* @__PURE__ */ jsxs(Box, { children: [
132
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Targets: " }),
133
+ /* @__PURE__ */ jsx(Text, { color: "yellow", bold: true, children: targetFormats.map((f) => f.toUpperCase()).join(", ") })
134
+ ] })
135
+ ] }),
136
+ /* @__PURE__ */ jsxs(Box, { justifyContent: "space-between", marginTop: 0, children: [
137
+ /* @__PURE__ */ jsxs(Box, { children: [
138
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Layout: " }),
139
+ /* @__PURE__ */ jsx(Text, { color: "green", children: paperLabel })
140
+ ] }),
141
+ /* @__PURE__ */ jsxs(Box, { children: [
142
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Output: " }),
143
+ /* @__PURE__ */ jsx(Text, { color: "white", children: outputLabel })
144
+ ] })
145
+ ] }),
146
+ activeFeatures.length > 0 && /* @__PURE__ */ jsxs(Box, { marginTop: 0, children: [
147
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Modules: " }),
148
+ /* @__PURE__ */ jsx(Text, { color: "blue", children: activeFeatures.join(" | ") })
149
+ ] })
150
+ ] }),
151
+ /* @__PURE__ */ jsxs(Box, { marginTop: 1, justifyContent: "space-between", children: [
152
+ /* @__PURE__ */ jsxs(Box, { children: [
153
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "Author: " }),
154
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "Ma'sum" }),
155
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: " (@masumrpg)" })
156
+ ] }),
157
+ /* @__PURE__ */ jsxs(Box, { children: [
158
+ /* @__PURE__ */ jsx(Text, { color: "gray", children: "GitHub: " }),
159
+ /* @__PURE__ */ jsx(Text, { color: "blue", underline: true, children: "https://github.com/masumrpg" })
160
+ ] })
161
+ ] })
162
+ ]
163
+ }
164
+ ) });
165
+ };
166
+
167
+ // src/ui/LiveProgress.tsx
168
+ import { Box as Box2, Text as Text2 } from "ink";
169
+ import Spinner from "ink-spinner";
170
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
171
+ var LiveProgress = ({ status, isCompiling }) => {
172
+ if (!isCompiling) return null;
173
+ return /* @__PURE__ */ jsxs2(Box2, { marginY: 1, alignItems: "center", children: [
174
+ /* @__PURE__ */ jsx2(Text2, { color: "cyan", children: /* @__PURE__ */ jsx2(Spinner, { type: "dots" }) }),
175
+ /* @__PURE__ */ jsxs2(Box2, { marginLeft: 1, children: [
176
+ /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "[COMPILING] " }),
177
+ /* @__PURE__ */ jsx2(Text2, { color: "white", children: status })
178
+ ] })
179
+ ] });
180
+ };
181
+
182
+ // src/ui/SummaryTable.tsx
183
+ import * as path2 from "path";
184
+ import { Box as Box3, Text as Text3 } from "ink";
185
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
186
+ function formatBytes(bytes) {
187
+ if (bytes === 0) return "0 B";
188
+ const k = 1024;
189
+ const sizes = ["B", "KB", "MB", "GB"];
190
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
191
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
192
+ }
193
+ function formatDisplayPath2(p) {
194
+ if (!p) return "";
195
+ try {
196
+ const rel = path2.relative(process.cwd(), p);
197
+ return rel && !rel.startsWith("..") && rel.length < p.length ? `./${rel}` : p;
198
+ } catch {
199
+ return p;
200
+ }
201
+ }
202
+ var SummaryTable = ({ result }) => {
203
+ if (!result) return null;
204
+ const hasErrors = result.errors.length > 0;
205
+ return /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", marginY: 1, children: /* @__PURE__ */ jsxs3(
206
+ Box3,
207
+ {
208
+ borderStyle: "round",
209
+ borderColor: hasErrors ? "red" : "green",
210
+ paddingX: 2,
211
+ paddingY: 1,
212
+ flexDirection: "column",
213
+ children: [
214
+ /* @__PURE__ */ jsxs3(Box3, { justifyContent: "space-between", alignItems: "center", children: [
215
+ /* @__PURE__ */ jsx3(Box3, { children: /* @__PURE__ */ jsx3(Text3, { bold: true, color: hasErrors ? "red" : "green", children: hasErrors ? "[COMPILATION FAILED]" : "[COMPILATION SUCCESSFUL]" }) }),
216
+ /* @__PURE__ */ jsxs3(Box3, { children: [
217
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Duration: " }),
218
+ /* @__PURE__ */ jsxs3(Text3, { color: "yellow", bold: true, children: [
219
+ result.durationMs,
220
+ "ms"
221
+ ] })
222
+ ] })
223
+ ] }),
224
+ result.files.length > 0 && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
225
+ /* @__PURE__ */ jsxs3(Text3, { bold: true, color: "white", children: [
226
+ "Generated Documents (",
227
+ result.files.length,
228
+ "):"
229
+ ] }),
230
+ result.files.map((f, i) => {
231
+ const formatColor = f.format === "docx" ? "blue" : f.format === "pdf" ? "red" : f.format === "html" ? "yellow" : f.format === "png" ? "magenta" : "green";
232
+ const paddedBadge = `[${f.format.toUpperCase()}]`.padEnd(7, " ");
233
+ return /* @__PURE__ */ jsxs3(Box3, { marginTop: 0, flexDirection: "column", marginLeft: 1, children: [
234
+ /* @__PURE__ */ jsxs3(Box3, { justifyContent: "space-between", alignItems: "center", children: [
235
+ /* @__PURE__ */ jsxs3(Box3, { children: [
236
+ /* @__PURE__ */ jsx3(Text3, { color: formatColor, bold: true, children: paddedBadge }),
237
+ /* @__PURE__ */ jsx3(Text3, { color: "white", bold: true, children: f.fileName })
238
+ ] }),
239
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: formatBytes(f.sizeBytes) })
240
+ ] }),
241
+ /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
242
+ "\u2514\u2500 ",
243
+ formatDisplayPath2(f.filePath)
244
+ ] }) })
245
+ ] }, i);
246
+ })
247
+ ] }),
248
+ hasErrors && /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, flexDirection: "column", children: [
249
+ /* @__PURE__ */ jsx3(Text3, { bold: true, color: "red", children: "Errors:" }),
250
+ result.errors.map((err, i) => /* @__PURE__ */ jsx3(Box3, { marginLeft: 2, children: /* @__PURE__ */ jsxs3(Text3, { color: "red", children: [
251
+ "- ",
252
+ err
253
+ ] }) }, i))
254
+ ] }),
255
+ /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, justifyContent: "space-between", alignItems: "center", children: [
256
+ /* @__PURE__ */ jsxs3(Box3, { children: [
257
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "Author: " }),
258
+ /* @__PURE__ */ jsx3(Text3, { color: "cyan", bold: true, children: "Ma'sum" }),
259
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: " (@masumrpg)" })
260
+ ] }),
261
+ /* @__PURE__ */ jsxs3(Box3, { children: [
262
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", children: "GitHub: " }),
263
+ /* @__PURE__ */ jsx3(Text3, { color: "blue", underline: true, children: "https://github.com/masumrpg" })
264
+ ] })
265
+ ] })
266
+ ]
267
+ }
268
+ ) });
269
+ };
270
+
271
+ // src/core/engine.ts
272
+ import * as fs2 from "fs";
273
+ import * as path4 from "path";
274
+
275
+ // src/core/text/textBuilder.ts
276
+ var LINE_WIDTH = 80;
277
+ var HR_DOUBLE = "=".repeat(LINE_WIDTH);
278
+ var HR_SINGLE = "-".repeat(LINE_WIDTH);
279
+ function centerText(text, width = LINE_WIDTH) {
280
+ if (text.length >= width) return text;
281
+ const leftPad = Math.floor((width - text.length) / 2);
282
+ return " ".repeat(leftPad) + text;
283
+ }
284
+ function renderInlinesToText(spans = [], meta = {}) {
285
+ let result = "";
286
+ for (const span of spans) {
287
+ switch (span.type) {
288
+ case "text": {
289
+ const content = replaceDocumentTokens(span.content, meta);
290
+ result += content;
291
+ break;
292
+ }
293
+ case "bold": {
294
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
295
+ result += `**${inner}**`;
296
+ break;
297
+ }
298
+ case "italic": {
299
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
300
+ result += `*${inner}*`;
301
+ break;
302
+ }
303
+ case "code": {
304
+ result += `\`${span.content}\``;
305
+ break;
306
+ }
307
+ case "link": {
308
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
309
+ result += span.url && span.url !== inner ? `${inner} (${span.url})` : inner;
310
+ break;
311
+ }
312
+ case "image": {
313
+ result += `[Image: ${span.alt || "image"} (${span.url || ""})]`;
314
+ break;
315
+ }
316
+ case "strikethrough": {
317
+ const inner = span.children ? renderInlinesToText(span.children, meta) : replaceDocumentTokens(span.content, meta);
318
+ result += `~${inner}~`;
319
+ break;
320
+ }
321
+ case "mathInline": {
322
+ result += `$${span.content}$`;
323
+ break;
324
+ }
325
+ case "footnoteRef": {
326
+ const id = span.footnoteId || span.content;
327
+ result += `[${id}]`;
328
+ break;
329
+ }
330
+ case "htmlInline": {
331
+ result += span.content.replace(/<[^>]+>/g, "");
332
+ break;
333
+ }
334
+ default: {
335
+ result += span.content || "";
336
+ break;
337
+ }
338
+ }
339
+ }
340
+ return result;
341
+ }
342
+ async function buildTextDocument(doc, config = {}, _baseDir = process.cwd()) {
343
+ const resolved = resolveDocumentConfig(doc.metadata, config);
344
+ const meta = {
345
+ ...doc.metadata,
346
+ ...config.metadata,
347
+ title: resolved.title,
348
+ subtitle: resolved.subtitle,
349
+ author: resolved.author,
350
+ company: resolved.company,
351
+ version: resolved.version,
352
+ date: resolved.date
353
+ };
354
+ const nodes = JSON.parse(JSON.stringify(doc.nodes));
355
+ const tocEntries = JSON.parse(JSON.stringify(doc.tocEntries));
356
+ if (resolved.numberHeadings && resolved.numberHeadings.enabled) {
357
+ applyHeadingNumbering(nodes, tocEntries, resolved.numberHeadings);
358
+ }
359
+ const lines = [];
360
+ if (resolved.coverPage && resolved.coverPage.enabled) {
361
+ const cover = resolved.coverPage;
362
+ const coverTitle = (cover.title || meta.title || "DOCUMENT").toUpperCase();
363
+ const coverSubtitle = cover.subtitle || meta.subtitle || "";
364
+ const coverAuthor = cover.author || meta.author || "";
365
+ const coverCompany = cover.company || meta.company || "";
366
+ const coverDate = cover.date || meta.date || "";
367
+ const coverVersion = cover.version || meta.version || "";
368
+ const coverBadge = cover.badge || "";
369
+ const coverFooter = cover.footerText || "";
370
+ lines.push(HR_DOUBLE);
371
+ lines.push(centerText(coverTitle));
372
+ if (coverSubtitle) {
373
+ lines.push(centerText(coverSubtitle));
374
+ }
375
+ lines.push(HR_DOUBLE);
376
+ if (coverBadge) {
377
+ lines.push(`[${coverBadge}]`);
378
+ lines.push("");
379
+ }
380
+ if (coverAuthor) lines.push(`Author: ${coverAuthor}`);
381
+ if (coverCompany) lines.push(`Company: ${coverCompany}`);
382
+ if (coverDate) lines.push(`Date: ${coverDate}`);
383
+ if (coverVersion) lines.push(`Version: ${coverVersion}`);
384
+ if (coverFooter) lines.push(`Notice: ${coverFooter}`);
385
+ lines.push(HR_DOUBLE);
386
+ lines.push("");
387
+ lines.push("");
388
+ }
389
+ if (resolved.toc) {
390
+ const headings = nodes.filter(
391
+ (n) => n.type === "heading" && (n.level || 1) <= 3
392
+ );
393
+ if (headings.length > 0) {
394
+ const tocTitle = "TABLE OF CONTENTS";
395
+ lines.push(HR_DOUBLE);
396
+ lines.push(centerText(tocTitle));
397
+ lines.push(HR_DOUBLE);
398
+ for (const h of headings) {
399
+ const level = h.level || 1;
400
+ const indent = " ".repeat(level - 1);
401
+ const headingText = renderInlinesToText(h.inlines, meta);
402
+ const prefix = level === 1 ? "* " : "- ";
403
+ lines.push(`${indent}${prefix}${headingText}`);
404
+ }
405
+ lines.push(HR_DOUBLE);
406
+ lines.push("");
407
+ lines.push("");
408
+ }
409
+ }
410
+ function renderNode(node, listLevel = 0) {
411
+ var _a;
412
+ switch (node.type) {
413
+ case "heading": {
414
+ const level = node.level || 1;
415
+ const text = renderInlinesToText(node.inlines, meta);
416
+ lines.push("");
417
+ if (level === 1) {
418
+ lines.push(HR_DOUBLE);
419
+ lines.push(text.toUpperCase());
420
+ lines.push(HR_DOUBLE);
421
+ } else if (level === 2) {
422
+ lines.push(HR_SINGLE);
423
+ lines.push(text);
424
+ lines.push(HR_SINGLE);
425
+ } else {
426
+ const hashes = "#".repeat(level);
427
+ lines.push(`${hashes} ${text}`);
428
+ }
429
+ lines.push("");
430
+ break;
431
+ }
432
+ case "paragraph": {
433
+ const text = renderInlinesToText(node.inlines, meta);
434
+ if (text.trim()) {
435
+ lines.push(text);
436
+ lines.push("");
437
+ }
438
+ break;
439
+ }
440
+ case "callout": {
441
+ const calloutType = (node.calloutType || "NOTE").toUpperCase();
442
+ lines.push(`| [${calloutType}]`);
443
+ if (node.inlines && node.inlines.length > 0) {
444
+ const text = renderInlinesToText(node.inlines, meta);
445
+ text.split("\n").forEach((l) => lines.push(`| ${l}`));
446
+ }
447
+ lines.push("");
448
+ break;
449
+ }
450
+ case "blockquote": {
451
+ if (node.inlines && node.inlines.length > 0) {
452
+ const text = renderInlinesToText(node.inlines, meta);
453
+ text.split("\n").forEach((l) => lines.push(`> ${l}`));
454
+ lines.push("");
455
+ }
456
+ break;
457
+ }
458
+ case "codeBlock": {
459
+ const lang = node.language ? `[Language: ${node.language}]` : "[Code]";
460
+ lines.push(HR_SINGLE);
461
+ lines.push(lang);
462
+ lines.push(HR_SINGLE);
463
+ const codeText = node.text || "";
464
+ lines.push(codeText);
465
+ lines.push(HR_SINGLE);
466
+ lines.push("");
467
+ break;
468
+ }
469
+ case "list": {
470
+ if (node.children) {
471
+ let itemIndex = 1;
472
+ for (const item of node.children) {
473
+ const indent = " ".repeat(listLevel);
474
+ let prefix = node.ordered ? `${itemIndex}. ` : "* ";
475
+ itemIndex++;
476
+ if (item.checked !== void 0) {
477
+ prefix = item.checked ? "[x] " : "[ ] ";
478
+ }
479
+ if (item.inlines && item.inlines.length > 0) {
480
+ const text = renderInlinesToText(item.inlines, meta);
481
+ lines.push(`${indent}${prefix}${text}`);
482
+ }
483
+ if (item.children && item.children.length > 0) {
484
+ for (const subChild of item.children) {
485
+ if (subChild.type === "list") {
486
+ renderNode(subChild, listLevel + 1);
487
+ } else if (subChild.type === "paragraph") {
488
+ const text = renderInlinesToText(subChild.inlines, meta);
489
+ lines.push(`${indent} ${text}`);
490
+ } else {
491
+ renderNode(subChild, listLevel + 1);
492
+ }
493
+ }
494
+ }
495
+ }
496
+ lines.push("");
497
+ }
498
+ break;
499
+ }
500
+ case "table": {
501
+ const rows = node.children || [];
502
+ if (rows.length === 0) break;
503
+ const rowCells = [];
504
+ let maxCols = 0;
505
+ for (const row of rows) {
506
+ const cells = row.children || [];
507
+ const texts = cells.map((c) => renderInlinesToText(c.inlines, meta));
508
+ const isHeader = cells.some((c) => c.isHeader);
509
+ maxCols = Math.max(maxCols, texts.length);
510
+ rowCells.push({ isHeader, texts });
511
+ }
512
+ if (maxCols === 0) break;
513
+ const colWidths = new Array(maxCols).fill(3);
514
+ for (const r of rowCells) {
515
+ for (let col = 0; col < maxCols; col++) {
516
+ const cellText = r.texts[col] || "";
517
+ colWidths[col] = Math.max(colWidths[col] ?? 3, cellText.length);
518
+ }
519
+ }
520
+ const separatorLine = "+" + colWidths.map((w) => "-".repeat(w + 2)).join("+") + "+";
521
+ const formatRow = (texts) => {
522
+ const paddedCells = colWidths.map((width, colIdx) => {
523
+ const text = texts[colIdx] || "";
524
+ return " " + text.padEnd(width, " ") + " ";
525
+ });
526
+ return "|" + paddedCells.join("|") + "|";
527
+ };
528
+ lines.push(separatorLine);
529
+ for (const r of rowCells) {
530
+ lines.push(formatRow(r.texts));
531
+ if (r.isHeader) {
532
+ lines.push(separatorLine);
533
+ }
534
+ }
535
+ if (!((_a = rowCells[rowCells.length - 1]) == null ? void 0 : _a.isHeader)) {
536
+ lines.push(separatorLine);
537
+ }
538
+ lines.push("");
539
+ break;
540
+ }
541
+ case "mathBlock": {
542
+ lines.push("[Equation]");
543
+ lines.push(` $$${node.text || ""} $$`);
544
+ lines.push("");
545
+ break;
546
+ }
547
+ case "mermaid": {
548
+ lines.push("[Diagram: Mermaid]");
549
+ lines.push(node.text || "");
550
+ lines.push("");
551
+ break;
552
+ }
553
+ case "thematicBreak": {
554
+ lines.push(HR_SINGLE);
555
+ lines.push("");
556
+ break;
557
+ }
558
+ case "columns": {
559
+ if (node.children) {
560
+ for (const col of node.children) {
561
+ if (col.children) {
562
+ for (const child of col.children) {
563
+ renderNode(child, listLevel);
564
+ }
565
+ }
566
+ }
567
+ }
568
+ break;
569
+ }
570
+ default: {
571
+ if (node.children && node.children.length > 0) {
572
+ for (const child of node.children) {
573
+ renderNode(child, listLevel);
574
+ }
575
+ }
576
+ break;
577
+ }
578
+ }
579
+ }
580
+ for (const node of nodes) {
581
+ renderNode(node);
582
+ }
583
+ if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
584
+ lines.push(HR_SINGLE);
585
+ lines.push("FOOTNOTES");
586
+ lines.push(HR_SINGLE);
587
+ for (const fn of doc.footnoteDefs) {
588
+ const text = renderInlinesToText(fn.inlines, meta);
589
+ lines.push(`[${fn.id}] ${text}`);
590
+ }
591
+ lines.push("");
592
+ }
593
+ if (resolved.signatures && resolved.signatures.items && resolved.signatures.items.length > 0) {
594
+ lines.push(HR_SINGLE);
595
+ lines.push("SIGNATURES & APPROVALS");
596
+ lines.push(HR_SINGLE);
597
+ lines.push("");
598
+ for (const item of resolved.signatures.items) {
599
+ const title = item.title || "Signatory";
600
+ const name = item.name ? replaceDocumentTokens(item.name, meta) : "";
601
+ const role = item.role ? replaceDocumentTokens(item.role, meta) : "";
602
+ const date = item.date ? replaceDocumentTokens(item.date, meta) : "";
603
+ lines.push(`[${title}]`);
604
+ lines.push("____________________________________");
605
+ if (name) lines.push(`Name: ${name}`);
606
+ if (role) lines.push(`Role: ${role}`);
607
+ if (date) lines.push(`Date: ${date}`);
608
+ lines.push("");
609
+ }
610
+ }
611
+ if (resolved.backCover && resolved.backCover.enabled) {
612
+ const back = resolved.backCover;
613
+ const backTitle = (back.title || "THANK YOU").toUpperCase();
614
+ const backSubtitle = back.subtitle || "";
615
+ const backCompany = back.company ? replaceDocumentTokens(back.company, meta) : "";
616
+ const backAddress = back.address ? replaceDocumentTokens(back.address, meta) : "";
617
+ const backEmail = back.email ? replaceDocumentTokens(back.email, meta) : "";
618
+ const backPhone = back.phone ? replaceDocumentTokens(back.phone, meta) : "";
619
+ const backWebsite = back.website ? replaceDocumentTokens(back.website, meta) : "";
620
+ const backCopyright = back.copyright ? replaceDocumentTokens(back.copyright, meta) : "";
621
+ lines.push(HR_DOUBLE);
622
+ lines.push(centerText(backTitle));
623
+ if (backSubtitle) {
624
+ lines.push(centerText(backSubtitle));
625
+ }
626
+ lines.push(HR_DOUBLE);
627
+ if (backCompany) lines.push(`Company: ${backCompany}`);
628
+ if (backAddress) lines.push(`Address: ${backAddress}`);
629
+ if (backEmail) lines.push(`Email: ${backEmail}`);
630
+ if (backPhone) lines.push(`Phone: ${backPhone}`);
631
+ if (backWebsite) lines.push(`Website: ${backWebsite}`);
632
+ if (back.social && back.social.github) {
633
+ lines.push(`GitHub: ${back.social.github}`);
634
+ }
635
+ if (backCopyright) {
636
+ lines.push("");
637
+ lines.push(backCopyright);
638
+ }
639
+ lines.push(HR_DOUBLE);
640
+ lines.push("");
641
+ }
642
+ return lines.join("\n").trim() + "\n";
643
+ }
644
+
645
+ // src/core/png/pngBuilder.ts
646
+ import * as fs from "fs";
647
+ import * as path3 from "path";
648
+ import * as os from "os";
649
+ import { pathToFileURL } from "url";
650
+ import { spawnSync } from "child_process";
651
+ async function buildPngDocument(doc, config, baseDir) {
652
+ const htmlContent = await buildHtmlDocument(doc, config, baseDir);
653
+ const chromePath = findChromeExecutable();
654
+ if (!chromePath) {
655
+ throw new Error(
656
+ "Headless Chrome / Chromium / Edge executable not found for PNG document export. Please install Google Chrome, Chromium, or Microsoft Edge, or set CHROME_PATH environment variable."
657
+ );
658
+ }
659
+ const tmpHtml = path3.join(
660
+ os.tmpdir(),
661
+ `markforge-png-${Date.now()}-${Math.random().toString(36).slice(2)}.html`
662
+ );
663
+ const tmpPng = path3.join(
664
+ os.tmpdir(),
665
+ `markforge-png-${Date.now()}-${Math.random().toString(36).slice(2)}.png`
666
+ );
667
+ try {
668
+ fs.writeFileSync(tmpHtml, htmlContent, "utf-8");
669
+ const fileUrl = pathToFileURL(tmpHtml).href;
670
+ const spawnResult = spawnSync(
671
+ chromePath,
672
+ [
673
+ "--headless=new",
674
+ "--disable-gpu",
675
+ "--no-sandbox",
676
+ "--disable-setuid-sandbox",
677
+ "--hide-scrollbars",
678
+ "--force-device-scale-factor=2",
679
+ "--window-size=1200,1600",
680
+ `--screenshot=${tmpPng}`,
681
+ fileUrl
682
+ ],
683
+ { timeout: 3e4, windowsHide: true }
684
+ );
685
+ if (spawnResult.error) {
686
+ throw new Error(`Failed to execute Chromium for PNG export: ${spawnResult.error.message}`);
687
+ }
688
+ if (!fs.existsSync(tmpPng) || fs.statSync(tmpPng).size === 0) {
689
+ throw new Error("Chromium PNG export failed to generate output screenshot file.");
690
+ }
691
+ return fs.readFileSync(tmpPng);
692
+ } finally {
693
+ try {
694
+ if (fs.existsSync(tmpHtml)) fs.unlinkSync(tmpHtml);
695
+ if (fs.existsSync(tmpPng)) fs.unlinkSync(tmpPng);
696
+ } catch {
697
+ }
698
+ }
699
+ }
700
+
701
+ // src/core/engine.ts
702
+ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgress) {
703
+ const startTime = Date.now();
704
+ const config = { ...DEFAULT_CONFIG, ...userConfig };
705
+ let rawMarkdown = "";
706
+ let baseDir = process.cwd();
707
+ let inputFileName = "document.md";
708
+ let isFilePath = false;
709
+ if (fs2.existsSync(inputFilePathOrContent)) {
710
+ isFilePath = true;
711
+ rawMarkdown = fs2.readFileSync(inputFilePathOrContent, "utf-8");
712
+ baseDir = path4.dirname(path4.resolve(inputFilePathOrContent));
713
+ inputFileName = path4.basename(inputFilePathOrContent);
714
+ } else {
715
+ rawMarkdown = inputFilePathOrContent;
716
+ }
717
+ onProgress == null ? void 0 : onProgress(`Parsing markdown AST: ${inputFileName}...`);
718
+ const parsedDoc = parseMarkdownDocument(rawMarkdown);
719
+ const baseName = inputFileName.replace(/\.(md|mdx|markdown)$/i, "");
720
+ const outputDir = config.outputDir ? path4.isAbsolute(config.outputDir) ? config.outputDir : path4.resolve(process.cwd(), config.outputDir) : baseDir;
721
+ if (!fs2.existsSync(outputDir)) {
722
+ fs2.mkdirSync(outputDir, { recursive: true });
723
+ }
724
+ const formats = Array.isArray(config.to) ? config.to : [config.to || "docx", "pdf"];
725
+ const generatedFiles = [];
726
+ const errors = [];
727
+ for (const fmt of formats) {
728
+ try {
729
+ if (fmt === "docx") {
730
+ onProgress == null ? void 0 : onProgress(`Generating DOCX document: ${baseName}.docx...`);
731
+ const docxBuffer = await buildDocxDocument(parsedDoc, config, baseDir);
732
+ const docxPath = path4.join(outputDir, `${baseName}.docx`);
733
+ fs2.writeFileSync(docxPath, docxBuffer);
734
+ generatedFiles.push({
735
+ format: "docx",
736
+ filePath: docxPath,
737
+ fileName: `${baseName}.docx`,
738
+ sizeBytes: docxBuffer.length
739
+ });
740
+ } else if (fmt === "html") {
741
+ onProgress == null ? void 0 : onProgress(`Generating HTML document: ${baseName}.html...`);
742
+ const htmlString = await buildHtmlDocument(parsedDoc, config, baseDir);
743
+ const htmlPath = path4.join(outputDir, `${baseName}.html`);
744
+ fs2.writeFileSync(htmlPath, htmlString, "utf-8");
745
+ generatedFiles.push({
746
+ format: "html",
747
+ filePath: htmlPath,
748
+ fileName: `${baseName}.html`,
749
+ sizeBytes: Buffer.byteLength(htmlString, "utf-8")
750
+ });
751
+ } else if (fmt === "pdf") {
752
+ onProgress == null ? void 0 : onProgress(`Generating PDF document: ${baseName}.pdf...`);
753
+ const pdfBuffer = await buildPdfDocument(parsedDoc, config, baseDir);
754
+ const pdfPath = path4.join(outputDir, `${baseName}.pdf`);
755
+ fs2.writeFileSync(pdfPath, pdfBuffer);
756
+ generatedFiles.push({
757
+ format: "pdf",
758
+ filePath: pdfPath,
759
+ fileName: `${baseName}.pdf`,
760
+ sizeBytes: pdfBuffer.length
761
+ });
762
+ } else if (fmt === "txt") {
763
+ onProgress == null ? void 0 : onProgress(`Generating Plain Text document: ${baseName}.txt...`);
764
+ const textContent = await buildTextDocument(parsedDoc, config, baseDir);
765
+ const textPath = path4.join(outputDir, `${baseName}.txt`);
766
+ fs2.writeFileSync(textPath, textContent, "utf-8");
767
+ generatedFiles.push({
768
+ format: "txt",
769
+ filePath: textPath,
770
+ fileName: `${baseName}.txt`,
771
+ sizeBytes: Buffer.byteLength(textContent, "utf-8")
772
+ });
773
+ } else if (fmt === "png") {
774
+ onProgress == null ? void 0 : onProgress(`Generating PNG document: ${baseName}.png...`);
775
+ const pngBuffer = await buildPngDocument(parsedDoc, config, baseDir);
776
+ const pngPath = path4.join(outputDir, `${baseName}.png`);
777
+ fs2.writeFileSync(pngPath, pngBuffer);
778
+ generatedFiles.push({
779
+ format: "png",
780
+ filePath: pngPath,
781
+ fileName: `${baseName}.png`,
782
+ sizeBytes: pngBuffer.length
783
+ });
784
+ }
785
+ } catch (err) {
786
+ const errMsg = err instanceof Error ? err.message : String(err);
787
+ errors.push(`Failed to generate ${fmt}: ${errMsg}`);
788
+ }
789
+ }
790
+ const durationMs = Date.now() - startTime;
791
+ return {
792
+ inputFile: isFilePath ? inputFilePathOrContent : "inline-string",
793
+ durationMs,
794
+ metadata: parsedDoc.metadata,
795
+ files: generatedFiles,
796
+ errors
797
+ };
798
+ }
799
+
800
+ // src/ui/App.tsx
801
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
802
+ var App = ({ inputFile, config, configPath, onComplete }) => {
803
+ const [status, setStatus] = useState("Initializing...");
804
+ const [isCompiling, setIsCompiling] = useState(true);
805
+ const [result, setResult] = useState(null);
806
+ useEffect(() => {
807
+ async function run() {
808
+ try {
809
+ const res = await compileMarkdown(inputFile, config, (msg) => setStatus(msg));
810
+ setResult(res);
811
+ setIsCompiling(false);
812
+ onComplete == null ? void 0 : onComplete(res);
813
+ } catch (err) {
814
+ const errMsg = err instanceof Error ? err.message : String(err);
815
+ const errResult = {
816
+ inputFile,
817
+ durationMs: 0,
818
+ metadata: {},
819
+ files: [],
820
+ errors: [errMsg]
821
+ };
822
+ setResult(errResult);
823
+ setIsCompiling(false);
824
+ onComplete == null ? void 0 : onComplete(errResult);
825
+ }
826
+ }
827
+ run();
828
+ }, [inputFile, config, onComplete]);
829
+ const targetFormats = Array.isArray(config.to) ? config.to : config.to ? [String(config.to)] : ["docx", "pdf"];
830
+ return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
831
+ /* @__PURE__ */ jsx4(
832
+ Header,
833
+ {
834
+ inputFile,
835
+ configPath,
836
+ config,
837
+ theme: config.theme,
838
+ targetFormats
839
+ }
840
+ ),
841
+ /* @__PURE__ */ jsx4(LiveProgress, { status, isCompiling }),
842
+ /* @__PURE__ */ jsx4(SummaryTable, { result })
843
+ ] });
844
+ };
845
+ export {
846
+ App
847
+ };