@masumdev/markforge 0.2.5 → 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 +173 -116
- package/dist/App-3QDKBKHG.mjs +328 -0
- package/dist/{chunk-IYGPJIK5.mjs → chunk-BPHDY6VB.mjs} +6 -0
- package/dist/{App-GNRUPFM7.mjs → chunk-CRV7R2BG.mjs} +2094 -570
- package/dist/{chunk-NGC2ZAWZ.mjs → chunk-TNWZ4MFS.mjs} +1 -1
- package/dist/cli.mjs +29 -7
- package/dist/index.d.mts +603 -117
- package/dist/index.d.ts +603 -117
- package/dist/index.js +2863 -202
- package/dist/index.mjs +2851 -202
- 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,18 +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,
|
|
77
|
+
normalizeSignatures: () => normalizeSignatures,
|
|
72
78
|
normalizeWatermark: () => normalizeWatermark,
|
|
73
79
|
parseInlineSpans: () => parseInlineSpans,
|
|
74
80
|
parseMarginToTwip: () => parseMarginToTwip2,
|
|
81
|
+
parseMarkdown: () => parseMarkdownDocument,
|
|
75
82
|
parseMarkdownDocument: () => parseMarkdownDocument,
|
|
83
|
+
renderBackCoverHtml: () => renderBackCoverHtml,
|
|
84
|
+
renderCoverPageHtml: () => renderCoverPageHtml,
|
|
76
85
|
renderInlinesToHtml: () => renderInlinesToHtml,
|
|
86
|
+
renderMathToHtml: () => renderMathToHtml,
|
|
77
87
|
renderMermaidToPng: () => renderMermaidToPng,
|
|
88
|
+
renderNodesToHtml: () => renderNodesToHtml,
|
|
78
89
|
replaceDocumentTokens: () => replaceDocumentTokens,
|
|
79
90
|
resolveDocumentConfig: () => resolveDocumentConfig,
|
|
80
91
|
resolveImage: () => resolveImage,
|
|
81
92
|
slugify: () => slugify,
|
|
93
|
+
startPreviewServer: () => startPreviewServer,
|
|
82
94
|
tokenizeCodeLine: () => tokenizeCodeLine
|
|
83
95
|
});
|
|
84
96
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -119,6 +131,26 @@ function parseInlineSpans(text) {
|
|
|
119
131
|
remaining = remaining.slice(imgMatch[0].length);
|
|
120
132
|
continue;
|
|
121
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
|
+
}
|
|
122
154
|
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/);
|
|
123
155
|
if (linkMatch) {
|
|
124
156
|
spans.push({
|
|
@@ -218,7 +250,7 @@ function parseInlineSpans(text) {
|
|
|
218
250
|
remaining = remaining.slice(fullTag.length);
|
|
219
251
|
continue;
|
|
220
252
|
}
|
|
221
|
-
const nextSpecial = remaining.search(/[\*\_\[
|
|
253
|
+
const nextSpecial = remaining.search(/[\*\_\[\!`~<\$]/);
|
|
222
254
|
if (nextSpecial === -1) {
|
|
223
255
|
spans.push({
|
|
224
256
|
type: "text",
|
|
@@ -244,6 +276,35 @@ function parseInlineSpans(text) {
|
|
|
244
276
|
function slugify(text) {
|
|
245
277
|
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
246
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
|
+
}
|
|
247
308
|
function parseMarkdownDocument(rawMarkdown) {
|
|
248
309
|
const { data: frontmatter, content } = (0, import_gray_matter.default)(rawMarkdown);
|
|
249
310
|
const metadata = frontmatter || {};
|
|
@@ -255,6 +316,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
255
316
|
const lines = cleanContent.split(/\r?\n/);
|
|
256
317
|
const nodes = [];
|
|
257
318
|
const tocEntries = [];
|
|
319
|
+
const footnoteDefs = [];
|
|
258
320
|
let i = 0;
|
|
259
321
|
while (i < lines.length) {
|
|
260
322
|
const line = lines[i];
|
|
@@ -283,6 +345,29 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
283
345
|
i++;
|
|
284
346
|
continue;
|
|
285
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
|
+
}
|
|
286
371
|
const codeBlockMatch = line.match(/^```(\w+)?/);
|
|
287
372
|
if (codeBlockMatch) {
|
|
288
373
|
const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
|
|
@@ -309,6 +394,100 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
309
394
|
}
|
|
310
395
|
continue;
|
|
311
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
|
+
}
|
|
312
491
|
const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
|
|
313
492
|
if (calloutMatch) {
|
|
314
493
|
const calloutType = calloutMatch[1].toUpperCase();
|
|
@@ -432,7 +611,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
432
611
|
continue;
|
|
433
612
|
}
|
|
434
613
|
const paraLines = [];
|
|
435
|
-
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+/)) {
|
|
436
615
|
paraLines.push(lines[i]);
|
|
437
616
|
i++;
|
|
438
617
|
}
|
|
@@ -443,12 +622,19 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
443
622
|
inlines: parseInlineSpans(paraText)
|
|
444
623
|
});
|
|
445
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
|
+
}
|
|
446
631
|
return {
|
|
447
632
|
metadata,
|
|
448
633
|
content: cleanContent,
|
|
449
634
|
nodes,
|
|
450
635
|
tocEntries,
|
|
451
|
-
inlinedStyles
|
|
636
|
+
inlinedStyles,
|
|
637
|
+
footnoteDefs
|
|
452
638
|
};
|
|
453
639
|
}
|
|
454
640
|
|
|
@@ -514,9 +700,14 @@ async function resolveImage(src, baseDir = process.cwd()) {
|
|
|
514
700
|
memoryImageCache.set(cacheKey, resolved2);
|
|
515
701
|
return resolved2;
|
|
516
702
|
}
|
|
517
|
-
|
|
703
|
+
let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
|
|
518
704
|
if (!fs.existsSync(localPath)) {
|
|
519
|
-
|
|
705
|
+
const cwdPath = path.resolve(process.cwd(), src);
|
|
706
|
+
if (fs.existsSync(cwdPath)) {
|
|
707
|
+
localPath = cwdPath;
|
|
708
|
+
} else {
|
|
709
|
+
return null;
|
|
710
|
+
}
|
|
520
711
|
}
|
|
521
712
|
const buffer = fs.readFileSync(localPath);
|
|
522
713
|
const mimeType = getMimeType(localPath);
|
|
@@ -921,6 +1112,8 @@ var path4 = __toESM(require("path"));
|
|
|
921
1112
|
var os = __toESM(require("os"));
|
|
922
1113
|
var import_node_url2 = require("url");
|
|
923
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");
|
|
924
1117
|
|
|
925
1118
|
// src/core/html/htmlBuilder.ts
|
|
926
1119
|
var fs3 = __toESM(require("fs"));
|
|
@@ -1163,6 +1356,12 @@ var DEFAULT_CONFIG = {
|
|
|
1163
1356
|
},
|
|
1164
1357
|
toc: false,
|
|
1165
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,
|
|
1166
1365
|
embedImages: true,
|
|
1167
1366
|
metadata: void 0,
|
|
1168
1367
|
watch: false,
|
|
@@ -1319,7 +1518,8 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
|
|
|
1319
1518
|
return str;
|
|
1320
1519
|
}
|
|
1321
1520
|
function replaceDocumentTokens(template = "", meta) {
|
|
1322
|
-
|
|
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 || "");
|
|
1323
1523
|
}
|
|
1324
1524
|
function normalizeWatermark(rawWatermark) {
|
|
1325
1525
|
if (!rawWatermark) {
|
|
@@ -1395,6 +1595,190 @@ function normalizeHeaderFooter(raw, meta) {
|
|
|
1395
1595
|
dividerColor: raw.dividerColor || "#E2E8F0"
|
|
1396
1596
|
};
|
|
1397
1597
|
}
|
|
1598
|
+
function normalizeSignatures(raw, meta = {}) {
|
|
1599
|
+
if (!raw) return void 0;
|
|
1600
|
+
let rawItems = [];
|
|
1601
|
+
let rawConfig = {};
|
|
1602
|
+
if (Array.isArray(raw)) {
|
|
1603
|
+
rawItems = raw;
|
|
1604
|
+
} else if (typeof raw === "object" && Array.isArray(raw.items)) {
|
|
1605
|
+
rawItems = raw.items;
|
|
1606
|
+
rawConfig = raw;
|
|
1607
|
+
}
|
|
1608
|
+
if (rawItems.length === 0) return void 0;
|
|
1609
|
+
const cappedItems = rawItems.slice(0, 4);
|
|
1610
|
+
const items = cappedItems.map((item) => {
|
|
1611
|
+
const tokenCtx = meta;
|
|
1612
|
+
const rawName = typeof item.name === "string" ? item.name : "";
|
|
1613
|
+
const name = replaceDocumentTokens(rawName, tokenCtx).trim();
|
|
1614
|
+
const title = item.title ? replaceDocumentTokens(item.title, tokenCtx).trim() : void 0;
|
|
1615
|
+
const role = item.role ? replaceDocumentTokens(item.role, tokenCtx).trim() : void 0;
|
|
1616
|
+
let dateStr;
|
|
1617
|
+
if (typeof item.date === "string") {
|
|
1618
|
+
dateStr = replaceDocumentTokens(item.date, tokenCtx).trim();
|
|
1619
|
+
} else if (item.date === true) {
|
|
1620
|
+
dateStr = meta.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1621
|
+
}
|
|
1622
|
+
let signatureHeight = 60;
|
|
1623
|
+
if (typeof item.signatureHeight === "number") {
|
|
1624
|
+
signatureHeight = item.signatureHeight;
|
|
1625
|
+
} else if (typeof item.signatureHeight === "string") {
|
|
1626
|
+
const parsed = parseFloat(item.signatureHeight);
|
|
1627
|
+
if (!isNaN(parsed)) signatureHeight = parsed;
|
|
1628
|
+
}
|
|
1629
|
+
return {
|
|
1630
|
+
title,
|
|
1631
|
+
name: name || "Authorized Signatory",
|
|
1632
|
+
role,
|
|
1633
|
+
date: dateStr,
|
|
1634
|
+
image: item.image,
|
|
1635
|
+
signatureHeight
|
|
1636
|
+
};
|
|
1637
|
+
});
|
|
1638
|
+
const align = rawConfig.align || (items.length === 1 ? "right" : "space-between");
|
|
1639
|
+
const style = rawConfig.style || "line";
|
|
1640
|
+
const borderColor = rawConfig.borderColor || "#CBD5E1";
|
|
1641
|
+
const titleColor = rawConfig.titleColor || "#64748B";
|
|
1642
|
+
const nameColor = rawConfig.nameColor || "#0F172A";
|
|
1643
|
+
const roleColor = rawConfig.roleColor || "#64748B";
|
|
1644
|
+
const spacingBeforeRaw = rawConfig.spacingBefore ?? "2.5rem";
|
|
1645
|
+
const spacingBefore = formatMarginCss(spacingBeforeRaw, "2.5rem");
|
|
1646
|
+
const spacingBeforeTwip = parseMarginToTwip(spacingBeforeRaw, 600);
|
|
1647
|
+
return {
|
|
1648
|
+
items,
|
|
1649
|
+
align,
|
|
1650
|
+
style,
|
|
1651
|
+
borderColor,
|
|
1652
|
+
titleColor,
|
|
1653
|
+
nameColor,
|
|
1654
|
+
roleColor,
|
|
1655
|
+
spacingBefore,
|
|
1656
|
+
spacingBeforeTwip
|
|
1657
|
+
};
|
|
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
|
+
}
|
|
1398
1782
|
function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
1399
1783
|
const configMeta = userConfig.metadata || {};
|
|
1400
1784
|
const mergedMeta = { ...configMeta, ...frontmatter };
|
|
@@ -1435,6 +1819,17 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1435
1819
|
const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
|
|
1436
1820
|
const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
|
|
1437
1821
|
const watermark = normalizeWatermark(rawWatermark);
|
|
1822
|
+
const rawSignatures = mergedMeta.signatures || userConfig.signatures;
|
|
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;
|
|
1438
1833
|
const cssList = [];
|
|
1439
1834
|
const addCss = (item) => {
|
|
1440
1835
|
if (!item) return;
|
|
@@ -1462,7 +1857,13 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1462
1857
|
header,
|
|
1463
1858
|
footer,
|
|
1464
1859
|
toc,
|
|
1860
|
+
signatures,
|
|
1465
1861
|
watermark,
|
|
1862
|
+
coverPage,
|
|
1863
|
+
backCover,
|
|
1864
|
+
numberHeadings,
|
|
1865
|
+
security,
|
|
1866
|
+
math,
|
|
1466
1867
|
css: cssList,
|
|
1467
1868
|
embedImages,
|
|
1468
1869
|
bundleHtml,
|
|
@@ -1470,6 +1871,45 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1470
1871
|
};
|
|
1471
1872
|
}
|
|
1472
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
|
+
|
|
1473
1913
|
// src/core/html/htmlBuilder.ts
|
|
1474
1914
|
function escapeHtml(str) {
|
|
1475
1915
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -1512,6 +1952,15 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1512
1952
|
result += `<code>${escapeHtml(span.content)}</code>`;
|
|
1513
1953
|
continue;
|
|
1514
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
|
+
}
|
|
1515
1964
|
if (span.type === "htmlInline") {
|
|
1516
1965
|
result += span.content;
|
|
1517
1966
|
continue;
|
|
@@ -1520,66 +1969,9 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1520
1969
|
}
|
|
1521
1970
|
return result;
|
|
1522
1971
|
}
|
|
1523
|
-
async function
|
|
1524
|
-
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
1525
|
-
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
1526
|
-
let customCss = "";
|
|
1527
|
-
for (const cssPath of resolved.css) {
|
|
1528
|
-
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
1529
|
-
if (fs3.existsSync(fullCssPath)) {
|
|
1530
|
-
customCss += `
|
|
1531
|
-
/* Custom CSS: ${cssPath} */
|
|
1532
|
-
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
1972
|
+
async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
|
|
1536
1973
|
let bodyHtml = "";
|
|
1537
|
-
|
|
1538
|
-
bodyHtml += ` <header class="document-header">
|
|
1539
|
-
`;
|
|
1540
|
-
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
1541
|
-
`;
|
|
1542
|
-
if (resolved.subtitle) {
|
|
1543
|
-
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
1544
|
-
`;
|
|
1545
|
-
}
|
|
1546
|
-
if (resolved.author || resolved.date || resolved.version) {
|
|
1547
|
-
bodyHtml += ` <div class="document-meta">
|
|
1548
|
-
`;
|
|
1549
|
-
if (resolved.author) {
|
|
1550
|
-
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
1551
|
-
`;
|
|
1552
|
-
}
|
|
1553
|
-
if (resolved.version) {
|
|
1554
|
-
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
1555
|
-
`;
|
|
1556
|
-
}
|
|
1557
|
-
if (resolved.date) {
|
|
1558
|
-
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
1559
|
-
`;
|
|
1560
|
-
}
|
|
1561
|
-
bodyHtml += ` </div>
|
|
1562
|
-
`;
|
|
1563
|
-
}
|
|
1564
|
-
bodyHtml += ` </header>
|
|
1565
|
-
`;
|
|
1566
|
-
}
|
|
1567
|
-
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
1568
|
-
bodyHtml += ` <nav class="table-of-contents">
|
|
1569
|
-
`;
|
|
1570
|
-
bodyHtml += ` <h2>Table of Contents</h2>
|
|
1571
|
-
<ul>
|
|
1572
|
-
`;
|
|
1573
|
-
for (const entry of doc.tocEntries) {
|
|
1574
|
-
const indent = " ".repeat(entry.level);
|
|
1575
|
-
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
1576
|
-
`;
|
|
1577
|
-
}
|
|
1578
|
-
bodyHtml += ` </ul>
|
|
1579
|
-
</nav>
|
|
1580
|
-
`;
|
|
1581
|
-
}
|
|
1582
|
-
for (const node of doc.nodes) {
|
|
1974
|
+
for (const node of nodes) {
|
|
1583
1975
|
if (node.type === "heading") {
|
|
1584
1976
|
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
1585
1977
|
bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
|
|
@@ -1589,6 +1981,26 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1589
1981
|
if (node.type === "paragraph") {
|
|
1590
1982
|
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
1591
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>
|
|
1592
2004
|
`;
|
|
1593
2005
|
continue;
|
|
1594
2006
|
}
|
|
@@ -1677,87 +2089,734 @@ ${escapeHtml(node.text || "")}
|
|
|
1677
2089
|
continue;
|
|
1678
2090
|
}
|
|
1679
2091
|
}
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
transform: translate(-50%, -50%) rotate(${wm.rotate}deg);
|
|
1690
|
-
font-size: ${wm.fontSize}pt;
|
|
1691
|
-
font-weight: 900;
|
|
1692
|
-
color: ${wm.color};
|
|
1693
|
-
opacity: ${wm.opacity};
|
|
1694
|
-
pointer-events: none;
|
|
1695
|
-
z-index: 0;
|
|
1696
|
-
user-select: none;
|
|
1697
|
-
text-transform: uppercase;
|
|
1698
|
-
letter-spacing: 0.15em;
|
|
1699
|
-
white-space: nowrap;
|
|
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>`;
|
|
1700
2101
|
}
|
|
1701
|
-
.
|
|
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;
|
|
1702
2122
|
position: relative;
|
|
1703
|
-
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;
|
|
2201
|
+
}
|
|
2202
|
+
@media print {
|
|
2203
|
+
.markforge-cover {
|
|
2204
|
+
page-break-after: always;
|
|
2205
|
+
break-after: page;
|
|
2206
|
+
height: 100vh;
|
|
2207
|
+
min-height: 100vh;
|
|
2208
|
+
max-height: 100vh;
|
|
2209
|
+
box-sizing: border-box;
|
|
2210
|
+
overflow: hidden;
|
|
2211
|
+
margin: 0;
|
|
2212
|
+
-webkit-print-color-adjust: exact;
|
|
2213
|
+
print-color-adjust: exact;
|
|
2214
|
+
}
|
|
1704
2215
|
}
|
|
1705
2216
|
`;
|
|
1706
|
-
|
|
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>
|
|
1707
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>`;
|
|
1708
2241
|
}
|
|
1709
|
-
const
|
|
1710
|
-
const
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
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>`);
|
|
1722
2255
|
}
|
|
1723
|
-
}
|
|
1724
|
-
</script>` : "";
|
|
1725
|
-
return `<!DOCTYPE html>
|
|
1726
|
-
<html lang="${resolved.lang}">
|
|
1727
|
-
<head>
|
|
1728
|
-
<meta charset="UTF-8">
|
|
1729
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1730
|
-
<title>${escapeHtml(resolved.title)}</title>
|
|
1731
|
-
<style>
|
|
1732
|
-
${THEME_COMPONENTS}
|
|
1733
|
-
${baseThemeCss}
|
|
1734
|
-
${customCss}
|
|
1735
|
-
${inlinedCss}
|
|
1736
|
-
${watermarkCss}
|
|
1737
|
-
</style>
|
|
1738
|
-
</head>
|
|
1739
|
-
<body>
|
|
1740
|
-
${watermarkHtml} <div class="document-container">
|
|
1741
|
-
${bodyHtml} </div>
|
|
1742
|
-
${mermaidScript}
|
|
1743
|
-
</body>
|
|
1744
|
-
</html>`;
|
|
1745
|
-
}
|
|
1746
|
-
|
|
1747
|
-
// src/core/pdf/pdfBuilder.ts
|
|
1748
|
-
function findChromeExecutable() {
|
|
1749
|
-
if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
|
|
1750
|
-
return process.env.CHROME_PATH;
|
|
2256
|
+
}
|
|
1751
2257
|
}
|
|
1752
|
-
|
|
1753
|
-
|
|
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")};
|
|
1754
2275
|
}
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
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;" : ""}
|
|
2608
|
+
display: flex;
|
|
2609
|
+
flex-direction: column;
|
|
2610
|
+
${sig.style === "box" ? `border: 1px solid ${sig.borderColor}; border-radius: 6px; padding: 14px 18px; background-color: var(--mf-card-bg, #F8FAFC);` : ""}
|
|
2611
|
+
}
|
|
2612
|
+
.markforge-sig-title {
|
|
2613
|
+
font-size: 0.85rem;
|
|
2614
|
+
color: ${sig.titleColor};
|
|
2615
|
+
font-weight: 600;
|
|
2616
|
+
margin-bottom: 6px;
|
|
2617
|
+
}
|
|
2618
|
+
.markforge-sig-space {
|
|
2619
|
+
height: var(--sig-height, 60px);
|
|
2620
|
+
display: flex;
|
|
2621
|
+
align-items: center;
|
|
2622
|
+
justify-content: center;
|
|
2623
|
+
margin-bottom: 6px;
|
|
2624
|
+
}
|
|
2625
|
+
.markforge-sig-space img {
|
|
2626
|
+
max-height: 100%;
|
|
2627
|
+
max-width: 100%;
|
|
2628
|
+
object-fit: contain;
|
|
2629
|
+
}
|
|
2630
|
+
.markforge-sig-line {
|
|
2631
|
+
${sig.style === "line" ? `border-bottom: 1.5px solid ${sig.borderColor}; margin-bottom: 8px;` : ""}
|
|
2632
|
+
}
|
|
2633
|
+
.markforge-sig-name {
|
|
2634
|
+
font-size: 0.95rem;
|
|
2635
|
+
font-weight: 700;
|
|
2636
|
+
color: ${sig.nameColor};
|
|
2637
|
+
}
|
|
2638
|
+
.markforge-sig-role {
|
|
2639
|
+
font-size: 0.82rem;
|
|
2640
|
+
color: ${sig.roleColor};
|
|
2641
|
+
margin-top: 2px;
|
|
2642
|
+
}
|
|
2643
|
+
.markforge-sig-date {
|
|
2644
|
+
font-size: 0.78rem;
|
|
2645
|
+
color: ${sig.roleColor};
|
|
2646
|
+
margin-top: 2px;
|
|
2647
|
+
}
|
|
2648
|
+
@media print {
|
|
2649
|
+
.markforge-signatures {
|
|
2650
|
+
page-break-inside: avoid;
|
|
2651
|
+
break-inside: avoid;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
`;
|
|
2655
|
+
const itemCards = sig.items.map((item) => {
|
|
2656
|
+
const titleHtml = item.title ? `<div class="markforge-sig-title">${escapeHtml(item.title)}</div>` : "";
|
|
2657
|
+
let signSpaceHtml = "";
|
|
2658
|
+
if (item.image) {
|
|
2659
|
+
signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"><img src="${escapeHtml(item.image)}" alt="Signature" /></div>`;
|
|
2660
|
+
} else {
|
|
2661
|
+
signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"></div>`;
|
|
2662
|
+
}
|
|
2663
|
+
const lineHtml = sig.style === "line" ? `<div class="markforge-sig-line"></div>` : "";
|
|
2664
|
+
const nameHtml = `<div class="markforge-sig-name">${escapeHtml(item.name)}</div>`;
|
|
2665
|
+
const roleHtml = item.role ? `<div class="markforge-sig-role">${escapeHtml(item.role)}</div>` : "";
|
|
2666
|
+
const dateHtml = item.date ? `<div class="markforge-sig-date">Date: ${escapeHtml(item.date)}</div>` : "";
|
|
2667
|
+
return ` <div class="markforge-signature-card">
|
|
2668
|
+
${titleHtml}
|
|
2669
|
+
${signSpaceHtml}
|
|
2670
|
+
${lineHtml}
|
|
2671
|
+
${nameHtml}
|
|
2672
|
+
${roleHtml}
|
|
2673
|
+
${dateHtml}
|
|
2674
|
+
</div>`;
|
|
2675
|
+
}).join("\n");
|
|
2676
|
+
signaturesHtml = `
|
|
2677
|
+
<div class="markforge-signatures">
|
|
2678
|
+
${itemCards}
|
|
2679
|
+
</div>
|
|
2680
|
+
`;
|
|
2681
|
+
}
|
|
2682
|
+
const hasMermaid = doc.nodes.some((n) => n.type === "mermaid");
|
|
2683
|
+
const mermaidScript = hasMermaid ? `<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
2684
|
+
<script>
|
|
2685
|
+
mermaid.initialize({
|
|
2686
|
+
startOnLoad: true,
|
|
2687
|
+
theme: 'neutral',
|
|
2688
|
+
themeVariables: {
|
|
2689
|
+
primaryColor: '#33CDCF',
|
|
2690
|
+
primaryTextColor: '#0F172A',
|
|
2691
|
+
primaryBorderColor: '#009DA0',
|
|
2692
|
+
lineColor: '#009DA0',
|
|
2693
|
+
secondaryColor: '#ECFDFD',
|
|
2694
|
+
tertiaryColor: '#F8FAFC'
|
|
2695
|
+
}
|
|
2696
|
+
});
|
|
2697
|
+
</script>` : "";
|
|
2698
|
+
return `<!DOCTYPE html>
|
|
2699
|
+
<html lang="${resolved.lang}">
|
|
2700
|
+
<head>
|
|
2701
|
+
<meta charset="UTF-8">
|
|
2702
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2703
|
+
<title>${escapeHtml(resolved.title)}</title>
|
|
2704
|
+
<style>
|
|
2705
|
+
${THEME_COMPONENTS}
|
|
2706
|
+
${baseThemeCss}
|
|
2707
|
+
${KATEX_INLINE_CSS}
|
|
2708
|
+
${extraCss}
|
|
2709
|
+
${coverCss}
|
|
2710
|
+
${backCss}
|
|
2711
|
+
${customCss}
|
|
2712
|
+
${inlinedCss}
|
|
2713
|
+
${watermarkCss}
|
|
2714
|
+
${signaturesCss}
|
|
2715
|
+
</style>
|
|
2716
|
+
</head>
|
|
2717
|
+
<body>
|
|
2718
|
+
${watermarkHtml}${coverHtml} <div class="document-container">
|
|
2719
|
+
${bodyHtml}${footnotesHtml}${signaturesHtml} </div>
|
|
2720
|
+
${mermaidScript}
|
|
2721
|
+
${backHtml}</body>
|
|
2722
|
+
</html>`;
|
|
2723
|
+
}
|
|
2724
|
+
|
|
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
|
+
}
|
|
2807
|
+
function findChromeExecutable() {
|
|
2808
|
+
if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
|
|
2809
|
+
return process.env.CHROME_PATH;
|
|
2810
|
+
}
|
|
2811
|
+
if (process.env.PUPPETEER_EXECUTABLE_PATH && fs4.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
|
|
2812
|
+
return process.env.PUPPETEER_EXECUTABLE_PATH;
|
|
2813
|
+
}
|
|
2814
|
+
const isWin = process.platform === "win32";
|
|
2815
|
+
const winLocalAppData = process.env.LOCALAPPDATA ?? "";
|
|
2816
|
+
const winProgramFiles = process.env.PROGRAMFILES ?? "C:\\Program Files";
|
|
2817
|
+
const winProgramFilesX86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
|
|
2818
|
+
const candidates = [
|
|
2819
|
+
// Linux
|
|
1761
2820
|
"/usr/bin/google-chrome",
|
|
1762
2821
|
"/usr/bin/google-chrome-stable",
|
|
1763
2822
|
"/usr/bin/chromium",
|
|
@@ -1809,7 +2868,7 @@ function findChromeExecutable() {
|
|
|
1809
2868
|
return null;
|
|
1810
2869
|
}
|
|
1811
2870
|
function injectPagedMediaStyles(html, config, metadata) {
|
|
1812
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2871
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1813
2872
|
const resolved = resolveDocumentConfig(metadata || {}, config);
|
|
1814
2873
|
const size = resolved.paperSize;
|
|
1815
2874
|
const orientation = resolved.orientation;
|
|
@@ -1853,6 +2912,38 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
1853
2912
|
${fontStyle}
|
|
1854
2913
|
}`;
|
|
1855
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
|
+
}` : "";
|
|
1856
2947
|
const pagedCss = `
|
|
1857
2948
|
@page {
|
|
1858
2949
|
size: ${size} ${orientation};
|
|
@@ -1860,15 +2951,18 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
1860
2951
|
margin-bottom: ${bottom};
|
|
1861
2952
|
margin-left: ${left};
|
|
1862
2953
|
margin-right: ${right};
|
|
1863
|
-
${buildZoneCss("top-left", (
|
|
1864
|
-
${buildZoneCss("top-center", (
|
|
1865
|
-
${buildZoneCss("top-right", (
|
|
1866
|
-
${buildZoneCss("bottom-left", (
|
|
1867
|
-
${buildZoneCss("bottom-center", (
|
|
1868
|
-
${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)}
|
|
1869
2960
|
}
|
|
2961
|
+
${coverPageCss}
|
|
2962
|
+
${backCoverCss}
|
|
1870
2963
|
@media print {
|
|
1871
2964
|
body { padding: 0; }
|
|
2965
|
+
.document-watermark { display: none !important; }
|
|
1872
2966
|
h1, h2, h3, pre, table, blockquote, .callout {
|
|
1873
2967
|
break-inside: avoid;
|
|
1874
2968
|
}
|
|
@@ -1916,6 +3010,7 @@ startxref
|
|
|
1916
3010
|
return Buffer.from(pdfBody, "utf-8");
|
|
1917
3011
|
}
|
|
1918
3012
|
async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
3013
|
+
var _a, _b, _c;
|
|
1919
3014
|
const baseHtml = await buildHtmlDocument(doc, config, baseDir);
|
|
1920
3015
|
const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
|
|
1921
3016
|
const chromePath = findChromeExecutable();
|
|
@@ -1925,6 +3020,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1925
3020
|
const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
|
|
1926
3021
|
const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
|
|
1927
3022
|
const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
|
|
3023
|
+
const isWin = process.platform === "win32";
|
|
1928
3024
|
const isolatedFlags = [
|
|
1929
3025
|
`--user-data-dir=${tmpProfile}`,
|
|
1930
3026
|
"--no-first-run",
|
|
@@ -1935,7 +3031,6 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1935
3031
|
"--disable-default-apps",
|
|
1936
3032
|
"--disable-extensions",
|
|
1937
3033
|
"--disable-domain-reliability",
|
|
1938
|
-
"--disable-client-side-phishing-detection",
|
|
1939
3034
|
"--disable-breakpad",
|
|
1940
3035
|
"--disable-component-extensions-with-background-pages",
|
|
1941
3036
|
"--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
|
|
@@ -1944,12 +3039,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1944
3039
|
"--mute-audio",
|
|
1945
3040
|
"--no-service-autorun",
|
|
1946
3041
|
"--disable-gpu",
|
|
1947
|
-
"--no-sandbox",
|
|
1948
|
-
"--disable-setuid-sandbox",
|
|
1949
|
-
"--allow-file-access-from-files",
|
|
1950
|
-
"--disable-web-security",
|
|
3042
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
1951
3043
|
"--force-color-profile=srgb",
|
|
1952
|
-
"--no-pdf-header-footer"
|
|
3044
|
+
"--no-pdf-header-footer",
|
|
3045
|
+
"--window-size=1200,1600"
|
|
1953
3046
|
];
|
|
1954
3047
|
try {
|
|
1955
3048
|
fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
|
|
@@ -1964,7 +3057,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1964
3057
|
`--print-to-pdf=${tmpPdf}`,
|
|
1965
3058
|
fileUrl
|
|
1966
3059
|
],
|
|
1967
|
-
{ timeout: 3e4 }
|
|
3060
|
+
{ timeout: 3e4, windowsHide: true }
|
|
1968
3061
|
);
|
|
1969
3062
|
if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
|
|
1970
3063
|
res = (0, import_node_child_process.spawnSync)(
|
|
@@ -1975,12 +3068,78 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1975
3068
|
`--print-to-pdf=${tmpPdf}`,
|
|
1976
3069
|
fileUrl
|
|
1977
3070
|
],
|
|
1978
|
-
{ timeout: 3e4 }
|
|
3071
|
+
{ timeout: 3e4, windowsHide: true }
|
|
1979
3072
|
);
|
|
1980
3073
|
}
|
|
1981
3074
|
if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
|
|
1982
3075
|
const pdfBuffer = fs4.readFileSync(tmpPdf);
|
|
1983
|
-
|
|
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
|
+
}
|
|
1984
3143
|
}
|
|
1985
3144
|
} catch {
|
|
1986
3145
|
} finally {
|
|
@@ -2235,6 +3394,29 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2235
3394
|
);
|
|
2236
3395
|
continue;
|
|
2237
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
|
+
}
|
|
2238
3420
|
runs.push(
|
|
2239
3421
|
new import_docx.TextRun({
|
|
2240
3422
|
text: span.content,
|
|
@@ -2249,7 +3431,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2249
3431
|
return runs;
|
|
2250
3432
|
}
|
|
2251
3433
|
async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
2252
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
3434
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
2253
3435
|
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2254
3436
|
const docElements = [];
|
|
2255
3437
|
const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
|
|
@@ -2260,7 +3442,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2260
3442
|
const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
|
|
2261
3443
|
const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
|
|
2262
3444
|
const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
|
|
2263
|
-
if (resolved.title) {
|
|
3445
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2264
3446
|
docElements.push(
|
|
2265
3447
|
new import_docx.Paragraph({
|
|
2266
3448
|
children: [
|
|
@@ -2676,7 +3858,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2676
3858
|
}
|
|
2677
3859
|
if (node.type === "table" && node.children) {
|
|
2678
3860
|
const tableRows = [];
|
|
2679
|
-
const numCols = ((
|
|
3861
|
+
const numCols = ((_c = (_b = node.children[0]) == null ? void 0 : _b.children) == null ? void 0 : _c.length) || 1;
|
|
2680
3862
|
const colWidth = Math.floor(9e3 / numCols);
|
|
2681
3863
|
for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
|
|
2682
3864
|
const rowNode = node.children[rowIdx];
|
|
@@ -2686,7 +3868,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2686
3868
|
if (rowNode.children) {
|
|
2687
3869
|
for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
|
|
2688
3870
|
const cellNode = rowNode.children[colIdx];
|
|
2689
|
-
const align = (
|
|
3871
|
+
const align = (_d = node.align) == null ? void 0 : _d[colIdx];
|
|
2690
3872
|
let alignment = import_docx.AlignmentType.LEFT;
|
|
2691
3873
|
if (align === "center") alignment = import_docx.AlignmentType.CENTER;
|
|
2692
3874
|
if (align === "right") alignment = import_docx.AlignmentType.RIGHT;
|
|
@@ -2825,15 +4007,175 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2825
4007
|
}
|
|
2826
4008
|
continue;
|
|
2827
4009
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
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,
|
|
4123
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4124
|
+
);
|
|
4125
|
+
docElements.push(new import_docx.Paragraph({ spacing: { before: sig.spacingBeforeTwip } }));
|
|
4126
|
+
const sigCells = [];
|
|
4127
|
+
const colWidths = [];
|
|
4128
|
+
if (numItems === 1) {
|
|
4129
|
+
const cardWidth = Math.min(3400, Math.floor(contentWidth * 0.42));
|
|
4130
|
+
const spacerWidth = contentWidth - cardWidth;
|
|
4131
|
+
const cardCell = await buildDocxSignatureCell(sig.items[0], sig, cardWidth, defaultFont, baseDir);
|
|
4132
|
+
if (sig.align === "left") {
|
|
4133
|
+
colWidths.push(cardWidth, spacerWidth);
|
|
4134
|
+
sigCells.push(cardCell, createEmptyDocxCell(spacerWidth));
|
|
4135
|
+
} else if (sig.align === "center") {
|
|
4136
|
+
const sideWidth = Math.floor(spacerWidth / 2);
|
|
4137
|
+
colWidths.push(sideWidth, cardWidth, sideWidth);
|
|
4138
|
+
sigCells.push(createEmptyDocxCell(sideWidth), cardCell, createEmptyDocxCell(sideWidth));
|
|
4139
|
+
} else {
|
|
4140
|
+
colWidths.push(spacerWidth, cardWidth);
|
|
4141
|
+
sigCells.push(createEmptyDocxCell(spacerWidth), cardCell);
|
|
4142
|
+
}
|
|
4143
|
+
} else {
|
|
4144
|
+
const colWidth = Math.floor(contentWidth / numItems);
|
|
4145
|
+
for (let i = 0; i < numItems; i++) {
|
|
4146
|
+
colWidths.push(colWidth);
|
|
4147
|
+
const cell = await buildDocxSignatureCell(sig.items[i], sig, colWidth, defaultFont, baseDir);
|
|
4148
|
+
sigCells.push(cell);
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
const sigTable = new import_docx.Table({
|
|
4152
|
+
width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
|
|
4153
|
+
columnWidths: colWidths,
|
|
4154
|
+
borders: {
|
|
4155
|
+
top: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4156
|
+
bottom: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4157
|
+
left: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4158
|
+
right: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4159
|
+
insideHorizontal: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4160
|
+
insideVertical: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" }
|
|
4161
|
+
},
|
|
4162
|
+
rows: [
|
|
4163
|
+
new import_docx.TableRow({
|
|
4164
|
+
cantSplit: true,
|
|
4165
|
+
children: sigCells
|
|
4166
|
+
})
|
|
4167
|
+
]
|
|
4168
|
+
});
|
|
4169
|
+
docElements.push(sigTable);
|
|
4170
|
+
}
|
|
4171
|
+
const contentWidthTwip = Math.max(
|
|
4172
|
+
1e3,
|
|
4173
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4174
|
+
);
|
|
4175
|
+
const centerPos = Math.round(contentWidthTwip / 2);
|
|
4176
|
+
const rightPos = contentWidthTwip;
|
|
4177
|
+
const headerRuns = [];
|
|
4178
|
+
if ((_e = resolved.header) == null ? void 0 : _e.left) {
|
|
2837
4179
|
headerRuns.push(
|
|
2838
4180
|
new import_docx.TextRun({
|
|
2839
4181
|
text: resolved.header.left.text,
|
|
@@ -2846,7 +4188,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2846
4188
|
);
|
|
2847
4189
|
}
|
|
2848
4190
|
headerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
2849
|
-
if ((
|
|
4191
|
+
if ((_f = resolved.header) == null ? void 0 : _f.center) {
|
|
2850
4192
|
headerRuns.push(
|
|
2851
4193
|
new import_docx.TextRun({
|
|
2852
4194
|
text: resolved.header.center.text,
|
|
@@ -2859,7 +4201,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2859
4201
|
);
|
|
2860
4202
|
}
|
|
2861
4203
|
headerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
2862
|
-
if ((
|
|
4204
|
+
if ((_g = resolved.header) == null ? void 0 : _g.right) {
|
|
2863
4205
|
headerRuns.push(
|
|
2864
4206
|
new import_docx.TextRun({
|
|
2865
4207
|
text: resolved.header.right.text,
|
|
@@ -2898,7 +4240,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2898
4240
|
]
|
|
2899
4241
|
}) : void 0;
|
|
2900
4242
|
const footerRuns = [];
|
|
2901
|
-
if ((
|
|
4243
|
+
if ((_h = resolved.footer) == null ? void 0 : _h.left) {
|
|
2902
4244
|
footerRuns.push(
|
|
2903
4245
|
new import_docx.TextRun({
|
|
2904
4246
|
text: resolved.footer.left.text,
|
|
@@ -2911,7 +4253,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2911
4253
|
);
|
|
2912
4254
|
}
|
|
2913
4255
|
footerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
2914
|
-
if ((
|
|
4256
|
+
if ((_i = resolved.footer) == null ? void 0 : _i.center) {
|
|
2915
4257
|
footerRuns.push(
|
|
2916
4258
|
new import_docx.TextRun({
|
|
2917
4259
|
text: resolved.footer.center.text,
|
|
@@ -2924,7 +4266,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2924
4266
|
);
|
|
2925
4267
|
}
|
|
2926
4268
|
footerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
2927
|
-
if ((
|
|
4269
|
+
if ((_j = resolved.footer) == null ? void 0 : _j.right) {
|
|
2928
4270
|
const rZone = resolved.footer.right;
|
|
2929
4271
|
const rColor = rZone.color.replace("#", "");
|
|
2930
4272
|
const rSize = (rZone.fontSize || 9) * 2;
|
|
@@ -3009,6 +4351,91 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3009
4351
|
]
|
|
3010
4352
|
}) : void 0;
|
|
3011
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
|
+
}
|
|
3012
4439
|
const document = new import_docx.Document({
|
|
3013
4440
|
styles: {
|
|
3014
4441
|
default: {
|
|
@@ -3029,32 +4456,383 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3029
4456
|
}
|
|
3030
4457
|
}
|
|
3031
4458
|
},
|
|
3032
|
-
sections:
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
4459
|
+
sections: docSections
|
|
4460
|
+
});
|
|
4461
|
+
return await import_docx.Packer.toBuffer(document);
|
|
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
|
+
}
|
|
4703
|
+
async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
|
|
4704
|
+
const cellParagraphs = [];
|
|
4705
|
+
if (item.title) {
|
|
4706
|
+
cellParagraphs.push(
|
|
4707
|
+
new import_docx.Paragraph({
|
|
4708
|
+
children: [
|
|
4709
|
+
new import_docx.TextRun({
|
|
4710
|
+
text: item.title,
|
|
4711
|
+
font: defaultFont,
|
|
4712
|
+
size: 18,
|
|
4713
|
+
// 9pt
|
|
4714
|
+
color: sig.titleColor.replace("#", ""),
|
|
4715
|
+
bold: true
|
|
4716
|
+
})
|
|
4717
|
+
],
|
|
4718
|
+
spacing: { after: 60 }
|
|
4719
|
+
})
|
|
4720
|
+
);
|
|
4721
|
+
}
|
|
4722
|
+
if (item.image) {
|
|
4723
|
+
const resolvedImg = await resolveImage(item.image, baseDir);
|
|
4724
|
+
if (resolvedImg) {
|
|
4725
|
+
cellParagraphs.push(
|
|
4726
|
+
new import_docx.Paragraph({
|
|
4727
|
+
children: [
|
|
4728
|
+
new import_docx.ImageRun({
|
|
4729
|
+
data: resolvedImg.buffer,
|
|
4730
|
+
transformation: {
|
|
4731
|
+
width: 140,
|
|
4732
|
+
height: 60
|
|
4733
|
+
},
|
|
4734
|
+
type: "png"
|
|
4735
|
+
})
|
|
4736
|
+
],
|
|
4737
|
+
spacing: { before: 40, after: 40 }
|
|
4738
|
+
})
|
|
4739
|
+
);
|
|
4740
|
+
} else {
|
|
4741
|
+
cellParagraphs.push(new import_docx.Paragraph({ spacing: { before: 240, after: 240 } }));
|
|
4742
|
+
}
|
|
4743
|
+
} else {
|
|
4744
|
+
cellParagraphs.push(new import_docx.Paragraph({ spacing: { before: 240, after: 240 } }));
|
|
4745
|
+
}
|
|
4746
|
+
if (sig.style === "line") {
|
|
4747
|
+
cellParagraphs.push(
|
|
4748
|
+
new import_docx.Paragraph({
|
|
4749
|
+
border: {
|
|
4750
|
+
bottom: {
|
|
4751
|
+
style: import_docx.BorderStyle.SINGLE,
|
|
4752
|
+
size: 6,
|
|
4753
|
+
space: 2,
|
|
4754
|
+
color: sig.borderColor.replace("#", "")
|
|
3049
4755
|
}
|
|
3050
4756
|
},
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
4757
|
+
spacing: { after: 60 }
|
|
4758
|
+
})
|
|
4759
|
+
);
|
|
4760
|
+
}
|
|
4761
|
+
cellParagraphs.push(
|
|
4762
|
+
new import_docx.Paragraph({
|
|
4763
|
+
children: [
|
|
4764
|
+
new import_docx.TextRun({
|
|
4765
|
+
text: item.name,
|
|
4766
|
+
font: defaultFont,
|
|
4767
|
+
size: 21,
|
|
4768
|
+
// 10.5pt
|
|
4769
|
+
bold: true,
|
|
4770
|
+
color: sig.nameColor.replace("#", "")
|
|
4771
|
+
})
|
|
4772
|
+
],
|
|
4773
|
+
spacing: { before: sig.style === "line" ? 40 : 20, after: 20 }
|
|
4774
|
+
})
|
|
4775
|
+
);
|
|
4776
|
+
if (item.role) {
|
|
4777
|
+
cellParagraphs.push(
|
|
4778
|
+
new import_docx.Paragraph({
|
|
4779
|
+
children: [
|
|
4780
|
+
new import_docx.TextRun({
|
|
4781
|
+
text: item.role,
|
|
4782
|
+
font: defaultFont,
|
|
4783
|
+
size: 18,
|
|
4784
|
+
// 9pt
|
|
4785
|
+
color: sig.roleColor.replace("#", "")
|
|
4786
|
+
})
|
|
4787
|
+
],
|
|
4788
|
+
spacing: { after: 20 }
|
|
4789
|
+
})
|
|
4790
|
+
);
|
|
4791
|
+
}
|
|
4792
|
+
if (item.date) {
|
|
4793
|
+
cellParagraphs.push(
|
|
4794
|
+
new import_docx.Paragraph({
|
|
4795
|
+
children: [
|
|
4796
|
+
new import_docx.TextRun({
|
|
4797
|
+
text: `Date: ${item.date}`,
|
|
4798
|
+
font: defaultFont,
|
|
4799
|
+
size: 17,
|
|
4800
|
+
// 8.5pt
|
|
4801
|
+
color: sig.roleColor.replace("#", "")
|
|
4802
|
+
})
|
|
4803
|
+
],
|
|
4804
|
+
spacing: { after: 20 }
|
|
4805
|
+
})
|
|
4806
|
+
);
|
|
4807
|
+
}
|
|
4808
|
+
const isBox = sig.style === "box";
|
|
4809
|
+
const boxBorder = { style: import_docx.BorderStyle.SINGLE, size: 4, color: sig.borderColor.replace("#", "") };
|
|
4810
|
+
const noneBorder = { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" };
|
|
4811
|
+
return new import_docx.TableCell({
|
|
4812
|
+
width: { size: widthDxa, type: import_docx.WidthType.DXA },
|
|
4813
|
+
shading: isBox ? { fill: "F8FAFC", type: import_docx.ShadingType.CLEAR } : void 0,
|
|
4814
|
+
margins: isBox ? { top: 140, bottom: 140, left: 160, right: 160 } : { top: 60, bottom: 60, left: 60, right: 60 },
|
|
4815
|
+
borders: {
|
|
4816
|
+
top: isBox ? boxBorder : noneBorder,
|
|
4817
|
+
bottom: isBox ? boxBorder : noneBorder,
|
|
4818
|
+
left: isBox ? boxBorder : noneBorder,
|
|
4819
|
+
right: isBox ? boxBorder : noneBorder
|
|
4820
|
+
},
|
|
4821
|
+
children: cellParagraphs
|
|
4822
|
+
});
|
|
4823
|
+
}
|
|
4824
|
+
function createEmptyDocxCell(widthDxa) {
|
|
4825
|
+
const noneBorder = { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" };
|
|
4826
|
+
return new import_docx.TableCell({
|
|
4827
|
+
width: { size: widthDxa, type: import_docx.WidthType.DXA },
|
|
4828
|
+
borders: {
|
|
4829
|
+
top: noneBorder,
|
|
4830
|
+
bottom: noneBorder,
|
|
4831
|
+
left: noneBorder,
|
|
4832
|
+
right: noneBorder
|
|
4833
|
+
},
|
|
4834
|
+
children: [new import_docx.Paragraph({})]
|
|
3056
4835
|
});
|
|
3057
|
-
return await import_docx.Packer.toBuffer(document);
|
|
3058
4836
|
}
|
|
3059
4837
|
|
|
3060
4838
|
// src/core/engine.ts
|
|
@@ -3149,14 +4927,885 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
|
|
|
3149
4927
|
};
|
|
3150
4928
|
}
|
|
3151
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
|
+
|
|
3152
5801
|
// src/config/defineConfig.ts
|
|
3153
5802
|
function defineConfig(config) {
|
|
3154
5803
|
return config;
|
|
3155
5804
|
}
|
|
3156
5805
|
|
|
3157
5806
|
// src/version.ts
|
|
3158
|
-
var
|
|
3159
|
-
var
|
|
5807
|
+
var fs8 = __toESM(require("fs"));
|
|
5808
|
+
var path8 = __toESM(require("path"));
|
|
3160
5809
|
var import_node_url4 = require("url");
|
|
3161
5810
|
var import_meta = {};
|
|
3162
5811
|
try {
|
|
@@ -3179,21 +5828,21 @@ try {
|
|
|
3179
5828
|
}
|
|
3180
5829
|
} catch {
|
|
3181
5830
|
}
|
|
3182
|
-
var FALLBACK_VERSION = "0.
|
|
5831
|
+
var FALLBACK_VERSION = "0.4.0";
|
|
3183
5832
|
function readVersionFromPackageJson(fromDir) {
|
|
3184
5833
|
let currentDir = fromDir;
|
|
3185
5834
|
for (let i = 0; i < 6; i++) {
|
|
3186
5835
|
try {
|
|
3187
|
-
const pkgJsonPath =
|
|
3188
|
-
if (
|
|
3189
|
-
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"));
|
|
3190
5839
|
if (pkg.name === "@masumdev/markforge" && pkg.version) {
|
|
3191
5840
|
return pkg.version;
|
|
3192
5841
|
}
|
|
3193
5842
|
}
|
|
3194
5843
|
} catch {
|
|
3195
5844
|
}
|
|
3196
|
-
const parentDir =
|
|
5845
|
+
const parentDir = path8.dirname(currentDir);
|
|
3197
5846
|
if (parentDir === currentDir) break;
|
|
3198
5847
|
currentDir = parentDir;
|
|
3199
5848
|
}
|
|
@@ -3204,7 +5853,7 @@ function getPackageDir() {
|
|
|
3204
5853
|
return __dirname;
|
|
3205
5854
|
}
|
|
3206
5855
|
try {
|
|
3207
|
-
return
|
|
5856
|
+
return path8.dirname((0, import_node_url4.fileURLToPath)(import_meta.url));
|
|
3208
5857
|
} catch {
|
|
3209
5858
|
return process.cwd();
|
|
3210
5859
|
}
|
|
@@ -3216,6 +5865,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3216
5865
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3217
5866
|
0 && (module.exports = {
|
|
3218
5867
|
DEFAULT_CONFIG,
|
|
5868
|
+
KATEX_INLINE_CSS,
|
|
3219
5869
|
MARKFORGE_VERSION,
|
|
3220
5870
|
Orientation,
|
|
3221
5871
|
OutputFormat,
|
|
@@ -3244,17 +5894,28 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3244
5894
|
inlineHtmlImages,
|
|
3245
5895
|
loadConfig,
|
|
3246
5896
|
markforge,
|
|
5897
|
+
normalizeBackCover,
|
|
5898
|
+
normalizeCoverPage,
|
|
3247
5899
|
normalizeHeaderFooter,
|
|
3248
5900
|
normalizeHeaderFooterSlot,
|
|
5901
|
+
normalizeNumberHeadings,
|
|
5902
|
+
normalizeSecurity,
|
|
5903
|
+
normalizeSignatures,
|
|
3249
5904
|
normalizeWatermark,
|
|
3250
5905
|
parseInlineSpans,
|
|
3251
5906
|
parseMarginToTwip,
|
|
5907
|
+
parseMarkdown,
|
|
3252
5908
|
parseMarkdownDocument,
|
|
5909
|
+
renderBackCoverHtml,
|
|
5910
|
+
renderCoverPageHtml,
|
|
3253
5911
|
renderInlinesToHtml,
|
|
5912
|
+
renderMathToHtml,
|
|
3254
5913
|
renderMermaidToPng,
|
|
5914
|
+
renderNodesToHtml,
|
|
3255
5915
|
replaceDocumentTokens,
|
|
3256
5916
|
resolveDocumentConfig,
|
|
3257
5917
|
resolveImage,
|
|
3258
5918
|
slugify,
|
|
5919
|
+
startPreviewServer,
|
|
3259
5920
|
tokenizeCodeLine
|
|
3260
5921
|
});
|