@masumdev/markforge 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -141
- package/dist/App-3QDKBKHG.mjs +328 -0
- package/dist/{chunk-IYGPJIK5.mjs → chunk-BPHDY6VB.mjs} +6 -0
- package/dist/{App-AM2CWXHJ.mjs → chunk-CRV7R2BG.mjs} +1722 -657
- package/dist/{chunk-HKB4CSPZ.mjs → chunk-TNWZ4MFS.mjs} +1 -1
- package/dist/cli.mjs +29 -7
- package/dist/index.d.mts +506 -123
- package/dist/index.d.ts +506 -123
- package/dist/index.js +2481 -208
- package/dist/index.mjs +2470 -208
- package/dist/{loadConfig-PD6ENMAM.mjs → loadConfig-PGJKPE6G.mjs} +1 -1
- package/dist/previewServer-XAUOBNBZ.mjs +893 -0
- package/package.json +5 -1
- package/schema.json +142 -0
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
39
39
|
var src_exports = {};
|
|
40
40
|
__export(src_exports, {
|
|
41
41
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
42
|
+
KATEX_INLINE_CSS: () => KATEX_INLINE_CSS,
|
|
42
43
|
MARKFORGE_VERSION: () => MARKFORGE_VERSION,
|
|
43
44
|
Orientation: () => Orientation,
|
|
44
45
|
OutputFormat: () => OutputFormat,
|
|
@@ -67,19 +68,29 @@ __export(src_exports, {
|
|
|
67
68
|
inlineHtmlImages: () => inlineHtmlImages,
|
|
68
69
|
loadConfig: () => loadConfig,
|
|
69
70
|
markforge: () => compileMarkdown,
|
|
71
|
+
normalizeBackCover: () => normalizeBackCover,
|
|
72
|
+
normalizeCoverPage: () => normalizeCoverPage,
|
|
70
73
|
normalizeHeaderFooter: () => normalizeHeaderFooter,
|
|
71
74
|
normalizeHeaderFooterSlot: () => normalizeHeaderFooterSlot,
|
|
75
|
+
normalizeNumberHeadings: () => normalizeNumberHeadings,
|
|
76
|
+
normalizeSecurity: () => normalizeSecurity,
|
|
72
77
|
normalizeSignatures: () => normalizeSignatures,
|
|
73
78
|
normalizeWatermark: () => normalizeWatermark,
|
|
74
79
|
parseInlineSpans: () => parseInlineSpans,
|
|
75
80
|
parseMarginToTwip: () => parseMarginToTwip2,
|
|
81
|
+
parseMarkdown: () => parseMarkdownDocument,
|
|
76
82
|
parseMarkdownDocument: () => parseMarkdownDocument,
|
|
83
|
+
renderBackCoverHtml: () => renderBackCoverHtml,
|
|
84
|
+
renderCoverPageHtml: () => renderCoverPageHtml,
|
|
77
85
|
renderInlinesToHtml: () => renderInlinesToHtml,
|
|
86
|
+
renderMathToHtml: () => renderMathToHtml,
|
|
78
87
|
renderMermaidToPng: () => renderMermaidToPng,
|
|
88
|
+
renderNodesToHtml: () => renderNodesToHtml,
|
|
79
89
|
replaceDocumentTokens: () => replaceDocumentTokens,
|
|
80
90
|
resolveDocumentConfig: () => resolveDocumentConfig,
|
|
81
91
|
resolveImage: () => resolveImage,
|
|
82
92
|
slugify: () => slugify,
|
|
93
|
+
startPreviewServer: () => startPreviewServer,
|
|
83
94
|
tokenizeCodeLine: () => tokenizeCodeLine
|
|
84
95
|
});
|
|
85
96
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -120,6 +131,26 @@ function parseInlineSpans(text) {
|
|
|
120
131
|
remaining = remaining.slice(imgMatch[0].length);
|
|
121
132
|
continue;
|
|
122
133
|
}
|
|
134
|
+
const fnMatch = remaining.match(/^\[\^([\w-]+)\]/);
|
|
135
|
+
if (fnMatch) {
|
|
136
|
+
const fnId = fnMatch[1];
|
|
137
|
+
spans.push({
|
|
138
|
+
type: "footnoteRef",
|
|
139
|
+
content: fnId,
|
|
140
|
+
footnoteId: fnId
|
|
141
|
+
});
|
|
142
|
+
remaining = remaining.slice(fnMatch[0].length);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const mathMatch = remaining.match(/^\$([^$\n]+?)\$/);
|
|
146
|
+
if (mathMatch && !mathMatch[1].startsWith("$")) {
|
|
147
|
+
spans.push({
|
|
148
|
+
type: "mathInline",
|
|
149
|
+
content: mathMatch[1]
|
|
150
|
+
});
|
|
151
|
+
remaining = remaining.slice(mathMatch[0].length);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
123
154
|
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/);
|
|
124
155
|
if (linkMatch) {
|
|
125
156
|
spans.push({
|
|
@@ -219,7 +250,7 @@ function parseInlineSpans(text) {
|
|
|
219
250
|
remaining = remaining.slice(fullTag.length);
|
|
220
251
|
continue;
|
|
221
252
|
}
|
|
222
|
-
const nextSpecial = remaining.search(/[\*\_\[
|
|
253
|
+
const nextSpecial = remaining.search(/[\*\_\[\!`~<\$]/);
|
|
223
254
|
if (nextSpecial === -1) {
|
|
224
255
|
spans.push({
|
|
225
256
|
type: "text",
|
|
@@ -245,6 +276,35 @@ function parseInlineSpans(text) {
|
|
|
245
276
|
function slugify(text) {
|
|
246
277
|
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
247
278
|
}
|
|
279
|
+
function applyHeadingNumbering(nodes, tocEntries, options) {
|
|
280
|
+
const depth = options.depth ?? 3;
|
|
281
|
+
const skipH1 = options.skipH1 ?? false;
|
|
282
|
+
const prefix = options.prefix ?? "";
|
|
283
|
+
const counters = [0, 0, 0, 0, 0, 0];
|
|
284
|
+
for (const node of nodes) {
|
|
285
|
+
if (node.type === "heading" && node.level) {
|
|
286
|
+
const lvl = node.level;
|
|
287
|
+
if (lvl > depth) continue;
|
|
288
|
+
if (lvl === 1 && skipH1) continue;
|
|
289
|
+
const idx = lvl - 1;
|
|
290
|
+
counters[idx]++;
|
|
291
|
+
for (let c = idx + 1; c < counters.length; c++) {
|
|
292
|
+
counters[c] = 0;
|
|
293
|
+
}
|
|
294
|
+
const startIdx = skipH1 ? 1 : 0;
|
|
295
|
+
const parts = counters.slice(startIdx, idx + 1).filter((n) => n > 0);
|
|
296
|
+
const numberStr = parts.join(".") + ".";
|
|
297
|
+
const fullPrefix = prefix ? `${prefix} ${numberStr} ` : `${numberStr} `;
|
|
298
|
+
const originalText = node.text || "";
|
|
299
|
+
node.text = fullPrefix + originalText;
|
|
300
|
+
node.inlines = parseInlineSpans(node.text);
|
|
301
|
+
const toc = tocEntries.find((t) => t.id === node.id);
|
|
302
|
+
if (toc) {
|
|
303
|
+
toc.text = node.text;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
248
308
|
function parseMarkdownDocument(rawMarkdown) {
|
|
249
309
|
const { data: frontmatter, content } = (0, import_gray_matter.default)(rawMarkdown);
|
|
250
310
|
const metadata = frontmatter || {};
|
|
@@ -256,6 +316,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
256
316
|
const lines = cleanContent.split(/\r?\n/);
|
|
257
317
|
const nodes = [];
|
|
258
318
|
const tocEntries = [];
|
|
319
|
+
const footnoteDefs = [];
|
|
259
320
|
let i = 0;
|
|
260
321
|
while (i < lines.length) {
|
|
261
322
|
const line = lines[i];
|
|
@@ -284,6 +345,29 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
284
345
|
i++;
|
|
285
346
|
continue;
|
|
286
347
|
}
|
|
348
|
+
if (line.trim().startsWith("$$")) {
|
|
349
|
+
const mathLines = [];
|
|
350
|
+
const singleLine = line.trim().match(/^\$\$(.+)\$\$$/);
|
|
351
|
+
if (singleLine) {
|
|
352
|
+
nodes.push({
|
|
353
|
+
type: "mathBlock",
|
|
354
|
+
text: singleLine[1].trim()
|
|
355
|
+
});
|
|
356
|
+
i++;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
i++;
|
|
360
|
+
while (i < lines.length && !lines[i].trim().startsWith("$$")) {
|
|
361
|
+
mathLines.push(lines[i]);
|
|
362
|
+
i++;
|
|
363
|
+
}
|
|
364
|
+
if (i < lines.length) i++;
|
|
365
|
+
nodes.push({
|
|
366
|
+
type: "mathBlock",
|
|
367
|
+
text: mathLines.join("\n").trim()
|
|
368
|
+
});
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
287
371
|
const codeBlockMatch = line.match(/^```(\w+)?/);
|
|
288
372
|
if (codeBlockMatch) {
|
|
289
373
|
const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
|
|
@@ -310,6 +394,100 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
310
394
|
}
|
|
311
395
|
continue;
|
|
312
396
|
}
|
|
397
|
+
const colsMatch = line.trim().match(/^:::columns(?:\s+\[?([\w\s=.-]+)\]?)?$/i);
|
|
398
|
+
if (colsMatch) {
|
|
399
|
+
const attrStr = colsMatch[1] || "";
|
|
400
|
+
let colsCount = 2;
|
|
401
|
+
let colGap = "1.5rem";
|
|
402
|
+
if (attrStr) {
|
|
403
|
+
const numMatch = attrStr.trim().match(/^(\d+)$/);
|
|
404
|
+
const cMatch = attrStr.match(/cols=(\d+)/i) || attrStr.match(/columns=(\d+)/i);
|
|
405
|
+
const gMatch = attrStr.match(/gap=([^\s]+)/i);
|
|
406
|
+
if (numMatch) colsCount = parseInt(numMatch[1], 10);
|
|
407
|
+
else if (cMatch) colsCount = parseInt(cMatch[1], 10);
|
|
408
|
+
if (gMatch) colGap = gMatch[1];
|
|
409
|
+
}
|
|
410
|
+
const columnNodes = [];
|
|
411
|
+
let currentColumnLines = [];
|
|
412
|
+
let inColBlock = false;
|
|
413
|
+
i++;
|
|
414
|
+
while (i < lines.length) {
|
|
415
|
+
const curLine = lines[i];
|
|
416
|
+
const trimmed = curLine.trim();
|
|
417
|
+
if (/^:::col(?:umn)?$/i.test(trimmed)) {
|
|
418
|
+
if (currentColumnLines.length > 0) {
|
|
419
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
420
|
+
columnNodes.push({
|
|
421
|
+
type: "column",
|
|
422
|
+
children: subDoc.nodes
|
|
423
|
+
});
|
|
424
|
+
currentColumnLines = [];
|
|
425
|
+
}
|
|
426
|
+
inColBlock = true;
|
|
427
|
+
i++;
|
|
428
|
+
} else if (trimmed === ":::") {
|
|
429
|
+
if (inColBlock) {
|
|
430
|
+
if (currentColumnLines.length > 0) {
|
|
431
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
432
|
+
columnNodes.push({
|
|
433
|
+
type: "column",
|
|
434
|
+
children: subDoc.nodes
|
|
435
|
+
});
|
|
436
|
+
currentColumnLines = [];
|
|
437
|
+
}
|
|
438
|
+
inColBlock = false;
|
|
439
|
+
i++;
|
|
440
|
+
} else {
|
|
441
|
+
if (currentColumnLines.length > 0) {
|
|
442
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
443
|
+
columnNodes.push({
|
|
444
|
+
type: "column",
|
|
445
|
+
children: subDoc.nodes
|
|
446
|
+
});
|
|
447
|
+
currentColumnLines = [];
|
|
448
|
+
}
|
|
449
|
+
i++;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
} else {
|
|
453
|
+
currentColumnLines.push(curLine);
|
|
454
|
+
i++;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (currentColumnLines.length > 0) {
|
|
458
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
459
|
+
columnNodes.push({
|
|
460
|
+
type: "column",
|
|
461
|
+
children: subDoc.nodes
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
nodes.push({
|
|
465
|
+
type: "columns",
|
|
466
|
+
columnsCount: columnNodes.length > 0 ? columnNodes.length : colsCount,
|
|
467
|
+
columnGap: colGap,
|
|
468
|
+
children: columnNodes
|
|
469
|
+
});
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const fnDefMatch = line.match(/^\[\^([\w-]+)\]:\s+(.+)$/);
|
|
473
|
+
if (fnDefMatch) {
|
|
474
|
+
const fnId = fnDefMatch[1];
|
|
475
|
+
const fnText = fnDefMatch[2].trim();
|
|
476
|
+
const inlines = parseInlineSpans(fnText);
|
|
477
|
+
footnoteDefs.push({
|
|
478
|
+
id: fnId,
|
|
479
|
+
text: fnText,
|
|
480
|
+
inlines
|
|
481
|
+
});
|
|
482
|
+
nodes.push({
|
|
483
|
+
type: "footnoteDef",
|
|
484
|
+
footnoteId: fnId,
|
|
485
|
+
text: fnText,
|
|
486
|
+
inlines
|
|
487
|
+
});
|
|
488
|
+
i++;
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
313
491
|
const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
|
|
314
492
|
if (calloutMatch) {
|
|
315
493
|
const calloutType = calloutMatch[1].toUpperCase();
|
|
@@ -433,7 +611,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
433
611
|
continue;
|
|
434
612
|
}
|
|
435
613
|
const paraLines = [];
|
|
436
|
-
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+/)) {
|
|
614
|
+
while (i < lines.length && lines[i].trim() && !lines[i].match(/^#{1,6}\s+/) && !lines[i].startsWith("```") && !lines[i].startsWith("$$") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !lines[i].match(/^:::columns/) && !lines[i].match(/^\[\^[\w-]+\]:\s+/) && !lines[i].match(/^(\s*)([-*+]|\d+\.)\s+/)) {
|
|
437
615
|
paraLines.push(lines[i]);
|
|
438
616
|
i++;
|
|
439
617
|
}
|
|
@@ -444,12 +622,19 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
444
622
|
inlines: parseInlineSpans(paraText)
|
|
445
623
|
});
|
|
446
624
|
}
|
|
625
|
+
if (metadata.numberHeadings) {
|
|
626
|
+
const opts = typeof metadata.numberHeadings === "boolean" ? { enabled: metadata.numberHeadings } : metadata.numberHeadings;
|
|
627
|
+
if (opts.enabled !== false) {
|
|
628
|
+
applyHeadingNumbering(nodes, tocEntries, opts);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
447
631
|
return {
|
|
448
632
|
metadata,
|
|
449
633
|
content: cleanContent,
|
|
450
634
|
nodes,
|
|
451
635
|
tocEntries,
|
|
452
|
-
inlinedStyles
|
|
636
|
+
inlinedStyles,
|
|
637
|
+
footnoteDefs
|
|
453
638
|
};
|
|
454
639
|
}
|
|
455
640
|
|
|
@@ -515,9 +700,14 @@ async function resolveImage(src, baseDir = process.cwd()) {
|
|
|
515
700
|
memoryImageCache.set(cacheKey, resolved2);
|
|
516
701
|
return resolved2;
|
|
517
702
|
}
|
|
518
|
-
|
|
703
|
+
let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
|
|
519
704
|
if (!fs.existsSync(localPath)) {
|
|
520
|
-
|
|
705
|
+
const cwdPath = path.resolve(process.cwd(), src);
|
|
706
|
+
if (fs.existsSync(cwdPath)) {
|
|
707
|
+
localPath = cwdPath;
|
|
708
|
+
} else {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
521
711
|
}
|
|
522
712
|
const buffer = fs.readFileSync(localPath);
|
|
523
713
|
const mimeType = getMimeType(localPath);
|
|
@@ -922,6 +1112,8 @@ var path4 = __toESM(require("path"));
|
|
|
922
1112
|
var os = __toESM(require("os"));
|
|
923
1113
|
var import_node_url2 = require("url");
|
|
924
1114
|
var import_node_child_process = require("child_process");
|
|
1115
|
+
var import_pdf_lib = require("pdf-lib");
|
|
1116
|
+
var import_pdf_encrypt = require("@pdfsmaller/pdf-encrypt");
|
|
925
1117
|
|
|
926
1118
|
// src/core/html/htmlBuilder.ts
|
|
927
1119
|
var fs3 = __toESM(require("fs"));
|
|
@@ -1164,6 +1356,12 @@ var DEFAULT_CONFIG = {
|
|
|
1164
1356
|
},
|
|
1165
1357
|
toc: false,
|
|
1166
1358
|
watermark: void 0,
|
|
1359
|
+
signatures: void 0,
|
|
1360
|
+
coverPage: void 0,
|
|
1361
|
+
backCover: void 0,
|
|
1362
|
+
numberHeadings: void 0,
|
|
1363
|
+
security: void 0,
|
|
1364
|
+
math: true,
|
|
1167
1365
|
embedImages: true,
|
|
1168
1366
|
metadata: void 0,
|
|
1169
1367
|
watch: false,
|
|
@@ -1320,7 +1518,8 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
|
|
|
1320
1518
|
return str;
|
|
1321
1519
|
}
|
|
1322
1520
|
function replaceDocumentTokens(template = "", meta) {
|
|
1323
|
-
|
|
1521
|
+
const currentYear = meta.year || (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1522
|
+
return template.replace(/\{title\}/gi, meta.title || "").replace(/\{subtitle\}/gi, meta.subtitle || "").replace(/\{author\}/gi, meta.author || "").replace(/\{version\}/gi, meta.version || "").replace(/\{date\}/gi, meta.date || "").replace(/\{year\}/gi, currentYear).replace(/\{company\}/gi, meta.company || "");
|
|
1324
1523
|
}
|
|
1325
1524
|
function normalizeWatermark(rawWatermark) {
|
|
1326
1525
|
if (!rawWatermark) {
|
|
@@ -1457,6 +1656,129 @@ function normalizeSignatures(raw, meta = {}) {
|
|
|
1457
1656
|
spacingBeforeTwip
|
|
1458
1657
|
};
|
|
1459
1658
|
}
|
|
1659
|
+
function normalizeCoverPage(rawCover, tokenCtx = {}) {
|
|
1660
|
+
if (!rawCover) return void 0;
|
|
1661
|
+
const cfg = typeof rawCover === "object" ? rawCover : {};
|
|
1662
|
+
if (cfg.enabled === false) return void 0;
|
|
1663
|
+
const preset = cfg.preset || "modern";
|
|
1664
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title || "Document Title";
|
|
1665
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle;
|
|
1666
|
+
const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author;
|
|
1667
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
|
|
1668
|
+
const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version;
|
|
1669
|
+
let dateStr;
|
|
1670
|
+
if (typeof cfg.date === "string") {
|
|
1671
|
+
dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
|
|
1672
|
+
} else if (cfg.date === true) {
|
|
1673
|
+
dateStr = tokenCtx.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1674
|
+
} else {
|
|
1675
|
+
dateStr = tokenCtx.date;
|
|
1676
|
+
}
|
|
1677
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1678
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1679
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1680
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1681
|
+
const logoWidth = cfg.logoWidth;
|
|
1682
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
|
|
1683
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1684
|
+
const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
|
|
1685
|
+
return {
|
|
1686
|
+
enabled: true,
|
|
1687
|
+
preset,
|
|
1688
|
+
title,
|
|
1689
|
+
subtitle,
|
|
1690
|
+
author,
|
|
1691
|
+
company,
|
|
1692
|
+
version,
|
|
1693
|
+
date: dateStr,
|
|
1694
|
+
badge,
|
|
1695
|
+
badgeColor,
|
|
1696
|
+
badgeTextColor,
|
|
1697
|
+
logo,
|
|
1698
|
+
logoWidth,
|
|
1699
|
+
bgGradient,
|
|
1700
|
+
textColor,
|
|
1701
|
+
footerText
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
function normalizeBackCover(rawBack, tokenCtx = {}) {
|
|
1705
|
+
if (!rawBack) return void 0;
|
|
1706
|
+
const cfg = typeof rawBack === "object" ? rawBack : {};
|
|
1707
|
+
if (cfg.enabled === false) return void 0;
|
|
1708
|
+
const preset = cfg.preset || "modern";
|
|
1709
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
|
|
1710
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
|
|
1711
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
|
|
1712
|
+
const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
|
|
1713
|
+
const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
|
|
1714
|
+
const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
|
|
1715
|
+
const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
|
|
1716
|
+
let socialMap;
|
|
1717
|
+
if (cfg.social && typeof cfg.social === "object") {
|
|
1718
|
+
socialMap = {};
|
|
1719
|
+
for (const [k, v] of Object.entries(cfg.social)) {
|
|
1720
|
+
if (typeof v === "string") {
|
|
1721
|
+
socialMap[k] = replaceDocumentTokens(v, tokenCtx);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1726
|
+
const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
|
|
1727
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1728
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1729
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1730
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1731
|
+
const logoWidth = cfg.logoWidth;
|
|
1732
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
|
|
1733
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1734
|
+
return {
|
|
1735
|
+
enabled: true,
|
|
1736
|
+
preset,
|
|
1737
|
+
title,
|
|
1738
|
+
subtitle,
|
|
1739
|
+
company,
|
|
1740
|
+
address,
|
|
1741
|
+
email,
|
|
1742
|
+
phone,
|
|
1743
|
+
website,
|
|
1744
|
+
social: socialMap,
|
|
1745
|
+
copyright,
|
|
1746
|
+
badge,
|
|
1747
|
+
badgeColor,
|
|
1748
|
+
badgeTextColor,
|
|
1749
|
+
logo,
|
|
1750
|
+
logoWidth,
|
|
1751
|
+
bgGradient,
|
|
1752
|
+
textColor
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
function normalizeNumberHeadings(raw) {
|
|
1756
|
+
if (raw === void 0 || raw === false) return void 0;
|
|
1757
|
+
if (raw === true) {
|
|
1758
|
+
return { enabled: true, depth: 3, skipH1: false, prefix: "" };
|
|
1759
|
+
}
|
|
1760
|
+
if (typeof raw === "object") {
|
|
1761
|
+
const obj = raw;
|
|
1762
|
+
if (obj.enabled === false) return void 0;
|
|
1763
|
+
return {
|
|
1764
|
+
enabled: true,
|
|
1765
|
+
depth: obj.depth ?? 3,
|
|
1766
|
+
skipH1: obj.skipH1 ?? false,
|
|
1767
|
+
prefix: obj.prefix ?? ""
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
return void 0;
|
|
1771
|
+
}
|
|
1772
|
+
function normalizeSecurity(raw) {
|
|
1773
|
+
if (!raw) return void 0;
|
|
1774
|
+
const sec = raw;
|
|
1775
|
+
if (!sec.userPassword && !sec.ownerPassword && !sec.permissions) return void 0;
|
|
1776
|
+
return {
|
|
1777
|
+
userPassword: sec.userPassword,
|
|
1778
|
+
ownerPassword: sec.ownerPassword,
|
|
1779
|
+
permissions: sec.permissions
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1460
1782
|
function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
1461
1783
|
const configMeta = userConfig.metadata || {};
|
|
1462
1784
|
const mergedMeta = { ...configMeta, ...frontmatter };
|
|
@@ -1499,6 +1821,15 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1499
1821
|
const watermark = normalizeWatermark(rawWatermark);
|
|
1500
1822
|
const rawSignatures = mergedMeta.signatures || userConfig.signatures;
|
|
1501
1823
|
const signatures = normalizeSignatures(rawSignatures, tokenContext);
|
|
1824
|
+
const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
|
|
1825
|
+
const coverPage = normalizeCoverPage(rawCover, tokenContext);
|
|
1826
|
+
const rawBack = mergedMeta.backCover !== void 0 ? mergedMeta.backCover : userConfig.backCover;
|
|
1827
|
+
const backCover = normalizeBackCover(rawBack, tokenContext);
|
|
1828
|
+
const rawNumberHeadings = mergedMeta.numberHeadings !== void 0 ? mergedMeta.numberHeadings : userConfig.numberHeadings;
|
|
1829
|
+
const numberHeadings = normalizeNumberHeadings(rawNumberHeadings);
|
|
1830
|
+
const rawSecurity = mergedMeta.security || userConfig.security;
|
|
1831
|
+
const security = normalizeSecurity(rawSecurity);
|
|
1832
|
+
const math = mergedMeta.math !== false && userConfig.math !== false;
|
|
1502
1833
|
const cssList = [];
|
|
1503
1834
|
const addCss = (item) => {
|
|
1504
1835
|
if (!item) return;
|
|
@@ -1528,6 +1859,11 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1528
1859
|
toc,
|
|
1529
1860
|
signatures,
|
|
1530
1861
|
watermark,
|
|
1862
|
+
coverPage,
|
|
1863
|
+
backCover,
|
|
1864
|
+
numberHeadings,
|
|
1865
|
+
security,
|
|
1866
|
+
math,
|
|
1531
1867
|
css: cssList,
|
|
1532
1868
|
embedImages,
|
|
1533
1869
|
bundleHtml,
|
|
@@ -1535,6 +1871,45 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1535
1871
|
};
|
|
1536
1872
|
}
|
|
1537
1873
|
|
|
1874
|
+
// src/core/math/mathRenderer.ts
|
|
1875
|
+
var import_katex = __toESM(require("katex"));
|
|
1876
|
+
function renderMathToHtml(latex, displayMode = false) {
|
|
1877
|
+
try {
|
|
1878
|
+
return import_katex.default.renderToString(latex.trim(), {
|
|
1879
|
+
displayMode,
|
|
1880
|
+
throwOnError: false,
|
|
1881
|
+
output: "htmlAndMathml",
|
|
1882
|
+
strict: false
|
|
1883
|
+
});
|
|
1884
|
+
} catch {
|
|
1885
|
+
return `<span class="katex-fallback">${latex}</span>`;
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
var KATEX_INLINE_CSS = `
|
|
1889
|
+
.katex { font: normal 1.21em KaTeX_Main, Times New Roman, serif; line-height: 1.2; text-indent: 0; text-rendering: auto; border-color: currentColor; }
|
|
1890
|
+
.katex * { -ms-high-contrast-adjust: none !important; }
|
|
1891
|
+
.katex .katex-html { display: inline-block; }
|
|
1892
|
+
.katex .katex-mathml { clip: rect(1px, 1px, 1px, 1px); border: 0; height: 1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
|
|
1893
|
+
.katex-display { display: block; margin: 1em 0; text-align: center; }
|
|
1894
|
+
.katex-display > .katex { display: inline-block; text-align: initial; }
|
|
1895
|
+
.katex .base { position: relative; white-space: nowrap; width: min-content; }
|
|
1896
|
+
.katex .strut { display: inline-block; }
|
|
1897
|
+
.katex .mord { display: inline-block; }
|
|
1898
|
+
.katex .mbin { display: inline-block; }
|
|
1899
|
+
.katex .mrel { display: inline-block; }
|
|
1900
|
+
.katex .mopen { display: inline-block; }
|
|
1901
|
+
.katex .mclose { display: inline-block; }
|
|
1902
|
+
.katex .mpunct { display: inline-block; }
|
|
1903
|
+
.katex .minner { display: inline-block; }
|
|
1904
|
+
.katex .mop { display: inline-block; }
|
|
1905
|
+
.katex .frac-line { width: 100%; border-bottom-style: solid; }
|
|
1906
|
+
.katex .vlist-t { display: inline-table; table-layout: fixed; }
|
|
1907
|
+
.katex .vlist-r { display: table-row; }
|
|
1908
|
+
.katex .vlist { display: table-cell; vertical-align: bottom; position: relative; }
|
|
1909
|
+
.katex .msupsub { text-align: left; }
|
|
1910
|
+
.katex .sqrt > .root { margin-left: 0.27777778em; margin-right: -0.55555556em; }
|
|
1911
|
+
`;
|
|
1912
|
+
|
|
1538
1913
|
// src/core/html/htmlBuilder.ts
|
|
1539
1914
|
function escapeHtml(str) {
|
|
1540
1915
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -1577,6 +1952,15 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1577
1952
|
result += `<code>${escapeHtml(span.content)}</code>`;
|
|
1578
1953
|
continue;
|
|
1579
1954
|
}
|
|
1955
|
+
if (span.type === "mathInline") {
|
|
1956
|
+
result += renderMathToHtml(span.content, false);
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
if (span.type === "footnoteRef") {
|
|
1960
|
+
const id = escapeHtml(span.footnoteId || span.content);
|
|
1961
|
+
result += `<sup><a href="#fn-${id}" id="fnref-${id}" class="markforge-fnref">[${escapeHtml(span.content)}]</a></sup>`;
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1580
1964
|
if (span.type === "htmlInline") {
|
|
1581
1965
|
result += span.content;
|
|
1582
1966
|
continue;
|
|
@@ -1585,66 +1969,9 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1585
1969
|
}
|
|
1586
1970
|
return result;
|
|
1587
1971
|
}
|
|
1588
|
-
async function
|
|
1589
|
-
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
1590
|
-
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
1591
|
-
let customCss = "";
|
|
1592
|
-
for (const cssPath of resolved.css) {
|
|
1593
|
-
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
1594
|
-
if (fs3.existsSync(fullCssPath)) {
|
|
1595
|
-
customCss += `
|
|
1596
|
-
/* Custom CSS: ${cssPath} */
|
|
1597
|
-
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
1972
|
+
async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
|
|
1601
1973
|
let bodyHtml = "";
|
|
1602
|
-
|
|
1603
|
-
bodyHtml += ` <header class="document-header">
|
|
1604
|
-
`;
|
|
1605
|
-
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
1606
|
-
`;
|
|
1607
|
-
if (resolved.subtitle) {
|
|
1608
|
-
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
1609
|
-
`;
|
|
1610
|
-
}
|
|
1611
|
-
if (resolved.author || resolved.date || resolved.version) {
|
|
1612
|
-
bodyHtml += ` <div class="document-meta">
|
|
1613
|
-
`;
|
|
1614
|
-
if (resolved.author) {
|
|
1615
|
-
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
1616
|
-
`;
|
|
1617
|
-
}
|
|
1618
|
-
if (resolved.version) {
|
|
1619
|
-
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
1620
|
-
`;
|
|
1621
|
-
}
|
|
1622
|
-
if (resolved.date) {
|
|
1623
|
-
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
1624
|
-
`;
|
|
1625
|
-
}
|
|
1626
|
-
bodyHtml += ` </div>
|
|
1627
|
-
`;
|
|
1628
|
-
}
|
|
1629
|
-
bodyHtml += ` </header>
|
|
1630
|
-
`;
|
|
1631
|
-
}
|
|
1632
|
-
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
1633
|
-
bodyHtml += ` <nav class="table-of-contents">
|
|
1634
|
-
`;
|
|
1635
|
-
bodyHtml += ` <h2>Table of Contents</h2>
|
|
1636
|
-
<ul>
|
|
1637
|
-
`;
|
|
1638
|
-
for (const entry of doc.tocEntries) {
|
|
1639
|
-
const indent = " ".repeat(entry.level);
|
|
1640
|
-
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
1641
|
-
`;
|
|
1642
|
-
}
|
|
1643
|
-
bodyHtml += ` </ul>
|
|
1644
|
-
</nav>
|
|
1645
|
-
`;
|
|
1646
|
-
}
|
|
1647
|
-
for (const node of doc.nodes) {
|
|
1974
|
+
for (const node of nodes) {
|
|
1648
1975
|
if (node.type === "heading") {
|
|
1649
1976
|
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
1650
1977
|
bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
|
|
@@ -1654,6 +1981,26 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1654
1981
|
if (node.type === "paragraph") {
|
|
1655
1982
|
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
1656
1983
|
bodyHtml += ` <p>${inner}</p>
|
|
1984
|
+
`;
|
|
1985
|
+
continue;
|
|
1986
|
+
}
|
|
1987
|
+
if (node.type === "mathBlock") {
|
|
1988
|
+
bodyHtml += ` <div class="math-block">${renderMathToHtml(node.text || "", true)}</div>
|
|
1989
|
+
`;
|
|
1990
|
+
continue;
|
|
1991
|
+
}
|
|
1992
|
+
if (node.type === "columns") {
|
|
1993
|
+
const cols = node.columnsCount || 2;
|
|
1994
|
+
const gap = node.columnGap || "1.5rem";
|
|
1995
|
+
let colChildrenHtml = "";
|
|
1996
|
+
for (const col of node.children || []) {
|
|
1997
|
+
const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir);
|
|
1998
|
+
colChildrenHtml += ` <div class="markforge-col">
|
|
1999
|
+
${colInner} </div>
|
|
2000
|
+
`;
|
|
2001
|
+
}
|
|
2002
|
+
bodyHtml += ` <div class="markforge-columns" style="--cols: ${cols}; --col-gap: ${gap};">
|
|
2003
|
+
${colChildrenHtml} </div>
|
|
1657
2004
|
`;
|
|
1658
2005
|
continue;
|
|
1659
2006
|
}
|
|
@@ -1742,95 +2089,522 @@ ${escapeHtml(node.text || "")}
|
|
|
1742
2089
|
continue;
|
|
1743
2090
|
}
|
|
1744
2091
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
width: 100vw;
|
|
1755
|
-
height: 100vh;
|
|
1756
|
-
pointer-events: none;
|
|
1757
|
-
z-index: 0;
|
|
1758
|
-
user-select: none;
|
|
1759
|
-
-webkit-user-select: none;
|
|
2092
|
+
return bodyHtml;
|
|
2093
|
+
}
|
|
2094
|
+
async function renderCoverPageHtml(cover, baseDir = process.cwd()) {
|
|
2095
|
+
let logoHtml = "";
|
|
2096
|
+
if (cover.logo) {
|
|
2097
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
2098
|
+
const src = resolvedLogo ? resolvedLogo.dataUri : cover.logo;
|
|
2099
|
+
const widthStyle = cover.logoWidth ? `max-width: ${typeof cover.logoWidth === "number" ? cover.logoWidth + "px" : cover.logoWidth}; max-height: 80px; width: auto; height: auto;` : "max-height: 60px; max-width: 180px; width: auto; height: auto;";
|
|
2100
|
+
logoHtml = `<div class="cover-logo"><img src="${src}" alt="Logo" style="${widthStyle} object-fit: contain;" /></div>`;
|
|
1760
2101
|
}
|
|
1761
|
-
.
|
|
2102
|
+
const badgeHtml = cover.badge ? `<div class="cover-badge" style="${cover.badgeColor ? `background-color: ${cover.badgeColor};` : ""}${cover.badgeTextColor ? `color: ${cover.badgeTextColor};` : ""}">${escapeHtml(cover.badge)}</div>` : "";
|
|
2103
|
+
const titleHtml = `<h1 class="cover-title">${escapeHtml(cover.title)}</h1>`;
|
|
2104
|
+
const subtitleHtml = cover.subtitle ? `<div class="cover-subtitle">${escapeHtml(cover.subtitle)}</div>` : "";
|
|
2105
|
+
const metaItems = [];
|
|
2106
|
+
if (cover.company) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Organization:</span> <span class="cover-meta-value">${escapeHtml(cover.company)}</span></div>`);
|
|
2107
|
+
if (cover.author) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Author:</span> <span class="cover-meta-value">${escapeHtml(cover.author)}</span></div>`);
|
|
2108
|
+
if (cover.version) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Version:</span> <span class="cover-meta-value">${escapeHtml(cover.version)}</span></div>`);
|
|
2109
|
+
if (cover.date) metaItems.push(`<div class="cover-meta-item"><span class="cover-meta-label">Date:</span> <span class="cover-meta-value">${escapeHtml(cover.date)}</span></div>`);
|
|
2110
|
+
const metaHtml = metaItems.length > 0 ? `<div class="cover-meta">${metaItems.join("\n")}</div>` : "";
|
|
2111
|
+
const footerHtml = cover.footerText ? `<div class="cover-footer-text">${escapeHtml(cover.footerText)}</div>` : "";
|
|
2112
|
+
const css = `
|
|
2113
|
+
.markforge-cover {
|
|
2114
|
+
min-height: 100vh;
|
|
2115
|
+
box-sizing: border-box;
|
|
2116
|
+
display: flex;
|
|
2117
|
+
flex-direction: column;
|
|
2118
|
+
justify-content: space-between;
|
|
2119
|
+
padding: 4rem 3.5rem;
|
|
2120
|
+
page-break-after: always;
|
|
2121
|
+
break-after: page;
|
|
1762
2122
|
position: relative;
|
|
1763
|
-
z-index:
|
|
2123
|
+
z-index: 2;
|
|
2124
|
+
background: ${cover.bgGradient || "#FFFFFF"};
|
|
2125
|
+
-webkit-print-color-adjust: exact;
|
|
2126
|
+
print-color-adjust: exact;
|
|
2127
|
+
color: ${cover.textColor || "#0F172A"};
|
|
2128
|
+
}
|
|
2129
|
+
.markforge-cover.cover-modern {
|
|
2130
|
+
border-top: 8px solid #0D998D;
|
|
2131
|
+
}
|
|
2132
|
+
.markforge-cover.cover-corporate-split {
|
|
2133
|
+
border-left: 12px solid #0D998D;
|
|
2134
|
+
}
|
|
2135
|
+
.markforge-cover.cover-card {
|
|
2136
|
+
background: #F8FAFC;
|
|
2137
|
+
}
|
|
2138
|
+
.cover-top {
|
|
2139
|
+
display: flex;
|
|
2140
|
+
justify-content: space-between;
|
|
2141
|
+
align-items: flex-start;
|
|
2142
|
+
width: 100%;
|
|
2143
|
+
}
|
|
2144
|
+
.cover-badge {
|
|
2145
|
+
display: inline-block;
|
|
2146
|
+
padding: 0.35rem 0.85rem;
|
|
2147
|
+
font-size: 0.78rem;
|
|
2148
|
+
font-weight: 700;
|
|
2149
|
+
letter-spacing: 0.08em;
|
|
2150
|
+
text-transform: uppercase;
|
|
2151
|
+
background-color: #ECFDFD;
|
|
2152
|
+
color: #0D998D;
|
|
2153
|
+
border-radius: 4px;
|
|
2154
|
+
border: 1px solid #33CDCF;
|
|
2155
|
+
}
|
|
2156
|
+
.cover-body {
|
|
2157
|
+
margin: auto 0;
|
|
2158
|
+
}
|
|
2159
|
+
.cover-title {
|
|
2160
|
+
font-size: 2.8rem;
|
|
2161
|
+
font-weight: 800;
|
|
2162
|
+
line-height: 1.15;
|
|
2163
|
+
margin: 0 0 1rem 0;
|
|
2164
|
+
color: inherit;
|
|
2165
|
+
}
|
|
2166
|
+
.cover-subtitle {
|
|
2167
|
+
font-size: 1.35rem;
|
|
2168
|
+
font-weight: 400;
|
|
2169
|
+
color: #64748B;
|
|
2170
|
+
margin: 0 0 2rem 0;
|
|
2171
|
+
line-height: 1.4;
|
|
2172
|
+
}
|
|
2173
|
+
.cover-meta {
|
|
2174
|
+
display: flex;
|
|
2175
|
+
flex-direction: column;
|
|
2176
|
+
gap: 0.5rem;
|
|
2177
|
+
border-top: 1.5px solid #E2E8F0;
|
|
2178
|
+
padding-top: 1.5rem;
|
|
2179
|
+
max-width: 480px;
|
|
2180
|
+
}
|
|
2181
|
+
.cover-meta-item {
|
|
2182
|
+
font-size: 0.92rem;
|
|
2183
|
+
display: flex;
|
|
2184
|
+
gap: 0.75rem;
|
|
2185
|
+
}
|
|
2186
|
+
.cover-meta-label {
|
|
2187
|
+
font-weight: 600;
|
|
2188
|
+
color: #64748B;
|
|
2189
|
+
min-width: 110px;
|
|
2190
|
+
}
|
|
2191
|
+
.cover-meta-value {
|
|
2192
|
+
font-weight: 500;
|
|
2193
|
+
color: #0F172A;
|
|
2194
|
+
}
|
|
2195
|
+
.cover-bottom {
|
|
2196
|
+
display: flex;
|
|
2197
|
+
justify-content: space-between;
|
|
2198
|
+
align-items: flex-end;
|
|
2199
|
+
font-size: 0.82rem;
|
|
2200
|
+
color: #94A3B8;
|
|
1764
2201
|
}
|
|
1765
2202
|
@media print {
|
|
1766
|
-
.
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
left: 0;
|
|
1770
|
-
width: 100vw;
|
|
2203
|
+
.markforge-cover {
|
|
2204
|
+
page-break-after: always;
|
|
2205
|
+
break-after: page;
|
|
1771
2206
|
height: 100vh;
|
|
2207
|
+
min-height: 100vh;
|
|
2208
|
+
max-height: 100vh;
|
|
2209
|
+
box-sizing: border-box;
|
|
2210
|
+
overflow: hidden;
|
|
2211
|
+
margin: 0;
|
|
1772
2212
|
-webkit-print-color-adjust: exact;
|
|
1773
2213
|
print-color-adjust: exact;
|
|
1774
2214
|
}
|
|
1775
2215
|
}
|
|
1776
2216
|
`;
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
ctx.rotate((${wm.rotate} * Math.PI) / 180);
|
|
1792
|
-
ctx.textAlign = 'center';
|
|
1793
|
-
ctx.textBaseline = 'middle';
|
|
1794
|
-
ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
|
|
1795
|
-
ctx.fillStyle = '${wm.color}';
|
|
1796
|
-
ctx.globalAlpha = ${wm.opacity};
|
|
1797
|
-
try { ctx.letterSpacing = '0.15em'; } catch(e) {}
|
|
1798
|
-
ctx.fillText(${JSON.stringify(wm.text.toUpperCase())}, 0, 0);
|
|
1799
|
-
var dataUrl = canvas.toDataURL('image/png');
|
|
1800
|
-
var wmEl = document.getElementById('markforge-watermark');
|
|
1801
|
-
if (wmEl) {
|
|
1802
|
-
wmEl.style.backgroundImage = 'url("' + dataUrl + '")';
|
|
1803
|
-
wmEl.style.backgroundRepeat = 'no-repeat';
|
|
1804
|
-
wmEl.style.backgroundPosition = 'center center';
|
|
1805
|
-
wmEl.style.backgroundSize = 'contain';
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
} catch(err) {}
|
|
1809
|
-
})();
|
|
1810
|
-
</script>
|
|
2217
|
+
const html = ` <section class="markforge-cover cover-${cover.preset}">
|
|
2218
|
+
<div class="cover-top">
|
|
2219
|
+
${logoHtml}
|
|
2220
|
+
${badgeHtml}
|
|
2221
|
+
</div>
|
|
2222
|
+
<div class="cover-body">
|
|
2223
|
+
${titleHtml}
|
|
2224
|
+
${subtitleHtml}
|
|
2225
|
+
${metaHtml}
|
|
2226
|
+
</div>
|
|
2227
|
+
<div class="cover-bottom">
|
|
2228
|
+
${footerHtml}
|
|
2229
|
+
</div>
|
|
2230
|
+
</section>
|
|
1811
2231
|
`;
|
|
2232
|
+
return { html, css };
|
|
2233
|
+
}
|
|
2234
|
+
async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
|
|
2235
|
+
let logoHtml = "";
|
|
2236
|
+
if (backCover.logo) {
|
|
2237
|
+
const resolved = await resolveImage(backCover.logo, baseDir);
|
|
2238
|
+
const src = resolved ? resolved.dataUri : backCover.logo;
|
|
2239
|
+
const widthStyle = backCover.logoWidth ? `style="width: ${typeof backCover.logoWidth === "number" ? `${backCover.logoWidth}px` : backCover.logoWidth}; max-width: 100%;"` : `style="max-width: 160px; height: auto;"`;
|
|
2240
|
+
logoHtml = `<div class="back-logo"><img src="${src}" alt="Brand Logo" ${widthStyle} /></div>`;
|
|
1812
2241
|
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
page-break-inside: avoid;
|
|
1829
|
-
break-inside: avoid;
|
|
2242
|
+
const badgeHtml = backCover.badge ? `<div class="back-badge" style="${backCover.badgeColor ? `background-color: ${backCover.badgeColor};` : ""}${backCover.badgeTextColor ? `color: ${backCover.badgeTextColor};` : ""}">${escapeHtml(backCover.badge)}</div>` : "";
|
|
2243
|
+
const titleHtml = `<h1 class="back-title">${escapeHtml(backCover.title)}</h1>`;
|
|
2244
|
+
const subtitleHtml = backCover.subtitle ? `<div class="back-subtitle">${escapeHtml(backCover.subtitle)}</div>` : "";
|
|
2245
|
+
const contactItems = [];
|
|
2246
|
+
if (backCover.company) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Organization:</span> <span class="back-contact-value">${escapeHtml(backCover.company)}</span></div>`);
|
|
2247
|
+
if (backCover.address) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Address:</span> <span class="back-contact-value">${escapeHtml(backCover.address)}</span></div>`);
|
|
2248
|
+
if (backCover.email) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Email:</span> <a href="mailto:${escapeHtml(backCover.email)}" class="back-contact-link">${escapeHtml(backCover.email)}</a></div>`);
|
|
2249
|
+
if (backCover.phone) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Phone:</span> <span class="back-contact-value">${escapeHtml(backCover.phone)}</span></div>`);
|
|
2250
|
+
if (backCover.website) contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">Website:</span> <a href="${escapeHtml(backCover.website)}" target="_blank" class="back-contact-link">${escapeHtml(backCover.website)}</a></div>`);
|
|
2251
|
+
if (backCover.social) {
|
|
2252
|
+
for (const [network, url] of Object.entries(backCover.social)) {
|
|
2253
|
+
if (url) {
|
|
2254
|
+
contactItems.push(`<div class="back-contact-item"><span class="back-contact-label">${escapeHtml(network.toUpperCase())}:</span> <a href="${escapeHtml(url)}" target="_blank" class="back-contact-link">${escapeHtml(url)}</a></div>`);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
1830
2257
|
}
|
|
1831
|
-
.
|
|
1832
|
-
|
|
1833
|
-
|
|
2258
|
+
const contactHtml = contactItems.length > 0 ? `<div class="back-contact-grid">${contactItems.join("\n")}</div>` : "";
|
|
2259
|
+
const copyrightHtml = backCover.copyright ? `<div class="back-copyright">${escapeHtml(backCover.copyright)}</div>` : "";
|
|
2260
|
+
const isDark = backCover.preset === "corporate";
|
|
2261
|
+
const css = `
|
|
2262
|
+
.markforge-back-cover {
|
|
2263
|
+
min-height: 100vh;
|
|
2264
|
+
box-sizing: border-box;
|
|
2265
|
+
display: flex;
|
|
2266
|
+
flex-direction: column;
|
|
2267
|
+
justify-content: space-between;
|
|
2268
|
+
padding: 4rem 3.5rem;
|
|
2269
|
+
page-break-before: always;
|
|
2270
|
+
break-before: page;
|
|
2271
|
+
position: relative;
|
|
2272
|
+
z-index: 2;
|
|
2273
|
+
background: ${backCover.bgGradient || (isDark ? "#0F172A" : "#FFFFFF")};
|
|
2274
|
+
color: ${backCover.textColor || (isDark ? "#F8FAFC" : "#0F172A")};
|
|
2275
|
+
}
|
|
2276
|
+
.markforge-back-cover.back-modern {
|
|
2277
|
+
border-bottom: 8px solid #0D998D;
|
|
2278
|
+
}
|
|
2279
|
+
.markforge-back-cover.back-corporate {
|
|
2280
|
+
border-left: 12px solid #33CDCF;
|
|
2281
|
+
}
|
|
2282
|
+
.markforge-back-cover.back-card {
|
|
2283
|
+
background: #F8FAFC;
|
|
2284
|
+
}
|
|
2285
|
+
.back-top {
|
|
2286
|
+
display: flex;
|
|
2287
|
+
justify-content: space-between;
|
|
2288
|
+
align-items: flex-start;
|
|
2289
|
+
width: 100%;
|
|
2290
|
+
}
|
|
2291
|
+
.back-badge {
|
|
2292
|
+
display: inline-block;
|
|
2293
|
+
padding: 0.35rem 0.85rem;
|
|
2294
|
+
font-size: 0.78rem;
|
|
2295
|
+
font-weight: 700;
|
|
2296
|
+
letter-spacing: 0.08em;
|
|
2297
|
+
text-transform: uppercase;
|
|
2298
|
+
background-color: #ECFDFD;
|
|
2299
|
+
color: #0D998D;
|
|
2300
|
+
border-radius: 4px;
|
|
2301
|
+
border: 1px solid #33CDCF;
|
|
2302
|
+
}
|
|
2303
|
+
.back-body {
|
|
2304
|
+
margin: auto 0;
|
|
2305
|
+
}
|
|
2306
|
+
.back-title {
|
|
2307
|
+
font-size: 2.6rem;
|
|
2308
|
+
font-weight: 800;
|
|
2309
|
+
line-height: 1.15;
|
|
2310
|
+
margin: 0 0 0.75rem 0;
|
|
2311
|
+
color: inherit;
|
|
2312
|
+
}
|
|
2313
|
+
.back-subtitle {
|
|
2314
|
+
font-size: 1.25rem;
|
|
2315
|
+
font-weight: 400;
|
|
2316
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2317
|
+
margin: 0 0 2rem 0;
|
|
2318
|
+
line-height: 1.4;
|
|
2319
|
+
}
|
|
2320
|
+
.back-contact-grid {
|
|
2321
|
+
display: flex;
|
|
2322
|
+
flex-direction: column;
|
|
2323
|
+
gap: 0.6rem;
|
|
2324
|
+
border-top: 1.5px solid ${isDark ? "#334155" : "#E2E8F0"};
|
|
2325
|
+
padding-top: 1.5rem;
|
|
2326
|
+
max-width: 540px;
|
|
2327
|
+
}
|
|
2328
|
+
.back-contact-item {
|
|
2329
|
+
font-size: 0.92rem;
|
|
2330
|
+
display: flex;
|
|
2331
|
+
gap: 0.75rem;
|
|
2332
|
+
}
|
|
2333
|
+
.back-contact-label {
|
|
2334
|
+
font-weight: 600;
|
|
2335
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2336
|
+
min-width: 110px;
|
|
2337
|
+
}
|
|
2338
|
+
.back-contact-link {
|
|
2339
|
+
color: #0D998D;
|
|
2340
|
+
text-decoration: none;
|
|
2341
|
+
font-weight: 600;
|
|
2342
|
+
}
|
|
2343
|
+
.back-contact-link:hover {
|
|
2344
|
+
text-decoration: underline;
|
|
2345
|
+
}
|
|
2346
|
+
.back-copyright {
|
|
2347
|
+
font-size: 0.82rem;
|
|
2348
|
+
color: ${isDark ? "#64748B" : "#94A3B8"};
|
|
2349
|
+
border-top: 1px solid ${isDark ? "#1E293B" : "#F1F5F9"};
|
|
2350
|
+
padding-top: 1rem;
|
|
2351
|
+
margin-top: 2rem;
|
|
2352
|
+
}
|
|
2353
|
+
@media print {
|
|
2354
|
+
.markforge-back-cover {
|
|
2355
|
+
page: back-cover-page;
|
|
2356
|
+
page-break-before: always;
|
|
2357
|
+
break-before: page;
|
|
2358
|
+
page-break-after: avoid;
|
|
2359
|
+
break-after: avoid;
|
|
2360
|
+
min-height: 100vh;
|
|
2361
|
+
height: 100vh;
|
|
2362
|
+
max-height: 100vh;
|
|
2363
|
+
margin: 0;
|
|
2364
|
+
box-sizing: border-box;
|
|
2365
|
+
overflow: hidden;
|
|
2366
|
+
-webkit-print-color-adjust: exact;
|
|
2367
|
+
print-color-adjust: exact;
|
|
2368
|
+
}
|
|
2369
|
+
}
|
|
2370
|
+
`;
|
|
2371
|
+
const html = ` <section class="markforge-back-cover back-${backCover.preset}">
|
|
2372
|
+
<div class="back-top">
|
|
2373
|
+
${logoHtml}
|
|
2374
|
+
${badgeHtml}
|
|
2375
|
+
</div>
|
|
2376
|
+
<div class="back-body">
|
|
2377
|
+
${titleHtml}
|
|
2378
|
+
${subtitleHtml}
|
|
2379
|
+
${contactHtml}
|
|
2380
|
+
</div>
|
|
2381
|
+
${copyrightHtml}
|
|
2382
|
+
</section>
|
|
2383
|
+
`;
|
|
2384
|
+
return { html, css };
|
|
2385
|
+
}
|
|
2386
|
+
async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
|
|
2387
|
+
var _a;
|
|
2388
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2389
|
+
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
2390
|
+
let customCss = "";
|
|
2391
|
+
for (const cssPath of resolved.css) {
|
|
2392
|
+
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
2393
|
+
if (fs3.existsSync(fullCssPath)) {
|
|
2394
|
+
customCss += `
|
|
2395
|
+
/* Custom CSS: ${cssPath} */
|
|
2396
|
+
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
2400
|
+
const extraCss = `
|
|
2401
|
+
.markforge-columns {
|
|
2402
|
+
display: grid;
|
|
2403
|
+
grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
|
|
2404
|
+
gap: var(--col-gap, 1.5rem);
|
|
2405
|
+
margin: 1.5rem 0;
|
|
2406
|
+
}
|
|
2407
|
+
.markforge-col {
|
|
2408
|
+
min-width: 0;
|
|
2409
|
+
}
|
|
2410
|
+
.markforge-fnref {
|
|
2411
|
+
text-decoration: none;
|
|
2412
|
+
font-size: 0.8em;
|
|
2413
|
+
vertical-align: super;
|
|
2414
|
+
color: #0D998D;
|
|
2415
|
+
font-weight: 700;
|
|
2416
|
+
}
|
|
2417
|
+
.markforge-footnotes {
|
|
2418
|
+
margin-top: 3rem;
|
|
2419
|
+
padding-top: 1rem;
|
|
2420
|
+
font-size: 0.88rem;
|
|
2421
|
+
color: #64748B;
|
|
2422
|
+
}
|
|
2423
|
+
.markforge-footnotes hr {
|
|
2424
|
+
border: 0;
|
|
2425
|
+
border-top: 1px solid #E2E8F0;
|
|
2426
|
+
margin-bottom: 1rem;
|
|
2427
|
+
}
|
|
2428
|
+
.markforge-fn-return {
|
|
2429
|
+
text-decoration: none;
|
|
2430
|
+
color: #0D998D;
|
|
2431
|
+
}
|
|
2432
|
+
.math-block {
|
|
2433
|
+
margin: 1.5rem 0;
|
|
2434
|
+
text-align: center;
|
|
2435
|
+
overflow-x: auto;
|
|
2436
|
+
}
|
|
2437
|
+
`;
|
|
2438
|
+
let coverHtml = "";
|
|
2439
|
+
let coverCss = "";
|
|
2440
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
2441
|
+
const coverRes = await renderCoverPageHtml(resolved.coverPage, baseDir);
|
|
2442
|
+
coverHtml = coverRes.html;
|
|
2443
|
+
coverCss = coverRes.css;
|
|
2444
|
+
}
|
|
2445
|
+
let backHtml = "";
|
|
2446
|
+
let backCss = "";
|
|
2447
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
2448
|
+
const backRes = await renderBackCoverHtml(resolved.backCover, baseDir);
|
|
2449
|
+
backHtml = backRes.html;
|
|
2450
|
+
backCss = backRes.css;
|
|
2451
|
+
}
|
|
2452
|
+
let bodyHtml = "";
|
|
2453
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2454
|
+
bodyHtml += ` <header class="document-header">
|
|
2455
|
+
`;
|
|
2456
|
+
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
2457
|
+
`;
|
|
2458
|
+
if (resolved.subtitle) {
|
|
2459
|
+
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
2460
|
+
`;
|
|
2461
|
+
}
|
|
2462
|
+
if (resolved.author || resolved.date || resolved.version) {
|
|
2463
|
+
bodyHtml += ` <div class="document-meta">
|
|
2464
|
+
`;
|
|
2465
|
+
if (resolved.author) {
|
|
2466
|
+
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
2467
|
+
`;
|
|
2468
|
+
}
|
|
2469
|
+
if (resolved.version) {
|
|
2470
|
+
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
2471
|
+
`;
|
|
2472
|
+
}
|
|
2473
|
+
if (resolved.date) {
|
|
2474
|
+
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
2475
|
+
`;
|
|
2476
|
+
}
|
|
2477
|
+
bodyHtml += ` </div>
|
|
2478
|
+
`;
|
|
2479
|
+
}
|
|
2480
|
+
bodyHtml += ` </header>
|
|
2481
|
+
`;
|
|
2482
|
+
}
|
|
2483
|
+
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
2484
|
+
bodyHtml += ` <nav class="table-of-contents">
|
|
2485
|
+
`;
|
|
2486
|
+
bodyHtml += ` <h2>Table of Contents</h2>
|
|
2487
|
+
<ul>
|
|
2488
|
+
`;
|
|
2489
|
+
for (const entry of doc.tocEntries) {
|
|
2490
|
+
const indent = " ".repeat(entry.level);
|
|
2491
|
+
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
2492
|
+
`;
|
|
2493
|
+
}
|
|
2494
|
+
bodyHtml += ` </ul>
|
|
2495
|
+
</nav>
|
|
2496
|
+
`;
|
|
2497
|
+
}
|
|
2498
|
+
bodyHtml += await renderNodesToHtml(doc.nodes, resolved, baseDir);
|
|
2499
|
+
let footnotesHtml = "";
|
|
2500
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
2501
|
+
let fnListHtml = "";
|
|
2502
|
+
for (const def of doc.footnoteDefs) {
|
|
2503
|
+
const defInner = await renderInlinesToHtml(def.inlines, baseDir);
|
|
2504
|
+
fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">↩</a></li>
|
|
2505
|
+
`;
|
|
2506
|
+
}
|
|
2507
|
+
footnotesHtml = `
|
|
2508
|
+
<footer class="markforge-footnotes">
|
|
2509
|
+
<hr />
|
|
2510
|
+
<ol>
|
|
2511
|
+
${fnListHtml} </ol>
|
|
2512
|
+
</footer>
|
|
2513
|
+
`;
|
|
2514
|
+
}
|
|
2515
|
+
let watermarkCss = "";
|
|
2516
|
+
let watermarkHtml = "";
|
|
2517
|
+
if (resolved.watermark) {
|
|
2518
|
+
const wm = resolved.watermark;
|
|
2519
|
+
watermarkCss = `
|
|
2520
|
+
.document-watermark {
|
|
2521
|
+
position: fixed;
|
|
2522
|
+
top: 0;
|
|
2523
|
+
left: 0;
|
|
2524
|
+
right: 0;
|
|
2525
|
+
bottom: 0;
|
|
2526
|
+
width: 100%;
|
|
2527
|
+
height: 100%;
|
|
2528
|
+
pointer-events: none;
|
|
2529
|
+
z-index: 0;
|
|
2530
|
+
user-select: none;
|
|
2531
|
+
-webkit-user-select: none;
|
|
2532
|
+
-webkit-print-color-adjust: exact;
|
|
2533
|
+
print-color-adjust: exact;
|
|
2534
|
+
}
|
|
2535
|
+
.document-container {
|
|
2536
|
+
position: relative;
|
|
2537
|
+
z-index: 1;
|
|
2538
|
+
}
|
|
2539
|
+
@media print {
|
|
2540
|
+
.document-watermark {
|
|
2541
|
+
position: fixed;
|
|
2542
|
+
top: 0;
|
|
2543
|
+
left: 0;
|
|
2544
|
+
right: 0;
|
|
2545
|
+
bottom: 0;
|
|
2546
|
+
width: 100vw;
|
|
2547
|
+
height: 100vh;
|
|
2548
|
+
pointer-events: none;
|
|
2549
|
+
z-index: 0;
|
|
2550
|
+
-webkit-print-color-adjust: exact;
|
|
2551
|
+
print-color-adjust: exact;
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
`;
|
|
2555
|
+
watermarkHtml = ` <div id="markforge-watermark" class="document-watermark" aria-hidden="true"></div>
|
|
2556
|
+
<script>
|
|
2557
|
+
(function() {
|
|
2558
|
+
try {
|
|
2559
|
+
var canvas = document.createElement('canvas');
|
|
2560
|
+
var dpr = 2;
|
|
2561
|
+
var width = 1200;
|
|
2562
|
+
var height = 1600;
|
|
2563
|
+
canvas.width = width * dpr;
|
|
2564
|
+
canvas.height = height * dpr;
|
|
2565
|
+
var ctx = canvas.getContext('2d');
|
|
2566
|
+
if (ctx) {
|
|
2567
|
+
ctx.scale(dpr, dpr);
|
|
2568
|
+
ctx.translate(width / 2, height / 2);
|
|
2569
|
+
ctx.rotate((-Math.abs(${wm.rotate || 45}) * Math.PI) / 180);
|
|
2570
|
+
ctx.textAlign = 'center';
|
|
2571
|
+
ctx.textBaseline = 'middle';
|
|
2572
|
+
ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
|
|
2573
|
+
ctx.fillStyle = '${wm.color}';
|
|
2574
|
+
ctx.globalAlpha = ${wm.opacity};
|
|
2575
|
+
try { ctx.letterSpacing = '0.15em'; } catch(e) {}
|
|
2576
|
+
ctx.fillText(${JSON.stringify(wm.text.toUpperCase())}, 0, 0);
|
|
2577
|
+
var dataUrl = canvas.toDataURL('image/png');
|
|
2578
|
+
var wmEl = document.getElementById('markforge-watermark');
|
|
2579
|
+
if (wmEl) {
|
|
2580
|
+
wmEl.style.backgroundImage = 'url("' + dataUrl + '")';
|
|
2581
|
+
wmEl.style.backgroundRepeat = 'no-repeat';
|
|
2582
|
+
wmEl.style.backgroundPosition = 'center center';
|
|
2583
|
+
wmEl.style.backgroundSize = 'contain';
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
} catch(err) {}
|
|
2587
|
+
})();
|
|
2588
|
+
</script>
|
|
2589
|
+
`;
|
|
2590
|
+
}
|
|
2591
|
+
let signaturesHtml = "";
|
|
2592
|
+
let signaturesCss = "";
|
|
2593
|
+
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
2594
|
+
const sig = resolved.signatures;
|
|
2595
|
+
const numItems = sig.items.length;
|
|
2596
|
+
signaturesCss = `
|
|
2597
|
+
.markforge-signatures {
|
|
2598
|
+
margin-top: ${sig.spacingBefore};
|
|
2599
|
+
display: grid;
|
|
2600
|
+
grid-template-columns: ${numItems === 1 ? sig.align === "left" ? "minmax(200px, 280px) 1fr" : sig.align === "center" ? "1fr minmax(200px, 280px) 1fr" : "1fr minmax(200px, 280px)" : `repeat(${numItems}, minmax(0, 1fr))`};
|
|
2601
|
+
gap: 2rem;
|
|
2602
|
+
page-break-inside: avoid;
|
|
2603
|
+
break-inside: avoid;
|
|
2604
|
+
}
|
|
2605
|
+
.markforge-signature-card {
|
|
2606
|
+
${numItems === 1 && sig.align === "center" ? "grid-column: 2;" : ""}
|
|
2607
|
+
${numItems === 1 && sig.align === "right" ? "grid-column: 2;" : ""}
|
|
1834
2608
|
display: flex;
|
|
1835
2609
|
flex-direction: column;
|
|
1836
2610
|
${sig.style === "box" ? `border: 1px solid ${sig.borderColor}; border-radius: 6px; padding: 14px 18px; background-color: var(--mf-card-bg, #F8FAFC);` : ""}
|
|
@@ -1930,6 +2704,10 @@ ${itemCards}
|
|
|
1930
2704
|
<style>
|
|
1931
2705
|
${THEME_COMPONENTS}
|
|
1932
2706
|
${baseThemeCss}
|
|
2707
|
+
${KATEX_INLINE_CSS}
|
|
2708
|
+
${extraCss}
|
|
2709
|
+
${coverCss}
|
|
2710
|
+
${backCss}
|
|
1933
2711
|
${customCss}
|
|
1934
2712
|
${inlinedCss}
|
|
1935
2713
|
${watermarkCss}
|
|
@@ -1937,14 +2715,95 @@ ${signaturesCss}
|
|
|
1937
2715
|
</style>
|
|
1938
2716
|
</head>
|
|
1939
2717
|
<body>
|
|
1940
|
-
${watermarkHtml} <div class="document-container">
|
|
1941
|
-
${bodyHtml}${signaturesHtml} </div>
|
|
2718
|
+
${watermarkHtml}${coverHtml} <div class="document-container">
|
|
2719
|
+
${bodyHtml}${footnotesHtml}${signaturesHtml} </div>
|
|
1942
2720
|
${mermaidScript}
|
|
1943
|
-
</body>
|
|
2721
|
+
${backHtml}</body>
|
|
1944
2722
|
</html>`;
|
|
1945
2723
|
}
|
|
1946
2724
|
|
|
1947
2725
|
// src/core/pdf/pdfBuilder.ts
|
|
2726
|
+
function escapeXml(str) {
|
|
2727
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2728
|
+
}
|
|
2729
|
+
function generateWatermarkPngBuffer(chromePath, wm) {
|
|
2730
|
+
const tmpHtml = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.html`);
|
|
2731
|
+
const tmpPng = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
|
|
2732
|
+
try {
|
|
2733
|
+
const text = escapeXml(wm.text.toUpperCase());
|
|
2734
|
+
const fontSize = (wm.fontSize || 52) * 1.5;
|
|
2735
|
+
const color = wm.color || "#E11D48";
|
|
2736
|
+
const opacity = wm.opacity !== void 0 ? wm.opacity : 0.12;
|
|
2737
|
+
const rotate = wm.rotate !== void 0 ? wm.rotate : -45;
|
|
2738
|
+
const html = `<!DOCTYPE html>
|
|
2739
|
+
<html>
|
|
2740
|
+
<head>
|
|
2741
|
+
<meta charset="utf-8">
|
|
2742
|
+
<style>
|
|
2743
|
+
html, body {
|
|
2744
|
+
margin: 0;
|
|
2745
|
+
padding: 0;
|
|
2746
|
+
width: 1200px;
|
|
2747
|
+
height: 1600px;
|
|
2748
|
+
background: transparent;
|
|
2749
|
+
overflow: hidden;
|
|
2750
|
+
}
|
|
2751
|
+
.wm-box {
|
|
2752
|
+
width: 1200px;
|
|
2753
|
+
height: 1600px;
|
|
2754
|
+
display: flex;
|
|
2755
|
+
align-items: center;
|
|
2756
|
+
justify-content: center;
|
|
2757
|
+
transform: rotate(${rotate}deg);
|
|
2758
|
+
}
|
|
2759
|
+
.wm-text {
|
|
2760
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
2761
|
+
font-weight: 900;
|
|
2762
|
+
font-size: ${fontSize}px;
|
|
2763
|
+
color: ${color};
|
|
2764
|
+
opacity: ${opacity};
|
|
2765
|
+
letter-spacing: 0.15em;
|
|
2766
|
+
text-transform: uppercase;
|
|
2767
|
+
white-space: nowrap;
|
|
2768
|
+
}
|
|
2769
|
+
</style>
|
|
2770
|
+
</head>
|
|
2771
|
+
<body>
|
|
2772
|
+
<div class="wm-box"><span class="wm-text">${text}</span></div>
|
|
2773
|
+
</body>
|
|
2774
|
+
</html>`;
|
|
2775
|
+
fs4.writeFileSync(tmpHtml, html, "utf8");
|
|
2776
|
+
const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
|
|
2777
|
+
const isWin = process.platform === "win32";
|
|
2778
|
+
(0, import_node_child_process.spawnSync)(
|
|
2779
|
+
chromePath,
|
|
2780
|
+
[
|
|
2781
|
+
"--headless=new",
|
|
2782
|
+
"--disable-gpu",
|
|
2783
|
+
"--disable-sync",
|
|
2784
|
+
"--disable-extensions",
|
|
2785
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
2786
|
+
`--screenshot=${tmpPng}`,
|
|
2787
|
+
"--window-size=1200,1600",
|
|
2788
|
+
"--default-background-color=00000000",
|
|
2789
|
+
fileUrl
|
|
2790
|
+
],
|
|
2791
|
+
{ timeout: 15e3, windowsHide: true }
|
|
2792
|
+
);
|
|
2793
|
+
if (fs4.existsSync(tmpPng) && fs4.statSync(tmpPng).size > 0) {
|
|
2794
|
+
return fs4.readFileSync(tmpPng);
|
|
2795
|
+
}
|
|
2796
|
+
return null;
|
|
2797
|
+
} catch {
|
|
2798
|
+
return null;
|
|
2799
|
+
} finally {
|
|
2800
|
+
try {
|
|
2801
|
+
if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
|
|
2802
|
+
if (fs4.existsSync(tmpPng)) fs4.unlinkSync(tmpPng);
|
|
2803
|
+
} catch {
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
1948
2807
|
function findChromeExecutable() {
|
|
1949
2808
|
if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
|
|
1950
2809
|
return process.env.CHROME_PATH;
|
|
@@ -2009,7 +2868,7 @@ function findChromeExecutable() {
|
|
|
2009
2868
|
return null;
|
|
2010
2869
|
}
|
|
2011
2870
|
function injectPagedMediaStyles(html, config, metadata) {
|
|
2012
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2871
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2013
2872
|
const resolved = resolveDocumentConfig(metadata || {}, config);
|
|
2014
2873
|
const size = resolved.paperSize;
|
|
2015
2874
|
const orientation = resolved.orientation;
|
|
@@ -2053,6 +2912,38 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
2053
2912
|
${fontStyle}
|
|
2054
2913
|
}`;
|
|
2055
2914
|
};
|
|
2915
|
+
const coverPageCss = ((_a = resolved.coverPage) == null ? void 0 : _a.enabled) ? `
|
|
2916
|
+
@page :first {
|
|
2917
|
+
margin-top: 0;
|
|
2918
|
+
margin-bottom: 0;
|
|
2919
|
+
margin-left: 0;
|
|
2920
|
+
margin-right: 0;
|
|
2921
|
+
background-image: none !important;
|
|
2922
|
+
@top-left { content: none; }
|
|
2923
|
+
@top-center { content: none; }
|
|
2924
|
+
@top-right { content: none; }
|
|
2925
|
+
@bottom-left { content: none; }
|
|
2926
|
+
@bottom-center { content: none; }
|
|
2927
|
+
@bottom-right { content: none; }
|
|
2928
|
+
}` : "";
|
|
2929
|
+
const backCoverCss = ((_b = resolved.backCover) == null ? void 0 : _b.enabled) ? `
|
|
2930
|
+
@page back-cover-page {
|
|
2931
|
+
size: ${size} ${orientation};
|
|
2932
|
+
margin: 0;
|
|
2933
|
+
background-image: none !important;
|
|
2934
|
+
@top-left { content: none; }
|
|
2935
|
+
@top-center { content: none; }
|
|
2936
|
+
@top-right { content: none; }
|
|
2937
|
+
@bottom-left { content: none; }
|
|
2938
|
+
@bottom-center { content: none; }
|
|
2939
|
+
@bottom-right { content: none; }
|
|
2940
|
+
}
|
|
2941
|
+
.markforge-back-cover {
|
|
2942
|
+
page: back-cover-page;
|
|
2943
|
+
min-height: 100vh;
|
|
2944
|
+
height: 100vh;
|
|
2945
|
+
box-sizing: border-box;
|
|
2946
|
+
}` : "";
|
|
2056
2947
|
const pagedCss = `
|
|
2057
2948
|
@page {
|
|
2058
2949
|
size: ${size} ${orientation};
|
|
@@ -2060,15 +2951,18 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
2060
2951
|
margin-bottom: ${bottom};
|
|
2061
2952
|
margin-left: ${left};
|
|
2062
2953
|
margin-right: ${right};
|
|
2063
|
-
${buildZoneCss("top-left", (
|
|
2064
|
-
${buildZoneCss("top-center", (
|
|
2065
|
-
${buildZoneCss("top-right", (
|
|
2066
|
-
${buildZoneCss("bottom-left", (
|
|
2067
|
-
${buildZoneCss("bottom-center", (
|
|
2068
|
-
${buildZoneCss("bottom-right", (
|
|
2954
|
+
${buildZoneCss("top-left", (_c = resolved.header) == null ? void 0 : _c.left)}
|
|
2955
|
+
${buildZoneCss("top-center", (_d = resolved.header) == null ? void 0 : _d.center)}
|
|
2956
|
+
${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
|
|
2957
|
+
${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
|
|
2958
|
+
${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
|
|
2959
|
+
${buildZoneCss("bottom-right", (_h = resolved.footer) == null ? void 0 : _h.right, true)}
|
|
2069
2960
|
}
|
|
2961
|
+
${coverPageCss}
|
|
2962
|
+
${backCoverCss}
|
|
2070
2963
|
@media print {
|
|
2071
2964
|
body { padding: 0; }
|
|
2965
|
+
.document-watermark { display: none !important; }
|
|
2072
2966
|
h1, h2, h3, pre, table, blockquote, .callout {
|
|
2073
2967
|
break-inside: avoid;
|
|
2074
2968
|
}
|
|
@@ -2116,6 +3010,7 @@ startxref
|
|
|
2116
3010
|
return Buffer.from(pdfBody, "utf-8");
|
|
2117
3011
|
}
|
|
2118
3012
|
async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
3013
|
+
var _a, _b, _c;
|
|
2119
3014
|
const baseHtml = await buildHtmlDocument(doc, config, baseDir);
|
|
2120
3015
|
const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
|
|
2121
3016
|
const chromePath = findChromeExecutable();
|
|
@@ -2125,6 +3020,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2125
3020
|
const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
|
|
2126
3021
|
const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
|
|
2127
3022
|
const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
|
|
3023
|
+
const isWin = process.platform === "win32";
|
|
2128
3024
|
const isolatedFlags = [
|
|
2129
3025
|
`--user-data-dir=${tmpProfile}`,
|
|
2130
3026
|
"--no-first-run",
|
|
@@ -2135,7 +3031,6 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2135
3031
|
"--disable-default-apps",
|
|
2136
3032
|
"--disable-extensions",
|
|
2137
3033
|
"--disable-domain-reliability",
|
|
2138
|
-
"--disable-client-side-phishing-detection",
|
|
2139
3034
|
"--disable-breakpad",
|
|
2140
3035
|
"--disable-component-extensions-with-background-pages",
|
|
2141
3036
|
"--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
|
|
@@ -2144,12 +3039,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2144
3039
|
"--mute-audio",
|
|
2145
3040
|
"--no-service-autorun",
|
|
2146
3041
|
"--disable-gpu",
|
|
2147
|
-
"--no-sandbox",
|
|
2148
|
-
"--disable-setuid-sandbox",
|
|
2149
|
-
"--allow-file-access-from-files",
|
|
2150
|
-
"--disable-web-security",
|
|
3042
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
2151
3043
|
"--force-color-profile=srgb",
|
|
2152
|
-
"--no-pdf-header-footer"
|
|
3044
|
+
"--no-pdf-header-footer",
|
|
3045
|
+
"--window-size=1200,1600"
|
|
2153
3046
|
];
|
|
2154
3047
|
try {
|
|
2155
3048
|
fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
|
|
@@ -2164,7 +3057,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2164
3057
|
`--print-to-pdf=${tmpPdf}`,
|
|
2165
3058
|
fileUrl
|
|
2166
3059
|
],
|
|
2167
|
-
{ timeout: 3e4 }
|
|
3060
|
+
{ timeout: 3e4, windowsHide: true }
|
|
2168
3061
|
);
|
|
2169
3062
|
if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
|
|
2170
3063
|
res = (0, import_node_child_process.spawnSync)(
|
|
@@ -2175,12 +3068,78 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2175
3068
|
`--print-to-pdf=${tmpPdf}`,
|
|
2176
3069
|
fileUrl
|
|
2177
3070
|
],
|
|
2178
|
-
{ timeout: 3e4 }
|
|
3071
|
+
{ timeout: 3e4, windowsHide: true }
|
|
2179
3072
|
);
|
|
2180
3073
|
}
|
|
2181
3074
|
if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
|
|
2182
3075
|
const pdfBuffer = fs4.readFileSync(tmpPdf);
|
|
2183
|
-
|
|
3076
|
+
try {
|
|
3077
|
+
const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBuffer);
|
|
3078
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
3079
|
+
if (resolved.title) pdfDoc.setTitle(resolved.title);
|
|
3080
|
+
if (resolved.author) pdfDoc.setAuthor(resolved.author);
|
|
3081
|
+
if (resolved.subtitle) pdfDoc.setSubject(resolved.subtitle);
|
|
3082
|
+
pdfDoc.setCreator("MarkForge Enterprise Document Generator");
|
|
3083
|
+
pdfDoc.setProducer("MarkForge (by Ma'sum)");
|
|
3084
|
+
pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
|
|
3085
|
+
if (((_a = resolved.backCover) == null ? void 0 : _a.enabled) && pdfDoc.getPageCount() > 2) {
|
|
3086
|
+
pdfDoc.removePage(pdfDoc.getPageCount() - 1);
|
|
3087
|
+
}
|
|
3088
|
+
if (resolved.watermark) {
|
|
3089
|
+
const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
|
|
3090
|
+
if (wmPng) {
|
|
3091
|
+
const embeddedPng = await pdfDoc.embedPng(wmPng);
|
|
3092
|
+
const pages = pdfDoc.getPages();
|
|
3093
|
+
const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
|
|
3094
|
+
const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? pages.length - 1 : pages.length;
|
|
3095
|
+
for (let i = startPageIndex; i < endPageIndex; i++) {
|
|
3096
|
+
const page = pages[i];
|
|
3097
|
+
const { width, height } = page.getSize();
|
|
3098
|
+
page.drawImage(embeddedPng, {
|
|
3099
|
+
x: 0,
|
|
3100
|
+
y: 0,
|
|
3101
|
+
width,
|
|
3102
|
+
height
|
|
3103
|
+
});
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
const savedBytes = await pdfDoc.save();
|
|
3108
|
+
let finalBuffer = Buffer.from(savedBytes);
|
|
3109
|
+
if (resolved.security) {
|
|
3110
|
+
const sec = resolved.security;
|
|
3111
|
+
const hasUserPassword = typeof sec.userPassword === "string" && sec.userPassword.length > 0;
|
|
3112
|
+
const hasOwnerPassword = typeof sec.ownerPassword === "string" && sec.ownerPassword.length > 0;
|
|
3113
|
+
if (hasUserPassword || hasOwnerPassword) {
|
|
3114
|
+
try {
|
|
3115
|
+
const userPass = sec.userPassword ?? "";
|
|
3116
|
+
const ownerPass = sec.ownerPassword ?? userPass;
|
|
3117
|
+
const perms = sec.permissions;
|
|
3118
|
+
const encryptedBytes = await (0, import_pdf_encrypt.encryptPDF)(
|
|
3119
|
+
new Uint8Array(finalBuffer),
|
|
3120
|
+
userPass,
|
|
3121
|
+
{
|
|
3122
|
+
ownerPassword: ownerPass,
|
|
3123
|
+
algorithm: "AES-256",
|
|
3124
|
+
allowPrinting: (perms == null ? void 0 : perms.printing) !== "none",
|
|
3125
|
+
allowHighQualityPrint: (perms == null ? void 0 : perms.printing) === "highResolution",
|
|
3126
|
+
allowModifying: (perms == null ? void 0 : perms.modifying) ?? true,
|
|
3127
|
+
allowCopying: (perms == null ? void 0 : perms.copying) ?? true,
|
|
3128
|
+
allowAnnotating: (perms == null ? void 0 : perms.annotating) ?? true,
|
|
3129
|
+
allowFillingForms: (perms == null ? void 0 : perms.fillingForms) ?? true,
|
|
3130
|
+
allowExtraction: (perms == null ? void 0 : perms.contentAccessibility) ?? true,
|
|
3131
|
+
allowAssembly: (perms == null ? void 0 : perms.documentAssembly) ?? true
|
|
3132
|
+
}
|
|
3133
|
+
);
|
|
3134
|
+
finalBuffer = Buffer.from(encryptedBytes);
|
|
3135
|
+
} catch {
|
|
3136
|
+
}
|
|
3137
|
+
}
|
|
3138
|
+
}
|
|
3139
|
+
return finalBuffer;
|
|
3140
|
+
} catch {
|
|
3141
|
+
return pdfBuffer;
|
|
3142
|
+
}
|
|
2184
3143
|
}
|
|
2185
3144
|
} catch {
|
|
2186
3145
|
} finally {
|
|
@@ -2435,6 +3394,29 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2435
3394
|
);
|
|
2436
3395
|
continue;
|
|
2437
3396
|
}
|
|
3397
|
+
if (span.type === "mathInline") {
|
|
3398
|
+
runs.push(
|
|
3399
|
+
new import_docx.TextRun({
|
|
3400
|
+
text: span.content,
|
|
3401
|
+
font: "Cambria Math",
|
|
3402
|
+
italics: true,
|
|
3403
|
+
size: options.size,
|
|
3404
|
+
color: options.color || "0F172A"
|
|
3405
|
+
})
|
|
3406
|
+
);
|
|
3407
|
+
continue;
|
|
3408
|
+
}
|
|
3409
|
+
if (span.type === "footnoteRef") {
|
|
3410
|
+
runs.push(
|
|
3411
|
+
new import_docx.TextRun({
|
|
3412
|
+
text: `[${span.content}]`,
|
|
3413
|
+
superScript: true,
|
|
3414
|
+
color: "009DA0",
|
|
3415
|
+
bold: true
|
|
3416
|
+
})
|
|
3417
|
+
);
|
|
3418
|
+
continue;
|
|
3419
|
+
}
|
|
2438
3420
|
runs.push(
|
|
2439
3421
|
new import_docx.TextRun({
|
|
2440
3422
|
text: span.content,
|
|
@@ -2449,7 +3431,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2449
3431
|
return runs;
|
|
2450
3432
|
}
|
|
2451
3433
|
async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
2452
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
3434
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
2453
3435
|
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2454
3436
|
const docElements = [];
|
|
2455
3437
|
const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
|
|
@@ -2460,7 +3442,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2460
3442
|
const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
|
|
2461
3443
|
const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
|
|
2462
3444
|
const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
|
|
2463
|
-
if (resolved.title) {
|
|
3445
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2464
3446
|
docElements.push(
|
|
2465
3447
|
new import_docx.Paragraph({
|
|
2466
3448
|
children: [
|
|
@@ -2876,7 +3858,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2876
3858
|
}
|
|
2877
3859
|
if (node.type === "table" && node.children) {
|
|
2878
3860
|
const tableRows = [];
|
|
2879
|
-
const numCols = ((
|
|
3861
|
+
const numCols = ((_c = (_b = node.children[0]) == null ? void 0 : _b.children) == null ? void 0 : _c.length) || 1;
|
|
2880
3862
|
const colWidth = Math.floor(9e3 / numCols);
|
|
2881
3863
|
for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
|
|
2882
3864
|
const rowNode = node.children[rowIdx];
|
|
@@ -2886,7 +3868,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2886
3868
|
if (rowNode.children) {
|
|
2887
3869
|
for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
|
|
2888
3870
|
const cellNode = rowNode.children[colIdx];
|
|
2889
|
-
const align = (
|
|
3871
|
+
const align = (_d = node.align) == null ? void 0 : _d[colIdx];
|
|
2890
3872
|
let alignment = import_docx.AlignmentType.LEFT;
|
|
2891
3873
|
if (align === "center") alignment = import_docx.AlignmentType.CENTER;
|
|
2892
3874
|
if (align === "right") alignment = import_docx.AlignmentType.RIGHT;
|
|
@@ -3025,12 +4007,119 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3025
4007
|
}
|
|
3026
4008
|
continue;
|
|
3027
4009
|
}
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
4010
|
+
if (node.type === "mathBlock") {
|
|
4011
|
+
docElements.push(
|
|
4012
|
+
new import_docx.Paragraph({
|
|
4013
|
+
alignment: import_docx.AlignmentType.CENTER,
|
|
4014
|
+
children: [
|
|
4015
|
+
new import_docx.TextRun({
|
|
4016
|
+
text: node.text || "",
|
|
4017
|
+
font: "Cambria Math",
|
|
4018
|
+
italics: true,
|
|
4019
|
+
size: 24,
|
|
4020
|
+
// 12pt
|
|
4021
|
+
color: textHex
|
|
4022
|
+
})
|
|
4023
|
+
],
|
|
4024
|
+
spacing: { before: 180, after: 180 },
|
|
4025
|
+
shading: { fill: cardBgHex, type: import_docx.ShadingType.CLEAR },
|
|
4026
|
+
border: {
|
|
4027
|
+
top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4028
|
+
bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4029
|
+
left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4030
|
+
right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex }
|
|
4031
|
+
}
|
|
4032
|
+
})
|
|
4033
|
+
);
|
|
4034
|
+
continue;
|
|
4035
|
+
}
|
|
4036
|
+
if (node.type === "columns") {
|
|
4037
|
+
const cols = node.columnsCount || 2;
|
|
4038
|
+
const contentWidth = Math.max(
|
|
4039
|
+
1e3,
|
|
4040
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4041
|
+
);
|
|
4042
|
+
const cellWidthDxa = Math.floor(contentWidth / cols);
|
|
4043
|
+
const cells = [];
|
|
4044
|
+
for (const col of node.children || []) {
|
|
4045
|
+
const colParagraphs = [];
|
|
4046
|
+
for (const childNode of col.children || []) {
|
|
4047
|
+
if (childNode.type === "heading") {
|
|
4048
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, bold: true, size: 24, color: primaryDarkHex });
|
|
4049
|
+
colParagraphs.push(new import_docx.Paragraph({ children: runs, spacing: { before: 120, after: 60 } }));
|
|
4050
|
+
} else if (childNode.type === "paragraph") {
|
|
4051
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
4052
|
+
colParagraphs.push(new import_docx.Paragraph({ children: runs, spacing: { after: 100 } }));
|
|
4053
|
+
} else if (childNode.type === "list" && childNode.children) {
|
|
4054
|
+
for (const item of childNode.children) {
|
|
4055
|
+
const runs = await convertInlinesToTextRuns(item.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
4056
|
+
colParagraphs.push(new import_docx.Paragraph({ children: [new import_docx.TextRun({ text: "\u2022 ", font: defaultFont, color: primaryHex }), ...runs], spacing: { after: 40 } }));
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
if (colParagraphs.length === 0) colParagraphs.push(new import_docx.Paragraph({}));
|
|
4061
|
+
cells.push(
|
|
4062
|
+
new import_docx.TableCell({
|
|
4063
|
+
width: { size: cellWidthDxa, type: import_docx.WidthType.DXA },
|
|
4064
|
+
borders: {
|
|
4065
|
+
top: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4066
|
+
bottom: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4067
|
+
left: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4068
|
+
right: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" }
|
|
4069
|
+
},
|
|
4070
|
+
margins: { top: 60, bottom: 60, left: 100, right: 100 },
|
|
4071
|
+
children: colParagraphs
|
|
4072
|
+
})
|
|
4073
|
+
);
|
|
4074
|
+
}
|
|
4075
|
+
docElements.push(
|
|
4076
|
+
new import_docx.Table({
|
|
4077
|
+
width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
|
|
4078
|
+
rows: [new import_docx.TableRow({ children: cells })]
|
|
4079
|
+
})
|
|
4080
|
+
);
|
|
4081
|
+
docElements.push(new import_docx.Paragraph({ spacing: { after: 120 } }));
|
|
4082
|
+
continue;
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
4086
|
+
docElements.push(
|
|
4087
|
+
new import_docx.Paragraph({
|
|
4088
|
+
border: {
|
|
4089
|
+
top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex, space: 8 }
|
|
4090
|
+
},
|
|
4091
|
+
spacing: { before: 360, after: 120 }
|
|
4092
|
+
})
|
|
4093
|
+
);
|
|
4094
|
+
for (const def of doc.footnoteDefs) {
|
|
4095
|
+
const defRuns = await convertInlinesToTextRuns(def.inlines, baseDir, {
|
|
4096
|
+
font: defaultFont,
|
|
4097
|
+
size: 18,
|
|
4098
|
+
// 9pt
|
|
4099
|
+
color: textMutedHex
|
|
4100
|
+
});
|
|
4101
|
+
docElements.push(
|
|
4102
|
+
new import_docx.Paragraph({
|
|
4103
|
+
children: [
|
|
4104
|
+
new import_docx.TextRun({
|
|
4105
|
+
text: `[${def.id}] `,
|
|
4106
|
+
bold: true,
|
|
4107
|
+
color: primaryDarkHex,
|
|
4108
|
+
font: defaultFont,
|
|
4109
|
+
size: 18
|
|
4110
|
+
}),
|
|
4111
|
+
...defRuns
|
|
4112
|
+
],
|
|
4113
|
+
spacing: { after: 60 }
|
|
4114
|
+
})
|
|
4115
|
+
);
|
|
4116
|
+
}
|
|
4117
|
+
}
|
|
4118
|
+
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
4119
|
+
const sig = resolved.signatures;
|
|
4120
|
+
const numItems = sig.items.length;
|
|
4121
|
+
const contentWidth = Math.max(
|
|
4122
|
+
1e3,
|
|
3034
4123
|
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
3035
4124
|
);
|
|
3036
4125
|
docElements.push(new import_docx.Paragraph({ spacing: { before: sig.spacingBeforeTwip } }));
|
|
@@ -3086,7 +4175,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3086
4175
|
const centerPos = Math.round(contentWidthTwip / 2);
|
|
3087
4176
|
const rightPos = contentWidthTwip;
|
|
3088
4177
|
const headerRuns = [];
|
|
3089
|
-
if ((
|
|
4178
|
+
if ((_e = resolved.header) == null ? void 0 : _e.left) {
|
|
3090
4179
|
headerRuns.push(
|
|
3091
4180
|
new import_docx.TextRun({
|
|
3092
4181
|
text: resolved.header.left.text,
|
|
@@ -3099,7 +4188,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3099
4188
|
);
|
|
3100
4189
|
}
|
|
3101
4190
|
headerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3102
|
-
if ((
|
|
4191
|
+
if ((_f = resolved.header) == null ? void 0 : _f.center) {
|
|
3103
4192
|
headerRuns.push(
|
|
3104
4193
|
new import_docx.TextRun({
|
|
3105
4194
|
text: resolved.header.center.text,
|
|
@@ -3112,7 +4201,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3112
4201
|
);
|
|
3113
4202
|
}
|
|
3114
4203
|
headerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3115
|
-
if ((
|
|
4204
|
+
if ((_g = resolved.header) == null ? void 0 : _g.right) {
|
|
3116
4205
|
headerRuns.push(
|
|
3117
4206
|
new import_docx.TextRun({
|
|
3118
4207
|
text: resolved.header.right.text,
|
|
@@ -3151,7 +4240,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3151
4240
|
]
|
|
3152
4241
|
}) : void 0;
|
|
3153
4242
|
const footerRuns = [];
|
|
3154
|
-
if ((
|
|
4243
|
+
if ((_h = resolved.footer) == null ? void 0 : _h.left) {
|
|
3155
4244
|
footerRuns.push(
|
|
3156
4245
|
new import_docx.TextRun({
|
|
3157
4246
|
text: resolved.footer.left.text,
|
|
@@ -3164,7 +4253,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3164
4253
|
);
|
|
3165
4254
|
}
|
|
3166
4255
|
footerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3167
|
-
if ((
|
|
4256
|
+
if ((_i = resolved.footer) == null ? void 0 : _i.center) {
|
|
3168
4257
|
footerRuns.push(
|
|
3169
4258
|
new import_docx.TextRun({
|
|
3170
4259
|
text: resolved.footer.center.text,
|
|
@@ -3177,7 +4266,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3177
4266
|
);
|
|
3178
4267
|
}
|
|
3179
4268
|
footerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3180
|
-
if ((
|
|
4269
|
+
if ((_j = resolved.footer) == null ? void 0 : _j.right) {
|
|
3181
4270
|
const rZone = resolved.footer.right;
|
|
3182
4271
|
const rColor = rZone.color.replace("#", "");
|
|
3183
4272
|
const rSize = (rZone.fontSize || 9) * 2;
|
|
@@ -3262,6 +4351,91 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3262
4351
|
]
|
|
3263
4352
|
}) : void 0;
|
|
3264
4353
|
const isLandscape = resolved.orientation === "landscape";
|
|
4354
|
+
const docSections = [];
|
|
4355
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
4356
|
+
const coverElements = await buildDocxCoverPageElements(
|
|
4357
|
+
resolved.coverPage,
|
|
4358
|
+
defaultFont,
|
|
4359
|
+
textHex,
|
|
4360
|
+
primaryHex,
|
|
4361
|
+
primaryDarkHex,
|
|
4362
|
+
textMutedHex,
|
|
4363
|
+
baseDir
|
|
4364
|
+
);
|
|
4365
|
+
docSections.push({
|
|
4366
|
+
properties: {
|
|
4367
|
+
page: {
|
|
4368
|
+
size: {
|
|
4369
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4370
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4371
|
+
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
4372
|
+
},
|
|
4373
|
+
margin: {
|
|
4374
|
+
top: resolved.margins.topTwip,
|
|
4375
|
+
bottom: resolved.margins.bottomTwip,
|
|
4376
|
+
left: resolved.margins.leftTwip,
|
|
4377
|
+
right: resolved.margins.rightTwip
|
|
4378
|
+
}
|
|
4379
|
+
}
|
|
4380
|
+
},
|
|
4381
|
+
headers: void 0,
|
|
4382
|
+
footers: void 0,
|
|
4383
|
+
children: coverElements
|
|
4384
|
+
});
|
|
4385
|
+
}
|
|
4386
|
+
docSections.push({
|
|
4387
|
+
properties: {
|
|
4388
|
+
page: {
|
|
4389
|
+
size: {
|
|
4390
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4391
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4392
|
+
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
4393
|
+
},
|
|
4394
|
+
margin: {
|
|
4395
|
+
top: resolved.margins.topTwip,
|
|
4396
|
+
bottom: resolved.margins.bottomTwip,
|
|
4397
|
+
left: resolved.margins.leftTwip,
|
|
4398
|
+
right: resolved.margins.rightTwip,
|
|
4399
|
+
header: 720,
|
|
4400
|
+
footer: 720
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
},
|
|
4404
|
+
headers: docHeader ? { default: docHeader } : void 0,
|
|
4405
|
+
footers: docFooter ? { default: docFooter } : void 0,
|
|
4406
|
+
children: docElements
|
|
4407
|
+
});
|
|
4408
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
4409
|
+
const backElements = await buildDocxBackCoverElements(
|
|
4410
|
+
resolved.backCover,
|
|
4411
|
+
defaultFont,
|
|
4412
|
+
textHex,
|
|
4413
|
+
primaryHex,
|
|
4414
|
+
primaryDarkHex,
|
|
4415
|
+
textMutedHex,
|
|
4416
|
+
baseDir
|
|
4417
|
+
);
|
|
4418
|
+
docSections.push({
|
|
4419
|
+
properties: {
|
|
4420
|
+
page: {
|
|
4421
|
+
size: {
|
|
4422
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4423
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4424
|
+
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
4425
|
+
},
|
|
4426
|
+
margin: {
|
|
4427
|
+
top: resolved.margins.topTwip,
|
|
4428
|
+
bottom: resolved.margins.bottomTwip,
|
|
4429
|
+
left: resolved.margins.leftTwip,
|
|
4430
|
+
right: resolved.margins.rightTwip
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
},
|
|
4434
|
+
headers: void 0,
|
|
4435
|
+
footers: void 0,
|
|
4436
|
+
children: backElements
|
|
4437
|
+
});
|
|
4438
|
+
}
|
|
3265
4439
|
const document = new import_docx.Document({
|
|
3266
4440
|
styles: {
|
|
3267
4441
|
default: {
|
|
@@ -3282,33 +4456,250 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3282
4456
|
}
|
|
3283
4457
|
}
|
|
3284
4458
|
},
|
|
3285
|
-
sections:
|
|
3286
|
-
{
|
|
3287
|
-
properties: {
|
|
3288
|
-
page: {
|
|
3289
|
-
size: {
|
|
3290
|
-
width: resolved.paperDimensions.widthTwip,
|
|
3291
|
-
height: resolved.paperDimensions.heightTwip,
|
|
3292
|
-
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
3293
|
-
},
|
|
3294
|
-
margin: {
|
|
3295
|
-
top: resolved.margins.topTwip,
|
|
3296
|
-
bottom: resolved.margins.bottomTwip,
|
|
3297
|
-
left: resolved.margins.leftTwip,
|
|
3298
|
-
right: resolved.margins.rightTwip,
|
|
3299
|
-
header: 720,
|
|
3300
|
-
footer: 720
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
3303
|
-
},
|
|
3304
|
-
headers: docHeader ? { default: docHeader } : void 0,
|
|
3305
|
-
footers: docFooter ? { default: docFooter } : void 0,
|
|
3306
|
-
children: docElements
|
|
3307
|
-
}
|
|
3308
|
-
]
|
|
4459
|
+
sections: docSections
|
|
3309
4460
|
});
|
|
3310
4461
|
return await import_docx.Packer.toBuffer(document);
|
|
3311
4462
|
}
|
|
4463
|
+
async function buildDocxBackCoverElements(backCover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4464
|
+
var _a;
|
|
4465
|
+
const elements = [];
|
|
4466
|
+
elements.push(new import_docx.Paragraph({ spacing: { before: 1800 } }));
|
|
4467
|
+
if (backCover.logo) {
|
|
4468
|
+
const resolvedLogo = await resolveImage(backCover.logo, baseDir);
|
|
4469
|
+
if (resolvedLogo) {
|
|
4470
|
+
const logoW = typeof backCover.logoWidth === "number" ? backCover.logoWidth : 140;
|
|
4471
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4472
|
+
elements.push(
|
|
4473
|
+
new import_docx.Paragraph({
|
|
4474
|
+
children: [
|
|
4475
|
+
new import_docx.ImageRun({
|
|
4476
|
+
data: resolvedLogo.buffer,
|
|
4477
|
+
transformation: {
|
|
4478
|
+
width: logoW,
|
|
4479
|
+
height: Math.round(logoW * 0.75)
|
|
4480
|
+
},
|
|
4481
|
+
type: logoType
|
|
4482
|
+
})
|
|
4483
|
+
],
|
|
4484
|
+
spacing: { after: 240 }
|
|
4485
|
+
})
|
|
4486
|
+
);
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
if (backCover.badge) {
|
|
4490
|
+
elements.push(
|
|
4491
|
+
new import_docx.Paragraph({
|
|
4492
|
+
children: [
|
|
4493
|
+
new import_docx.TextRun({
|
|
4494
|
+
text: `[ ${backCover.badge.toUpperCase()} ]`,
|
|
4495
|
+
font: defaultFont,
|
|
4496
|
+
size: 20,
|
|
4497
|
+
bold: true,
|
|
4498
|
+
color: primaryDarkHex
|
|
4499
|
+
})
|
|
4500
|
+
],
|
|
4501
|
+
spacing: { after: 240 }
|
|
4502
|
+
})
|
|
4503
|
+
);
|
|
4504
|
+
}
|
|
4505
|
+
elements.push(
|
|
4506
|
+
new import_docx.Paragraph({
|
|
4507
|
+
children: [
|
|
4508
|
+
new import_docx.TextRun({
|
|
4509
|
+
text: backCover.title,
|
|
4510
|
+
font: defaultFont,
|
|
4511
|
+
size: 52,
|
|
4512
|
+
// 26pt
|
|
4513
|
+
bold: true,
|
|
4514
|
+
color: textHex
|
|
4515
|
+
})
|
|
4516
|
+
],
|
|
4517
|
+
spacing: { after: 140 }
|
|
4518
|
+
})
|
|
4519
|
+
);
|
|
4520
|
+
if (backCover.subtitle) {
|
|
4521
|
+
elements.push(
|
|
4522
|
+
new import_docx.Paragraph({
|
|
4523
|
+
children: [
|
|
4524
|
+
new import_docx.TextRun({
|
|
4525
|
+
text: backCover.subtitle,
|
|
4526
|
+
font: defaultFont,
|
|
4527
|
+
size: 24,
|
|
4528
|
+
// 12pt
|
|
4529
|
+
color: textMutedHex
|
|
4530
|
+
})
|
|
4531
|
+
],
|
|
4532
|
+
spacing: { after: 480 }
|
|
4533
|
+
})
|
|
4534
|
+
);
|
|
4535
|
+
}
|
|
4536
|
+
elements.push(
|
|
4537
|
+
new import_docx.Paragraph({
|
|
4538
|
+
border: {
|
|
4539
|
+
bottom: { style: import_docx.BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4540
|
+
},
|
|
4541
|
+
spacing: { after: 480 }
|
|
4542
|
+
})
|
|
4543
|
+
);
|
|
4544
|
+
const contactRuns = [];
|
|
4545
|
+
if (backCover.company) contactRuns.push(new import_docx.TextRun({ text: `Organization: ${backCover.company}
|
|
4546
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4547
|
+
if (backCover.address) contactRuns.push(new import_docx.TextRun({ text: `Address: ${backCover.address}
|
|
4548
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4549
|
+
if (backCover.email) contactRuns.push(new import_docx.TextRun({ text: `Email: ${backCover.email}
|
|
4550
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4551
|
+
if (backCover.phone) contactRuns.push(new import_docx.TextRun({ text: `Phone: ${backCover.phone}
|
|
4552
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4553
|
+
if (backCover.website) contactRuns.push(new import_docx.TextRun({ text: `Website: ${backCover.website}
|
|
4554
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4555
|
+
if (backCover.social) {
|
|
4556
|
+
for (const [net, url] of Object.entries(backCover.social)) {
|
|
4557
|
+
if (url) {
|
|
4558
|
+
contactRuns.push(new import_docx.TextRun({ text: `${net.toUpperCase()}: ${url}
|
|
4559
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
if (contactRuns.length > 0) {
|
|
4564
|
+
elements.push(
|
|
4565
|
+
new import_docx.Paragraph({
|
|
4566
|
+
children: contactRuns,
|
|
4567
|
+
spacing: { before: 360, after: 360 }
|
|
4568
|
+
})
|
|
4569
|
+
);
|
|
4570
|
+
}
|
|
4571
|
+
if (backCover.copyright) {
|
|
4572
|
+
elements.push(
|
|
4573
|
+
new import_docx.Paragraph({
|
|
4574
|
+
children: [
|
|
4575
|
+
new import_docx.TextRun({
|
|
4576
|
+
text: backCover.copyright,
|
|
4577
|
+
font: defaultFont,
|
|
4578
|
+
size: 18,
|
|
4579
|
+
color: "94A3B8"
|
|
4580
|
+
})
|
|
4581
|
+
],
|
|
4582
|
+
spacing: { before: 720 }
|
|
4583
|
+
})
|
|
4584
|
+
);
|
|
4585
|
+
}
|
|
4586
|
+
return elements;
|
|
4587
|
+
}
|
|
4588
|
+
async function buildDocxCoverPageElements(cover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4589
|
+
var _a;
|
|
4590
|
+
const elements = [];
|
|
4591
|
+
elements.push(new import_docx.Paragraph({ spacing: { before: 1800 } }));
|
|
4592
|
+
if (cover.logo) {
|
|
4593
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
4594
|
+
if (resolvedLogo) {
|
|
4595
|
+
const logoW = typeof cover.logoWidth === "number" ? cover.logoWidth : 140;
|
|
4596
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4597
|
+
elements.push(
|
|
4598
|
+
new import_docx.Paragraph({
|
|
4599
|
+
children: [
|
|
4600
|
+
new import_docx.ImageRun({
|
|
4601
|
+
data: resolvedLogo.buffer,
|
|
4602
|
+
transformation: {
|
|
4603
|
+
width: logoW,
|
|
4604
|
+
height: Math.round(logoW * 0.75)
|
|
4605
|
+
},
|
|
4606
|
+
type: logoType
|
|
4607
|
+
})
|
|
4608
|
+
],
|
|
4609
|
+
spacing: { after: 240 }
|
|
4610
|
+
})
|
|
4611
|
+
);
|
|
4612
|
+
}
|
|
4613
|
+
}
|
|
4614
|
+
if (cover.badge) {
|
|
4615
|
+
elements.push(
|
|
4616
|
+
new import_docx.Paragraph({
|
|
4617
|
+
children: [
|
|
4618
|
+
new import_docx.TextRun({
|
|
4619
|
+
text: `[ ${cover.badge.toUpperCase()} ]`,
|
|
4620
|
+
font: defaultFont,
|
|
4621
|
+
size: 20,
|
|
4622
|
+
bold: true,
|
|
4623
|
+
color: primaryDarkHex
|
|
4624
|
+
})
|
|
4625
|
+
],
|
|
4626
|
+
spacing: { after: 240 }
|
|
4627
|
+
})
|
|
4628
|
+
);
|
|
4629
|
+
}
|
|
4630
|
+
elements.push(
|
|
4631
|
+
new import_docx.Paragraph({
|
|
4632
|
+
children: [
|
|
4633
|
+
new import_docx.TextRun({
|
|
4634
|
+
text: cover.title,
|
|
4635
|
+
font: defaultFont,
|
|
4636
|
+
size: 56,
|
|
4637
|
+
// 28pt
|
|
4638
|
+
bold: true,
|
|
4639
|
+
color: textHex
|
|
4640
|
+
})
|
|
4641
|
+
],
|
|
4642
|
+
spacing: { after: 140 }
|
|
4643
|
+
})
|
|
4644
|
+
);
|
|
4645
|
+
if (cover.subtitle) {
|
|
4646
|
+
elements.push(
|
|
4647
|
+
new import_docx.Paragraph({
|
|
4648
|
+
children: [
|
|
4649
|
+
new import_docx.TextRun({
|
|
4650
|
+
text: cover.subtitle,
|
|
4651
|
+
font: defaultFont,
|
|
4652
|
+
size: 26,
|
|
4653
|
+
// 13pt
|
|
4654
|
+
color: textMutedHex
|
|
4655
|
+
})
|
|
4656
|
+
],
|
|
4657
|
+
spacing: { after: 480 }
|
|
4658
|
+
})
|
|
4659
|
+
);
|
|
4660
|
+
}
|
|
4661
|
+
elements.push(
|
|
4662
|
+
new import_docx.Paragraph({
|
|
4663
|
+
border: {
|
|
4664
|
+
bottom: { style: import_docx.BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4665
|
+
},
|
|
4666
|
+
spacing: { after: 480 }
|
|
4667
|
+
})
|
|
4668
|
+
);
|
|
4669
|
+
if (cover.company || cover.author || cover.version || cover.date) {
|
|
4670
|
+
const metaRuns = [];
|
|
4671
|
+
if (cover.company) metaRuns.push(new import_docx.TextRun({ text: `Organization: ${cover.company}
|
|
4672
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4673
|
+
if (cover.author) metaRuns.push(new import_docx.TextRun({ text: `Author: ${cover.author}
|
|
4674
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4675
|
+
if (cover.version) metaRuns.push(new import_docx.TextRun({ text: `Version: ${cover.version}
|
|
4676
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4677
|
+
if (cover.date) metaRuns.push(new import_docx.TextRun({ text: `Date: ${cover.date}
|
|
4678
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4679
|
+
elements.push(
|
|
4680
|
+
new import_docx.Paragraph({
|
|
4681
|
+
children: metaRuns,
|
|
4682
|
+
spacing: { before: 360, after: 360 }
|
|
4683
|
+
})
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
if (cover.footerText) {
|
|
4687
|
+
elements.push(
|
|
4688
|
+
new import_docx.Paragraph({
|
|
4689
|
+
children: [
|
|
4690
|
+
new import_docx.TextRun({
|
|
4691
|
+
text: cover.footerText,
|
|
4692
|
+
font: defaultFont,
|
|
4693
|
+
size: 18,
|
|
4694
|
+
color: "94A3B8"
|
|
4695
|
+
})
|
|
4696
|
+
],
|
|
4697
|
+
spacing: { before: 720 }
|
|
4698
|
+
})
|
|
4699
|
+
);
|
|
4700
|
+
}
|
|
4701
|
+
return elements;
|
|
4702
|
+
}
|
|
3312
4703
|
async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
|
|
3313
4704
|
const cellParagraphs = [];
|
|
3314
4705
|
if (item.title) {
|
|
@@ -3536,14 +4927,885 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
|
|
|
3536
4927
|
};
|
|
3537
4928
|
}
|
|
3538
4929
|
|
|
4930
|
+
// src/server/previewServer.ts
|
|
4931
|
+
var http = __toESM(require("http"));
|
|
4932
|
+
var fs7 = __toESM(require("fs"));
|
|
4933
|
+
var path7 = __toESM(require("path"));
|
|
4934
|
+
async function startPreviewServer(options) {
|
|
4935
|
+
const absoluteFilePath = path7.resolve(process.cwd(), options.filePath);
|
|
4936
|
+
if (!fs7.existsSync(absoluteFilePath)) {
|
|
4937
|
+
throw new Error(`MarkForge preview error: File not found at "${absoluteFilePath}"`);
|
|
4938
|
+
}
|
|
4939
|
+
const baseDir = path7.dirname(absoluteFilePath);
|
|
4940
|
+
const { config: fileConfig } = await loadConfig(void 0, baseDir);
|
|
4941
|
+
const baseConfig = options.config || fileConfig;
|
|
4942
|
+
const port = options.port || 3e3;
|
|
4943
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
4944
|
+
const broadcastReload = () => {
|
|
4945
|
+
sseClients.forEach((client) => {
|
|
4946
|
+
try {
|
|
4947
|
+
client.write(`event: reload
|
|
4948
|
+
data: ${Date.now()}
|
|
4949
|
+
|
|
4950
|
+
`);
|
|
4951
|
+
} catch {
|
|
4952
|
+
sseClients.delete(client);
|
|
4953
|
+
}
|
|
4954
|
+
});
|
|
4955
|
+
};
|
|
4956
|
+
let debounceTimer = null;
|
|
4957
|
+
const watcher = fs7.watch(baseDir, { recursive: false }, (_event, filename) => {
|
|
4958
|
+
if (!filename) return;
|
|
4959
|
+
const changedPath = path7.resolve(baseDir, filename);
|
|
4960
|
+
if (changedPath === absoluteFilePath || filename.includes("markforge") || filename.endsWith(".css")) {
|
|
4961
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
4962
|
+
debounceTimer = setTimeout(() => {
|
|
4963
|
+
broadcastReload();
|
|
4964
|
+
}, 150);
|
|
4965
|
+
}
|
|
4966
|
+
});
|
|
4967
|
+
const server = http.createServer(async (req, res) => {
|
|
4968
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
4969
|
+
if (url.pathname === "/events") {
|
|
4970
|
+
res.writeHead(200, {
|
|
4971
|
+
"Content-Type": "text/event-stream",
|
|
4972
|
+
"Cache-Control": "no-cache, no-transform",
|
|
4973
|
+
Connection: "keep-alive"
|
|
4974
|
+
});
|
|
4975
|
+
res.write(`data: connected
|
|
4976
|
+
|
|
4977
|
+
`);
|
|
4978
|
+
sseClients.add(res);
|
|
4979
|
+
req.on("close", () => {
|
|
4980
|
+
sseClients.delete(res);
|
|
4981
|
+
});
|
|
4982
|
+
return;
|
|
4983
|
+
}
|
|
4984
|
+
if (url.pathname === "/api/file-content" && req.method === "GET") {
|
|
4985
|
+
try {
|
|
4986
|
+
const content = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
4987
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4988
|
+
res.end(
|
|
4989
|
+
JSON.stringify({
|
|
4990
|
+
content,
|
|
4991
|
+
fileName: path7.basename(absoluteFilePath),
|
|
4992
|
+
filePath: absoluteFilePath
|
|
4993
|
+
})
|
|
4994
|
+
);
|
|
4995
|
+
} catch (err) {
|
|
4996
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4997
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
4998
|
+
res.end(JSON.stringify({ error: msg }));
|
|
4999
|
+
}
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
5002
|
+
if (url.pathname === "/api/save-content" && req.method === "POST") {
|
|
5003
|
+
let body = "";
|
|
5004
|
+
req.on("data", (chunk) => {
|
|
5005
|
+
body += chunk;
|
|
5006
|
+
});
|
|
5007
|
+
req.on("end", () => {
|
|
5008
|
+
try {
|
|
5009
|
+
const parsed = JSON.parse(body);
|
|
5010
|
+
if (typeof parsed.content === "string") {
|
|
5011
|
+
fs7.writeFileSync(absoluteFilePath, parsed.content, "utf-8");
|
|
5012
|
+
broadcastReload();
|
|
5013
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5014
|
+
res.end(JSON.stringify({ success: true, savedAt: Date.now() }));
|
|
5015
|
+
} else {
|
|
5016
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
5017
|
+
res.end(JSON.stringify({ error: "Missing content field in request body" }));
|
|
5018
|
+
}
|
|
5019
|
+
} catch (err) {
|
|
5020
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5021
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
5022
|
+
res.end(JSON.stringify({ error: msg }));
|
|
5023
|
+
}
|
|
5024
|
+
});
|
|
5025
|
+
return;
|
|
5026
|
+
}
|
|
5027
|
+
if (url.pathname === "/api/export" && (req.method === "GET" || req.method === "POST")) {
|
|
5028
|
+
const format = url.searchParams.get("format") || "docx";
|
|
5029
|
+
try {
|
|
5030
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5031
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
5032
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
5033
|
+
const mergedConfig = { ...baseConfig, ...resolvedConfig };
|
|
5034
|
+
const fileBase = path7.basename(absoluteFilePath, path7.extname(absoluteFilePath));
|
|
5035
|
+
if (format === "docx") {
|
|
5036
|
+
const buffer = await buildDocxDocument(doc, mergedConfig, baseDir);
|
|
5037
|
+
res.writeHead(200, {
|
|
5038
|
+
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
5039
|
+
"Content-Disposition": `attachment; filename="${fileBase}.docx"`
|
|
5040
|
+
});
|
|
5041
|
+
res.end(buffer);
|
|
5042
|
+
return;
|
|
5043
|
+
} else if (format === "pdf") {
|
|
5044
|
+
const buffer = await buildPdfDocument(doc, mergedConfig, baseDir);
|
|
5045
|
+
res.writeHead(200, {
|
|
5046
|
+
"Content-Type": "application/pdf",
|
|
5047
|
+
"Content-Disposition": `attachment; filename="${fileBase}.pdf"`
|
|
5048
|
+
});
|
|
5049
|
+
res.end(buffer);
|
|
5050
|
+
return;
|
|
5051
|
+
} else {
|
|
5052
|
+
const html = await buildHtmlDocument(doc, mergedConfig, baseDir);
|
|
5053
|
+
res.writeHead(200, {
|
|
5054
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
5055
|
+
"Content-Disposition": `attachment; filename="${fileBase}.html"`
|
|
5056
|
+
});
|
|
5057
|
+
res.end(html);
|
|
5058
|
+
return;
|
|
5059
|
+
}
|
|
5060
|
+
} catch (err) {
|
|
5061
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5062
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
5063
|
+
res.end(`Export failed: ${msg}`);
|
|
5064
|
+
return;
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
if (url.pathname === "/document-content") {
|
|
5068
|
+
try {
|
|
5069
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5070
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
5071
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
5072
|
+
const html = await buildHtmlDocument(doc, { ...baseConfig, ...resolvedConfig }, baseDir);
|
|
5073
|
+
const injectedScript = `
|
|
5074
|
+
<script>
|
|
5075
|
+
(function() {
|
|
5076
|
+
var evtSource = new EventSource('/events');
|
|
5077
|
+
evtSource.addEventListener('reload', function() {
|
|
5078
|
+
var scrollPos = window.scrollY;
|
|
5079
|
+
sessionStorage.setItem('markforge_scroll', scrollPos);
|
|
5080
|
+
window.location.reload();
|
|
5081
|
+
});
|
|
5082
|
+
window.addEventListener('load', function() {
|
|
5083
|
+
var saved = sessionStorage.getItem('markforge_scroll');
|
|
5084
|
+
if (saved) {
|
|
5085
|
+
window.scrollTo(0, parseInt(saved, 10));
|
|
5086
|
+
}
|
|
5087
|
+
});
|
|
5088
|
+
})();
|
|
5089
|
+
</script>
|
|
5090
|
+
`;
|
|
5091
|
+
const finalHtml = html.replace("</body>", `${injectedScript}</body>`);
|
|
5092
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5093
|
+
res.end(finalHtml);
|
|
5094
|
+
} catch (err) {
|
|
5095
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5096
|
+
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
5097
|
+
res.end(`<div style="padding:2rem;font-family:sans-serif;color:#ef4444;background:#fef2f2;border:1px solid #f87171;border-radius:8px;"><h3>MarkForge Compilation Error</h3><pre>${escapeHtml2(msg)}</pre></div>`);
|
|
5098
|
+
}
|
|
5099
|
+
return;
|
|
5100
|
+
}
|
|
5101
|
+
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
5102
|
+
const fileName = path7.basename(absoluteFilePath);
|
|
5103
|
+
const initialContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5104
|
+
const appHtml = `<!DOCTYPE html>
|
|
5105
|
+
<html lang="en">
|
|
5106
|
+
<head>
|
|
5107
|
+
<meta charset="UTF-8">
|
|
5108
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5109
|
+
<title>MarkForge Live Studio - ${escapeHtml2(fileName)}</title>
|
|
5110
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
5111
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
5112
|
+
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
5113
|
+
<style>
|
|
5114
|
+
:root {
|
|
5115
|
+
--mf-primary: #0D998D;
|
|
5116
|
+
--mf-primary-dark: #008277;
|
|
5117
|
+
--mf-primary-light: #ECFDFD;
|
|
5118
|
+
--mf-primary-border: #33CDCF;
|
|
5119
|
+
--mf-dark: #0F172A;
|
|
5120
|
+
--mf-slate: #1E293B;
|
|
5121
|
+
--mf-editor-bg: #0F172A;
|
|
5122
|
+
--mf-editor-gutter: #1E293B;
|
|
5123
|
+
--mf-editor-text: #F8FAFC;
|
|
5124
|
+
--mf-muted: #64748B;
|
|
5125
|
+
--mf-light-border: #E2E8F0;
|
|
5126
|
+
--mf-bg: #F1F5F9;
|
|
5127
|
+
}
|
|
5128
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5129
|
+
body {
|
|
5130
|
+
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
5131
|
+
background: var(--mf-bg);
|
|
5132
|
+
color: var(--mf-dark);
|
|
5133
|
+
display: flex;
|
|
5134
|
+
flex-direction: column;
|
|
5135
|
+
height: 100vh;
|
|
5136
|
+
overflow: hidden;
|
|
5137
|
+
}
|
|
5138
|
+
header {
|
|
5139
|
+
background: #FFFFFF;
|
|
5140
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5141
|
+
min-height: 56px;
|
|
5142
|
+
display: flex;
|
|
5143
|
+
flex-wrap: wrap;
|
|
5144
|
+
align-items: center;
|
|
5145
|
+
justify-content: space-between;
|
|
5146
|
+
padding: 0.4rem 1.2rem;
|
|
5147
|
+
z-index: 10;
|
|
5148
|
+
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
|
5149
|
+
gap: 0.75rem;
|
|
5150
|
+
}
|
|
5151
|
+
.brand-section {
|
|
5152
|
+
display: flex;
|
|
5153
|
+
align-items: center;
|
|
5154
|
+
gap: 0.75rem;
|
|
5155
|
+
}
|
|
5156
|
+
.brand-badge {
|
|
5157
|
+
font-size: 0.72rem;
|
|
5158
|
+
font-weight: 800;
|
|
5159
|
+
letter-spacing: 0.08em;
|
|
5160
|
+
background: var(--mf-dark);
|
|
5161
|
+
color: #FFFFFF;
|
|
5162
|
+
padding: 0.25rem 0.55rem;
|
|
5163
|
+
border-radius: 4px;
|
|
5164
|
+
text-transform: uppercase;
|
|
5165
|
+
}
|
|
5166
|
+
.file-name {
|
|
5167
|
+
font-size: 0.9rem;
|
|
5168
|
+
font-weight: 700;
|
|
5169
|
+
color: var(--mf-dark);
|
|
5170
|
+
}
|
|
5171
|
+
.sync-status {
|
|
5172
|
+
display: flex;
|
|
5173
|
+
align-items: center;
|
|
5174
|
+
gap: 0.35rem;
|
|
5175
|
+
font-size: 0.75rem;
|
|
5176
|
+
font-weight: 600;
|
|
5177
|
+
color: var(--mf-primary-dark);
|
|
5178
|
+
background: var(--mf-primary-light);
|
|
5179
|
+
padding: 0.2rem 0.55rem;
|
|
5180
|
+
border-radius: 9999px;
|
|
5181
|
+
border: 1px solid var(--mf-primary-border);
|
|
5182
|
+
}
|
|
5183
|
+
.sync-dot {
|
|
5184
|
+
width: 7px;
|
|
5185
|
+
height: 7px;
|
|
5186
|
+
background-color: var(--mf-primary);
|
|
5187
|
+
border-radius: 50%;
|
|
5188
|
+
box-shadow: 0 0 0 2px rgba(13, 153, 141, 0.2);
|
|
5189
|
+
}
|
|
5190
|
+
.toolbar-section {
|
|
5191
|
+
display: flex;
|
|
5192
|
+
align-items: center;
|
|
5193
|
+
gap: 0.3rem;
|
|
5194
|
+
background: #F8FAFC;
|
|
5195
|
+
padding: 0.25rem 0.4rem;
|
|
5196
|
+
border-radius: 6px;
|
|
5197
|
+
border: 1px solid var(--mf-light-border);
|
|
5198
|
+
}
|
|
5199
|
+
.tool-btn {
|
|
5200
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5201
|
+
font-size: 0.75rem;
|
|
5202
|
+
font-weight: 600;
|
|
5203
|
+
padding: 0.25rem 0.45rem;
|
|
5204
|
+
background: transparent;
|
|
5205
|
+
border: 1px solid transparent;
|
|
5206
|
+
border-radius: 4px;
|
|
5207
|
+
cursor: pointer;
|
|
5208
|
+
color: var(--mf-slate);
|
|
5209
|
+
transition: all 0.1s ease;
|
|
5210
|
+
}
|
|
5211
|
+
.tool-btn:hover {
|
|
5212
|
+
background: #FFFFFF;
|
|
5213
|
+
border-color: var(--mf-light-border);
|
|
5214
|
+
color: var(--mf-primary-dark);
|
|
5215
|
+
}
|
|
5216
|
+
.tool-divider {
|
|
5217
|
+
width: 1px;
|
|
5218
|
+
height: 16px;
|
|
5219
|
+
background: var(--mf-light-border);
|
|
5220
|
+
margin: 0 0.15rem;
|
|
5221
|
+
}
|
|
5222
|
+
.controls {
|
|
5223
|
+
display: flex;
|
|
5224
|
+
align-items: center;
|
|
5225
|
+
gap: 0.5rem;
|
|
5226
|
+
}
|
|
5227
|
+
.view-toggles {
|
|
5228
|
+
display: flex;
|
|
5229
|
+
background: #F1F5F9;
|
|
5230
|
+
padding: 2px;
|
|
5231
|
+
border-radius: 6px;
|
|
5232
|
+
border: 1px solid var(--mf-light-border);
|
|
5233
|
+
}
|
|
5234
|
+
.toggle-btn {
|
|
5235
|
+
font-family: inherit;
|
|
5236
|
+
font-size: 0.74rem;
|
|
5237
|
+
font-weight: 600;
|
|
5238
|
+
padding: 0.25rem 0.55rem;
|
|
5239
|
+
border: none;
|
|
5240
|
+
background: transparent;
|
|
5241
|
+
border-radius: 4px;
|
|
5242
|
+
cursor: pointer;
|
|
5243
|
+
color: var(--mf-muted);
|
|
5244
|
+
transition: all 0.15s ease;
|
|
5245
|
+
}
|
|
5246
|
+
.toggle-btn.active {
|
|
5247
|
+
background: #FFFFFF;
|
|
5248
|
+
color: var(--mf-dark);
|
|
5249
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
|
5250
|
+
}
|
|
5251
|
+
.btn {
|
|
5252
|
+
font-family: inherit;
|
|
5253
|
+
font-size: 0.78rem;
|
|
5254
|
+
font-weight: 600;
|
|
5255
|
+
padding: 0.35rem 0.75rem;
|
|
5256
|
+
border-radius: 6px;
|
|
5257
|
+
cursor: pointer;
|
|
5258
|
+
text-decoration: none;
|
|
5259
|
+
transition: all 0.15s ease;
|
|
5260
|
+
display: inline-flex;
|
|
5261
|
+
align-items: center;
|
|
5262
|
+
gap: 0.3rem;
|
|
5263
|
+
border: 1px solid var(--mf-light-border);
|
|
5264
|
+
background: #FFFFFF;
|
|
5265
|
+
color: var(--mf-dark);
|
|
5266
|
+
}
|
|
5267
|
+
.btn:hover {
|
|
5268
|
+
background: #F8FAFC;
|
|
5269
|
+
border-color: #CBD5E1;
|
|
5270
|
+
}
|
|
5271
|
+
.btn-primary {
|
|
5272
|
+
background: var(--mf-primary);
|
|
5273
|
+
color: #FFFFFF;
|
|
5274
|
+
border-color: var(--mf-primary);
|
|
5275
|
+
}
|
|
5276
|
+
.btn-primary:hover {
|
|
5277
|
+
background: var(--mf-primary-dark);
|
|
5278
|
+
border-color: var(--mf-primary-dark);
|
|
5279
|
+
}
|
|
5280
|
+
.save-indicator {
|
|
5281
|
+
font-size: 0.75rem;
|
|
5282
|
+
font-weight: 600;
|
|
5283
|
+
color: var(--mf-muted);
|
|
5284
|
+
min-width: 65px;
|
|
5285
|
+
text-align: right;
|
|
5286
|
+
}
|
|
5287
|
+
.save-indicator.saved {
|
|
5288
|
+
color: var(--mf-primary-dark);
|
|
5289
|
+
}
|
|
5290
|
+
.save-indicator.saving {
|
|
5291
|
+
color: #D97706;
|
|
5292
|
+
}
|
|
5293
|
+
.save-indicator.unsaved {
|
|
5294
|
+
color: #E11D48;
|
|
5295
|
+
}
|
|
5296
|
+
|
|
5297
|
+
/* Main Workspace Splitter Layout */
|
|
5298
|
+
main.workspace {
|
|
5299
|
+
flex: 1;
|
|
5300
|
+
display: flex;
|
|
5301
|
+
height: calc(100vh - 56px);
|
|
5302
|
+
overflow: hidden;
|
|
5303
|
+
background: var(--mf-bg);
|
|
5304
|
+
position: relative;
|
|
5305
|
+
}
|
|
5306
|
+
.editor-pane {
|
|
5307
|
+
width: 50%;
|
|
5308
|
+
height: 100%;
|
|
5309
|
+
display: flex;
|
|
5310
|
+
flex-direction: column;
|
|
5311
|
+
background: var(--mf-editor-bg);
|
|
5312
|
+
border-right: 1px solid #334155;
|
|
5313
|
+
overflow: hidden;
|
|
5314
|
+
}
|
|
5315
|
+
.editor-header {
|
|
5316
|
+
background: #090D16;
|
|
5317
|
+
border-bottom: 1px solid #1E293B;
|
|
5318
|
+
padding: 0.4rem 0.8rem;
|
|
5319
|
+
display: flex;
|
|
5320
|
+
align-items: center;
|
|
5321
|
+
justify-content: space-between;
|
|
5322
|
+
color: #94A3B8;
|
|
5323
|
+
font-size: 0.74rem;
|
|
5324
|
+
font-weight: 500;
|
|
5325
|
+
}
|
|
5326
|
+
.editor-container {
|
|
5327
|
+
flex: 1;
|
|
5328
|
+
display: flex;
|
|
5329
|
+
position: relative;
|
|
5330
|
+
overflow: hidden;
|
|
5331
|
+
background: var(--mf-editor-bg);
|
|
5332
|
+
}
|
|
5333
|
+
.line-numbers {
|
|
5334
|
+
width: 44px;
|
|
5335
|
+
padding: 0.8rem 0.4rem;
|
|
5336
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5337
|
+
font-size: 13px;
|
|
5338
|
+
line-height: 1.55;
|
|
5339
|
+
color: #475569;
|
|
5340
|
+
text-align: right;
|
|
5341
|
+
user-select: none;
|
|
5342
|
+
background: var(--mf-editor-gutter);
|
|
5343
|
+
overflow: hidden;
|
|
5344
|
+
border-right: 1px solid #1E293B;
|
|
5345
|
+
}
|
|
5346
|
+
.code-editor {
|
|
5347
|
+
flex: 1;
|
|
5348
|
+
padding: 0.8rem 1rem;
|
|
5349
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5350
|
+
font-size: 13px;
|
|
5351
|
+
line-height: 1.55;
|
|
5352
|
+
color: var(--mf-editor-text);
|
|
5353
|
+
background: transparent;
|
|
5354
|
+
border: none;
|
|
5355
|
+
outline: none;
|
|
5356
|
+
resize: none;
|
|
5357
|
+
white-space: pre;
|
|
5358
|
+
overflow-wrap: normal;
|
|
5359
|
+
overflow: auto;
|
|
5360
|
+
tab-size: 2;
|
|
5361
|
+
}
|
|
5362
|
+
|
|
5363
|
+
/* Draggable Splitter Handle */
|
|
5364
|
+
.splitter {
|
|
5365
|
+
width: 8px;
|
|
5366
|
+
cursor: col-resize;
|
|
5367
|
+
background: #E2E8F0;
|
|
5368
|
+
transition: background 0.15s ease;
|
|
5369
|
+
position: relative;
|
|
5370
|
+
z-index: 5;
|
|
5371
|
+
}
|
|
5372
|
+
.splitter:hover, .splitter.active {
|
|
5373
|
+
background: var(--mf-primary);
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
/* Right Preview Pane */
|
|
5377
|
+
.preview-pane {
|
|
5378
|
+
width: 50%;
|
|
5379
|
+
height: 100%;
|
|
5380
|
+
display: flex;
|
|
5381
|
+
flex-direction: column;
|
|
5382
|
+
background: #FFFFFF;
|
|
5383
|
+
overflow: hidden;
|
|
5384
|
+
}
|
|
5385
|
+
.preview-header {
|
|
5386
|
+
background: #FFFFFF;
|
|
5387
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5388
|
+
padding: 0.35rem 0.8rem;
|
|
5389
|
+
display: flex;
|
|
5390
|
+
align-items: center;
|
|
5391
|
+
justify-content: space-between;
|
|
5392
|
+
color: var(--mf-muted);
|
|
5393
|
+
font-size: 0.74rem;
|
|
5394
|
+
font-weight: 600;
|
|
5395
|
+
}
|
|
5396
|
+
.viewport-selector {
|
|
5397
|
+
display: flex;
|
|
5398
|
+
gap: 0.25rem;
|
|
5399
|
+
}
|
|
5400
|
+
.vp-btn {
|
|
5401
|
+
font-size: 0.72rem;
|
|
5402
|
+
padding: 0.15rem 0.4rem;
|
|
5403
|
+
border: 1px solid var(--mf-light-border);
|
|
5404
|
+
background: #F8FAFC;
|
|
5405
|
+
border-radius: 4px;
|
|
5406
|
+
cursor: pointer;
|
|
5407
|
+
color: var(--mf-muted);
|
|
5408
|
+
}
|
|
5409
|
+
.vp-btn.active {
|
|
5410
|
+
background: var(--mf-primary-light);
|
|
5411
|
+
color: var(--mf-primary-dark);
|
|
5412
|
+
border-color: var(--mf-primary-border);
|
|
5413
|
+
}
|
|
5414
|
+
.preview-wrapper {
|
|
5415
|
+
flex: 1;
|
|
5416
|
+
display: flex;
|
|
5417
|
+
justify-content: center;
|
|
5418
|
+
align-items: stretch;
|
|
5419
|
+
background: #F1F5F9;
|
|
5420
|
+
overflow: hidden;
|
|
5421
|
+
}
|
|
5422
|
+
iframe {
|
|
5423
|
+
width: 100%;
|
|
5424
|
+
height: 100%;
|
|
5425
|
+
border: none;
|
|
5426
|
+
background: #FFFFFF;
|
|
5427
|
+
transition: max-width 0.2s ease;
|
|
5428
|
+
}
|
|
5429
|
+
.author-footer {
|
|
5430
|
+
font-size: 0.72rem;
|
|
5431
|
+
color: var(--mf-muted);
|
|
5432
|
+
padding-right: 0.5rem;
|
|
5433
|
+
}
|
|
5434
|
+
.author-footer a {
|
|
5435
|
+
color: var(--mf-primary-dark);
|
|
5436
|
+
text-decoration: none;
|
|
5437
|
+
font-weight: 600;
|
|
5438
|
+
}
|
|
5439
|
+
</style>
|
|
5440
|
+
</head>
|
|
5441
|
+
<body>
|
|
5442
|
+
<header>
|
|
5443
|
+
<div class="brand-section">
|
|
5444
|
+
<span class="brand-badge">MARKFORGE STUDIO</span>
|
|
5445
|
+
<span class="file-name" title="${escapeHtml2(absoluteFilePath)}">${escapeHtml2(fileName)}</span>
|
|
5446
|
+
<div class="sync-status">
|
|
5447
|
+
<div class="sync-dot"></div>
|
|
5448
|
+
<span>Live Sync Active</span>
|
|
5449
|
+
</div>
|
|
5450
|
+
</div>
|
|
5451
|
+
|
|
5452
|
+
<!-- Quick Formatting Toolbar -->
|
|
5453
|
+
<div class="toolbar-section">
|
|
5454
|
+
<button class="tool-btn" onclick="insertFormat('h1')" title="Heading 1">H1</button>
|
|
5455
|
+
<button class="tool-btn" onclick="insertFormat('h2')" title="Heading 2">H2</button>
|
|
5456
|
+
<button class="tool-btn" onclick="insertFormat('h3')" title="Heading 3">H3</button>
|
|
5457
|
+
<div class="tool-divider"></div>
|
|
5458
|
+
<button class="tool-btn" onclick="insertFormat('bold')" title="Bold">B</button>
|
|
5459
|
+
<button class="tool-btn" onclick="insertFormat('italic')" title="Italic">I</button>
|
|
5460
|
+
<button class="tool-btn" onclick="insertFormat('code')" title="Inline Code"><></button>
|
|
5461
|
+
<button class="tool-btn" onclick="insertFormat('quote')" title="Blockquote">></button>
|
|
5462
|
+
<div class="tool-divider"></div>
|
|
5463
|
+
<button class="tool-btn" onclick="insertFormat('table')" title="GFM Table">Table</button>
|
|
5464
|
+
<button class="tool-btn" onclick="insertFormat('list')" title="List">List</button>
|
|
5465
|
+
<button class="tool-btn" onclick="insertFormat('task')" title="Task Checklist">Task</button>
|
|
5466
|
+
<div class="tool-divider"></div>
|
|
5467
|
+
<button class="tool-btn" onclick="insertFormat('callout')" title="Callout Box">Callout</button>
|
|
5468
|
+
<button class="tool-btn" onclick="insertFormat('math')" title="LaTeX Math">Math</button>
|
|
5469
|
+
<button class="tool-btn" onclick="insertFormat('columns')" title="Multi-Columns">Columns</button>
|
|
5470
|
+
<button class="tool-btn" onclick="insertFormat('footnote')" title="Footnote">Footnote</button>
|
|
5471
|
+
<button class="tool-btn" onclick="insertFormat('mermaid')" title="Mermaid Diagram">Mermaid</button>
|
|
5472
|
+
</div>
|
|
5473
|
+
|
|
5474
|
+
<!-- Controls & View Mode -->
|
|
5475
|
+
<div class="controls">
|
|
5476
|
+
<div class="view-toggles">
|
|
5477
|
+
<button class="toggle-btn active" id="btn-split" onclick="setViewMode('split')">Split</button>
|
|
5478
|
+
<button class="toggle-btn" id="btn-edit" onclick="setViewMode('edit')">Editor</button>
|
|
5479
|
+
<button class="toggle-btn" id="btn-prev" onclick="setViewMode('prev')">Preview</button>
|
|
5480
|
+
</div>
|
|
5481
|
+
<span class="save-indicator saved" id="save-status">Saved</span>
|
|
5482
|
+
<button class="btn btn-primary" onclick="saveContentManual()" title="Save (Ctrl+S)">Save</button>
|
|
5483
|
+
<button class="btn" onclick="exportDoc('docx')" title="Download Word Document">DOCX</button>
|
|
5484
|
+
<button class="btn" onclick="exportDoc('pdf')" title="Download PDF Document">PDF</button>
|
|
5485
|
+
<button class="btn" onclick="printDoc()" title="Print / PDF dialog">Print</button>
|
|
5486
|
+
</div>
|
|
5487
|
+
</header>
|
|
5488
|
+
|
|
5489
|
+
<main class="workspace" id="workspace">
|
|
5490
|
+
<!-- Left: Code Editor Pane -->
|
|
5491
|
+
<div class="editor-pane" id="editor-pane">
|
|
5492
|
+
<div class="editor-header">
|
|
5493
|
+
<span>MARKDOWN SOURCE</span>
|
|
5494
|
+
<span id="editor-stats">Lines: 1 | Words: 0 | UTF-8</span>
|
|
5495
|
+
</div>
|
|
5496
|
+
<div class="editor-container">
|
|
5497
|
+
<div class="line-numbers" id="line-numbers">1</div>
|
|
5498
|
+
<textarea class="code-editor" id="code-editor" spellcheck="false" placeholder="Write markdown here...">${escapeHtml2(initialContent)}</textarea>
|
|
5499
|
+
</div>
|
|
5500
|
+
</div>
|
|
5501
|
+
|
|
5502
|
+
<!-- Middle: Draggable Splitter Handle -->
|
|
5503
|
+
<div class="splitter" id="splitter"></div>
|
|
5504
|
+
|
|
5505
|
+
<!-- Right: Rendered Preview Pane -->
|
|
5506
|
+
<div class="preview-pane" id="preview-pane">
|
|
5507
|
+
<div class="preview-header">
|
|
5508
|
+
<span>RENDERED PREVIEW</span>
|
|
5509
|
+
<div class="viewport-selector">
|
|
5510
|
+
<button class="vp-btn active" onclick="setViewport('100%')" id="vp-full">100% Full</button>
|
|
5511
|
+
<button class="vp-btn" onclick="setViewport('820px')" id="vp-a4">A4 (820px)</button>
|
|
5512
|
+
<button class="vp-btn" onclick="setViewport('440px')" id="vp-mob">Mobile</button>
|
|
5513
|
+
</div>
|
|
5514
|
+
<span class="author-footer">Created by <a href="https://github.com/masumrpg" target="_blank">Ma'sum (@masumrpg)</a></span>
|
|
5515
|
+
</div>
|
|
5516
|
+
<div class="preview-wrapper">
|
|
5517
|
+
<iframe id="preview-frame" src="/document-content"></iframe>
|
|
5518
|
+
</div>
|
|
5519
|
+
</div>
|
|
5520
|
+
</main>
|
|
5521
|
+
|
|
5522
|
+
<script>
|
|
5523
|
+
var editor = document.getElementById('code-editor');
|
|
5524
|
+
var lineNumbers = document.getElementById('line-numbers');
|
|
5525
|
+
var stats = document.getElementById('editor-stats');
|
|
5526
|
+
var saveStatus = document.getElementById('save-status');
|
|
5527
|
+
var previewFrame = document.getElementById('preview-frame');
|
|
5528
|
+
var editorPane = document.getElementById('editor-pane');
|
|
5529
|
+
var previewPane = document.getElementById('preview-pane');
|
|
5530
|
+
var splitter = document.getElementById('splitter');
|
|
5531
|
+
var isDirty = false;
|
|
5532
|
+
var autoSaveTimeout = null;
|
|
5533
|
+
|
|
5534
|
+
// Update Line Numbers & Stats
|
|
5535
|
+
function updateStatsAndLines() {
|
|
5536
|
+
var lines = editor.value.split('\\n');
|
|
5537
|
+
var lineCount = lines.length;
|
|
5538
|
+
var numHtml = '';
|
|
5539
|
+
for (var i = 1; i <= lineCount; i++) {
|
|
5540
|
+
numHtml += i + '<br>';
|
|
5541
|
+
}
|
|
5542
|
+
lineNumbers.innerHTML = numHtml;
|
|
5543
|
+
|
|
5544
|
+
var words = editor.value.trim().length > 0 ? editor.value.trim().split(/\\s+/).length : 0;
|
|
5545
|
+
var chars = editor.value.length;
|
|
5546
|
+
stats.textContent = 'Lines: ' + lineCount + ' | Words: ' + words + ' | Chars: ' + chars + ' | UTF-8';
|
|
5547
|
+
}
|
|
5548
|
+
|
|
5549
|
+
// Synchronize vertical scroll between Line Numbers and Textarea
|
|
5550
|
+
editor.addEventListener('scroll', function() {
|
|
5551
|
+
lineNumbers.scrollTop = editor.scrollTop;
|
|
5552
|
+
});
|
|
5553
|
+
|
|
5554
|
+
// Handle Input & Debounced Auto-Save
|
|
5555
|
+
editor.addEventListener('input', function() {
|
|
5556
|
+
updateStatsAndLines();
|
|
5557
|
+
setSaveState('unsaved');
|
|
5558
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5559
|
+
autoSaveTimeout = setTimeout(function() {
|
|
5560
|
+
saveContent();
|
|
5561
|
+
}, 600);
|
|
5562
|
+
});
|
|
5563
|
+
|
|
5564
|
+
function setSaveState(state) {
|
|
5565
|
+
if (state === 'saved') {
|
|
5566
|
+
saveStatus.textContent = 'Saved';
|
|
5567
|
+
saveStatus.className = 'save-indicator saved';
|
|
5568
|
+
isDirty = false;
|
|
5569
|
+
} else if (state === 'saving') {
|
|
5570
|
+
saveStatus.textContent = 'Saving...';
|
|
5571
|
+
saveStatus.className = 'save-indicator saving';
|
|
5572
|
+
} else {
|
|
5573
|
+
saveStatus.textContent = 'Changes...';
|
|
5574
|
+
saveStatus.className = 'save-indicator unsaved';
|
|
5575
|
+
isDirty = true;
|
|
5576
|
+
}
|
|
5577
|
+
}
|
|
5578
|
+
|
|
5579
|
+
// Save Content via API
|
|
5580
|
+
function saveContent(callback) {
|
|
5581
|
+
setSaveState('saving');
|
|
5582
|
+
fetch('/api/save-content', {
|
|
5583
|
+
method: 'POST',
|
|
5584
|
+
headers: { 'Content-Type': 'application/json' },
|
|
5585
|
+
body: JSON.stringify({ content: editor.value }),
|
|
5586
|
+
})
|
|
5587
|
+
.then(function(res) { return res.json(); })
|
|
5588
|
+
.then(function(data) {
|
|
5589
|
+
if (data.success) {
|
|
5590
|
+
setSaveState('saved');
|
|
5591
|
+
if (callback) callback();
|
|
5592
|
+
} else {
|
|
5593
|
+
saveStatus.textContent = 'Save Error';
|
|
5594
|
+
}
|
|
5595
|
+
})
|
|
5596
|
+
.catch(function() {
|
|
5597
|
+
saveStatus.textContent = 'Save Error';
|
|
5598
|
+
});
|
|
5599
|
+
}
|
|
5600
|
+
|
|
5601
|
+
function saveContentManual() {
|
|
5602
|
+
saveContent();
|
|
5603
|
+
}
|
|
5604
|
+
|
|
5605
|
+
// Keyboard Shortcuts: Tab (2 spaces), Shift+Tab, Ctrl+S
|
|
5606
|
+
editor.addEventListener('keydown', function(e) {
|
|
5607
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
5608
|
+
e.preventDefault();
|
|
5609
|
+
saveContent();
|
|
5610
|
+
return;
|
|
5611
|
+
}
|
|
5612
|
+
|
|
5613
|
+
if (e.key === 'Tab') {
|
|
5614
|
+
e.preventDefault();
|
|
5615
|
+
var start = this.selectionStart;
|
|
5616
|
+
var end = this.selectionEnd;
|
|
5617
|
+
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
|
|
5618
|
+
this.selectionStart = this.selectionEnd = start + 2;
|
|
5619
|
+
updateStatsAndLines();
|
|
5620
|
+
setSaveState('unsaved');
|
|
5621
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5622
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5623
|
+
}
|
|
5624
|
+
});
|
|
5625
|
+
|
|
5626
|
+
// Formatting Snippet Injector
|
|
5627
|
+
function insertFormat(type) {
|
|
5628
|
+
var start = editor.selectionStart;
|
|
5629
|
+
var end = editor.selectionEnd;
|
|
5630
|
+
var selected = editor.value.substring(start, end);
|
|
5631
|
+
var replacement = '';
|
|
5632
|
+
|
|
5633
|
+
switch (type) {
|
|
5634
|
+
case 'h1': replacement = '# ' + (selected || 'Heading 1'); break;
|
|
5635
|
+
case 'h2': replacement = '## ' + (selected || 'Heading 2'); break;
|
|
5636
|
+
case 'h3': replacement = '### ' + (selected || 'Heading 3'); break;
|
|
5637
|
+
case 'bold': replacement = '**' + (selected || 'bold text') + '**'; break;
|
|
5638
|
+
case 'italic': replacement = '*' + (selected || 'italic text') + '*'; break;
|
|
5639
|
+
case 'code': replacement = '\`' + (selected || 'inline code') + '\`'; break;
|
|
5640
|
+
case 'quote': replacement = '> ' + (selected || 'Quote text'); break;
|
|
5641
|
+
case 'table':
|
|
5642
|
+
replacement = '\\n| Column 1 | Column 2 | Column 3 |\\n| :--- | :---: | ---: |\\n| Data A | Data B | Data C |\\n| Data D | Data E | Data F |\\n';
|
|
5643
|
+
break;
|
|
5644
|
+
case 'list': replacement = '- ' + (selected || 'List item'); break;
|
|
5645
|
+
case 'task': replacement = '- [ ] ' + (selected || 'Task item'); break;
|
|
5646
|
+
case 'callout':
|
|
5647
|
+
replacement = '> [!NOTE]\\n> ' + (selected || 'This is an important callout note.');
|
|
5648
|
+
break;
|
|
5649
|
+
case 'math':
|
|
5650
|
+
replacement = '$$\\n' + (selected || '\\\\int_{-\\\\infty}^{\\\\infty} e^{-x^2} dx = \\\\sqrt{\\\\pi}') + '\\n$$';
|
|
5651
|
+
break;
|
|
5652
|
+
case 'columns':
|
|
5653
|
+
replacement = ':::columns 2\\n:::col\\n### Left Column\\n' + (selected || 'Content on the left.') + '\\n:::\\n:::col\\n### Right Column\\nContent on the right.\\n:::\\n:::';
|
|
5654
|
+
break;
|
|
5655
|
+
case 'footnote':
|
|
5656
|
+
replacement = (selected || 'Statement with footnote') + '[^1]\\n\\n[^1]: Note description text.';
|
|
5657
|
+
break;
|
|
5658
|
+
case 'mermaid':
|
|
5659
|
+
replacement = '\\n\`\`\`mermaid\\ngraph TD\\n A[Start] --> B(Process)\\n B --> C{Decision}\\n C -->|Yes| D[Done]\\n C -->|No| B\\n\`\`\`\\n';
|
|
5660
|
+
break;
|
|
5661
|
+
}
|
|
5662
|
+
|
|
5663
|
+
editor.value = editor.value.substring(0, start) + replacement + editor.value.substring(end);
|
|
5664
|
+
editor.selectionStart = editor.selectionEnd = start + replacement.length;
|
|
5665
|
+
editor.focus();
|
|
5666
|
+
updateStatsAndLines();
|
|
5667
|
+
setSaveState('unsaved');
|
|
5668
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5669
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5670
|
+
}
|
|
5671
|
+
|
|
5672
|
+
// View Mode Toggle (Split / Editor Only / Preview Only)
|
|
5673
|
+
function setViewMode(mode) {
|
|
5674
|
+
document.getElementById('btn-split').classList.remove('active');
|
|
5675
|
+
document.getElementById('btn-edit').classList.remove('active');
|
|
5676
|
+
document.getElementById('btn-prev').classList.remove('active');
|
|
5677
|
+
|
|
5678
|
+
if (mode === 'split') {
|
|
5679
|
+
document.getElementById('btn-split').classList.add('active');
|
|
5680
|
+
editorPane.style.display = 'flex';
|
|
5681
|
+
editorPane.style.width = '50%';
|
|
5682
|
+
previewPane.style.display = 'flex';
|
|
5683
|
+
previewPane.style.width = '50%';
|
|
5684
|
+
splitter.style.display = 'block';
|
|
5685
|
+
} else if (mode === 'edit') {
|
|
5686
|
+
document.getElementById('btn-edit').classList.add('active');
|
|
5687
|
+
editorPane.style.display = 'flex';
|
|
5688
|
+
editorPane.style.width = '100%';
|
|
5689
|
+
previewPane.style.display = 'none';
|
|
5690
|
+
splitter.style.display = 'none';
|
|
5691
|
+
} else if (mode === 'prev') {
|
|
5692
|
+
document.getElementById('btn-prev').classList.add('active');
|
|
5693
|
+
editorPane.style.display = 'none';
|
|
5694
|
+
previewPane.style.display = 'flex';
|
|
5695
|
+
previewPane.style.width = '100%';
|
|
5696
|
+
splitter.style.display = 'none';
|
|
5697
|
+
}
|
|
5698
|
+
}
|
|
5699
|
+
|
|
5700
|
+
// Viewport Width Resizer
|
|
5701
|
+
function setViewport(width) {
|
|
5702
|
+
document.getElementById('vp-full').classList.remove('active');
|
|
5703
|
+
document.getElementById('vp-a4').classList.remove('active');
|
|
5704
|
+
document.getElementById('vp-mob').classList.remove('active');
|
|
5705
|
+
|
|
5706
|
+
if (width === '100%') {
|
|
5707
|
+
document.getElementById('vp-full').classList.add('active');
|
|
5708
|
+
previewFrame.style.maxWidth = '100%';
|
|
5709
|
+
} else if (width === '820px') {
|
|
5710
|
+
document.getElementById('vp-a4').classList.add('active');
|
|
5711
|
+
previewFrame.style.maxWidth = '820px';
|
|
5712
|
+
} else if (width === '440px') {
|
|
5713
|
+
document.getElementById('vp-mob').classList.add('active');
|
|
5714
|
+
previewFrame.style.maxWidth = '440px';
|
|
5715
|
+
}
|
|
5716
|
+
}
|
|
5717
|
+
|
|
5718
|
+
// Draggable Splitter Handle Logic
|
|
5719
|
+
var isDragging = false;
|
|
5720
|
+
splitter.addEventListener('mousedown', function(e) {
|
|
5721
|
+
isDragging = true;
|
|
5722
|
+
splitter.classList.add('active');
|
|
5723
|
+
document.body.style.cursor = 'col-resize';
|
|
5724
|
+
document.body.style.userSelect = 'none';
|
|
5725
|
+
});
|
|
5726
|
+
|
|
5727
|
+
window.addEventListener('mousemove', function(e) {
|
|
5728
|
+
if (!isDragging) return;
|
|
5729
|
+
var totalWidth = document.getElementById('workspace').clientWidth;
|
|
5730
|
+
var newEditorWidth = (e.clientX / totalWidth) * 100;
|
|
5731
|
+
if (newEditorWidth > 15 && newEditorWidth < 85) {
|
|
5732
|
+
editorPane.style.width = newEditorWidth + '%';
|
|
5733
|
+
previewPane.style.width = (100 - newEditorWidth) + '%';
|
|
5734
|
+
}
|
|
5735
|
+
});
|
|
5736
|
+
|
|
5737
|
+
window.addEventListener('mouseup', function() {
|
|
5738
|
+
if (isDragging) {
|
|
5739
|
+
isDragging = false;
|
|
5740
|
+
splitter.classList.remove('active');
|
|
5741
|
+
document.body.style.cursor = '';
|
|
5742
|
+
document.body.style.userSelect = '';
|
|
5743
|
+
}
|
|
5744
|
+
});
|
|
5745
|
+
|
|
5746
|
+
// Document Print Action
|
|
5747
|
+
function printDoc() {
|
|
5748
|
+
previewFrame.contentWindow.print();
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5751
|
+
// Document Export Action
|
|
5752
|
+
function exportDoc(format) {
|
|
5753
|
+
saveContent(function() {
|
|
5754
|
+
window.location.href = '/api/export?format=' + format;
|
|
5755
|
+
});
|
|
5756
|
+
}
|
|
5757
|
+
|
|
5758
|
+
// Initialize line stats
|
|
5759
|
+
updateStatsAndLines();
|
|
5760
|
+
</script>
|
|
5761
|
+
</body>
|
|
5762
|
+
</html>`;
|
|
5763
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5764
|
+
res.end(appHtml);
|
|
5765
|
+
return;
|
|
5766
|
+
}
|
|
5767
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
5768
|
+
res.end("Not Found");
|
|
5769
|
+
});
|
|
5770
|
+
return new Promise((resolve6, reject) => {
|
|
5771
|
+
server.listen(port, () => {
|
|
5772
|
+
const url = `http://localhost:${port}`;
|
|
5773
|
+
resolve6({
|
|
5774
|
+
server,
|
|
5775
|
+
port,
|
|
5776
|
+
url,
|
|
5777
|
+
close: async () => {
|
|
5778
|
+
watcher.close();
|
|
5779
|
+
sseClients.forEach((client) => {
|
|
5780
|
+
try {
|
|
5781
|
+
client.end();
|
|
5782
|
+
} catch {
|
|
5783
|
+
}
|
|
5784
|
+
});
|
|
5785
|
+
sseClients.clear();
|
|
5786
|
+
return new Promise((res) => {
|
|
5787
|
+
server.close(() => res());
|
|
5788
|
+
});
|
|
5789
|
+
}
|
|
5790
|
+
});
|
|
5791
|
+
});
|
|
5792
|
+
server.on("error", (err) => {
|
|
5793
|
+
reject(err);
|
|
5794
|
+
});
|
|
5795
|
+
});
|
|
5796
|
+
}
|
|
5797
|
+
function escapeHtml2(str) {
|
|
5798
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5799
|
+
}
|
|
5800
|
+
|
|
3539
5801
|
// src/config/defineConfig.ts
|
|
3540
5802
|
function defineConfig(config) {
|
|
3541
5803
|
return config;
|
|
3542
5804
|
}
|
|
3543
5805
|
|
|
3544
5806
|
// src/version.ts
|
|
3545
|
-
var
|
|
3546
|
-
var
|
|
5807
|
+
var fs8 = __toESM(require("fs"));
|
|
5808
|
+
var path8 = __toESM(require("path"));
|
|
3547
5809
|
var import_node_url4 = require("url");
|
|
3548
5810
|
var import_meta = {};
|
|
3549
5811
|
try {
|
|
@@ -3566,21 +5828,21 @@ try {
|
|
|
3566
5828
|
}
|
|
3567
5829
|
} catch {
|
|
3568
5830
|
}
|
|
3569
|
-
var FALLBACK_VERSION = "0.
|
|
5831
|
+
var FALLBACK_VERSION = "0.4.0";
|
|
3570
5832
|
function readVersionFromPackageJson(fromDir) {
|
|
3571
5833
|
let currentDir = fromDir;
|
|
3572
5834
|
for (let i = 0; i < 6; i++) {
|
|
3573
5835
|
try {
|
|
3574
|
-
const pkgJsonPath =
|
|
3575
|
-
if (
|
|
3576
|
-
const pkg = JSON.parse(
|
|
5836
|
+
const pkgJsonPath = path8.join(currentDir, "package.json");
|
|
5837
|
+
if (fs8.existsSync(pkgJsonPath)) {
|
|
5838
|
+
const pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
|
|
3577
5839
|
if (pkg.name === "@masumdev/markforge" && pkg.version) {
|
|
3578
5840
|
return pkg.version;
|
|
3579
5841
|
}
|
|
3580
5842
|
}
|
|
3581
5843
|
} catch {
|
|
3582
5844
|
}
|
|
3583
|
-
const parentDir =
|
|
5845
|
+
const parentDir = path8.dirname(currentDir);
|
|
3584
5846
|
if (parentDir === currentDir) break;
|
|
3585
5847
|
currentDir = parentDir;
|
|
3586
5848
|
}
|
|
@@ -3591,7 +5853,7 @@ function getPackageDir() {
|
|
|
3591
5853
|
return __dirname;
|
|
3592
5854
|
}
|
|
3593
5855
|
try {
|
|
3594
|
-
return
|
|
5856
|
+
return path8.dirname((0, import_node_url4.fileURLToPath)(import_meta.url));
|
|
3595
5857
|
} catch {
|
|
3596
5858
|
return process.cwd();
|
|
3597
5859
|
}
|
|
@@ -3603,6 +5865,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3603
5865
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3604
5866
|
0 && (module.exports = {
|
|
3605
5867
|
DEFAULT_CONFIG,
|
|
5868
|
+
KATEX_INLINE_CSS,
|
|
3606
5869
|
MARKFORGE_VERSION,
|
|
3607
5870
|
Orientation,
|
|
3608
5871
|
OutputFormat,
|
|
@@ -3631,18 +5894,28 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3631
5894
|
inlineHtmlImages,
|
|
3632
5895
|
loadConfig,
|
|
3633
5896
|
markforge,
|
|
5897
|
+
normalizeBackCover,
|
|
5898
|
+
normalizeCoverPage,
|
|
3634
5899
|
normalizeHeaderFooter,
|
|
3635
5900
|
normalizeHeaderFooterSlot,
|
|
5901
|
+
normalizeNumberHeadings,
|
|
5902
|
+
normalizeSecurity,
|
|
3636
5903
|
normalizeSignatures,
|
|
3637
5904
|
normalizeWatermark,
|
|
3638
5905
|
parseInlineSpans,
|
|
3639
5906
|
parseMarginToTwip,
|
|
5907
|
+
parseMarkdown,
|
|
3640
5908
|
parseMarkdownDocument,
|
|
5909
|
+
renderBackCoverHtml,
|
|
5910
|
+
renderCoverPageHtml,
|
|
3641
5911
|
renderInlinesToHtml,
|
|
5912
|
+
renderMathToHtml,
|
|
3642
5913
|
renderMermaidToPng,
|
|
5914
|
+
renderNodesToHtml,
|
|
3643
5915
|
replaceDocumentTokens,
|
|
3644
5916
|
resolveDocumentConfig,
|
|
3645
5917
|
resolveImage,
|
|
3646
5918
|
slugify,
|
|
5919
|
+
startPreviewServer,
|
|
3647
5920
|
tokenizeCodeLine
|
|
3648
5921
|
});
|