@masumdev/markforge 0.2.2 → 0.2.4

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