@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.mjs
CHANGED
|
@@ -49,6 +49,26 @@ function parseInlineSpans(text) {
|
|
|
49
49
|
remaining = remaining.slice(imgMatch[0].length);
|
|
50
50
|
continue;
|
|
51
51
|
}
|
|
52
|
+
const fnMatch = remaining.match(/^\[\^([\w-]+)\]/);
|
|
53
|
+
if (fnMatch) {
|
|
54
|
+
const fnId = fnMatch[1];
|
|
55
|
+
spans.push({
|
|
56
|
+
type: "footnoteRef",
|
|
57
|
+
content: fnId,
|
|
58
|
+
footnoteId: fnId
|
|
59
|
+
});
|
|
60
|
+
remaining = remaining.slice(fnMatch[0].length);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const mathMatch = remaining.match(/^\$([^$\n]+?)\$/);
|
|
64
|
+
if (mathMatch && !mathMatch[1].startsWith("$")) {
|
|
65
|
+
spans.push({
|
|
66
|
+
type: "mathInline",
|
|
67
|
+
content: mathMatch[1]
|
|
68
|
+
});
|
|
69
|
+
remaining = remaining.slice(mathMatch[0].length);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
52
72
|
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/);
|
|
53
73
|
if (linkMatch) {
|
|
54
74
|
spans.push({
|
|
@@ -148,7 +168,7 @@ function parseInlineSpans(text) {
|
|
|
148
168
|
remaining = remaining.slice(fullTag.length);
|
|
149
169
|
continue;
|
|
150
170
|
}
|
|
151
|
-
const nextSpecial = remaining.search(/[\*\_\[
|
|
171
|
+
const nextSpecial = remaining.search(/[\*\_\[\!`~<\$]/);
|
|
152
172
|
if (nextSpecial === -1) {
|
|
153
173
|
spans.push({
|
|
154
174
|
type: "text",
|
|
@@ -174,6 +194,35 @@ function parseInlineSpans(text) {
|
|
|
174
194
|
function slugify(text) {
|
|
175
195
|
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
176
196
|
}
|
|
197
|
+
function applyHeadingNumbering(nodes, tocEntries, options) {
|
|
198
|
+
const depth = options.depth ?? 3;
|
|
199
|
+
const skipH1 = options.skipH1 ?? false;
|
|
200
|
+
const prefix = options.prefix ?? "";
|
|
201
|
+
const counters = [0, 0, 0, 0, 0, 0];
|
|
202
|
+
for (const node of nodes) {
|
|
203
|
+
if (node.type === "heading" && node.level) {
|
|
204
|
+
const lvl = node.level;
|
|
205
|
+
if (lvl > depth) continue;
|
|
206
|
+
if (lvl === 1 && skipH1) continue;
|
|
207
|
+
const idx = lvl - 1;
|
|
208
|
+
counters[idx]++;
|
|
209
|
+
for (let c = idx + 1; c < counters.length; c++) {
|
|
210
|
+
counters[c] = 0;
|
|
211
|
+
}
|
|
212
|
+
const startIdx = skipH1 ? 1 : 0;
|
|
213
|
+
const parts = counters.slice(startIdx, idx + 1).filter((n) => n > 0);
|
|
214
|
+
const numberStr = parts.join(".") + ".";
|
|
215
|
+
const fullPrefix = prefix ? `${prefix} ${numberStr} ` : `${numberStr} `;
|
|
216
|
+
const originalText = node.text || "";
|
|
217
|
+
node.text = fullPrefix + originalText;
|
|
218
|
+
node.inlines = parseInlineSpans(node.text);
|
|
219
|
+
const toc = tocEntries.find((t) => t.id === node.id);
|
|
220
|
+
if (toc) {
|
|
221
|
+
toc.text = node.text;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
177
226
|
function parseMarkdownDocument(rawMarkdown) {
|
|
178
227
|
const { data: frontmatter, content } = matter(rawMarkdown);
|
|
179
228
|
const metadata = frontmatter || {};
|
|
@@ -185,6 +234,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
185
234
|
const lines = cleanContent.split(/\r?\n/);
|
|
186
235
|
const nodes = [];
|
|
187
236
|
const tocEntries = [];
|
|
237
|
+
const footnoteDefs = [];
|
|
188
238
|
let i = 0;
|
|
189
239
|
while (i < lines.length) {
|
|
190
240
|
const line = lines[i];
|
|
@@ -213,6 +263,29 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
213
263
|
i++;
|
|
214
264
|
continue;
|
|
215
265
|
}
|
|
266
|
+
if (line.trim().startsWith("$$")) {
|
|
267
|
+
const mathLines = [];
|
|
268
|
+
const singleLine = line.trim().match(/^\$\$(.+)\$\$$/);
|
|
269
|
+
if (singleLine) {
|
|
270
|
+
nodes.push({
|
|
271
|
+
type: "mathBlock",
|
|
272
|
+
text: singleLine[1].trim()
|
|
273
|
+
});
|
|
274
|
+
i++;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
i++;
|
|
278
|
+
while (i < lines.length && !lines[i].trim().startsWith("$$")) {
|
|
279
|
+
mathLines.push(lines[i]);
|
|
280
|
+
i++;
|
|
281
|
+
}
|
|
282
|
+
if (i < lines.length) i++;
|
|
283
|
+
nodes.push({
|
|
284
|
+
type: "mathBlock",
|
|
285
|
+
text: mathLines.join("\n").trim()
|
|
286
|
+
});
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
216
289
|
const codeBlockMatch = line.match(/^```(\w+)?/);
|
|
217
290
|
if (codeBlockMatch) {
|
|
218
291
|
const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
|
|
@@ -239,6 +312,100 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
239
312
|
}
|
|
240
313
|
continue;
|
|
241
314
|
}
|
|
315
|
+
const colsMatch = line.trim().match(/^:::columns(?:\s+\[?([\w\s=.-]+)\]?)?$/i);
|
|
316
|
+
if (colsMatch) {
|
|
317
|
+
const attrStr = colsMatch[1] || "";
|
|
318
|
+
let colsCount = 2;
|
|
319
|
+
let colGap = "1.5rem";
|
|
320
|
+
if (attrStr) {
|
|
321
|
+
const numMatch = attrStr.trim().match(/^(\d+)$/);
|
|
322
|
+
const cMatch = attrStr.match(/cols=(\d+)/i) || attrStr.match(/columns=(\d+)/i);
|
|
323
|
+
const gMatch = attrStr.match(/gap=([^\s]+)/i);
|
|
324
|
+
if (numMatch) colsCount = parseInt(numMatch[1], 10);
|
|
325
|
+
else if (cMatch) colsCount = parseInt(cMatch[1], 10);
|
|
326
|
+
if (gMatch) colGap = gMatch[1];
|
|
327
|
+
}
|
|
328
|
+
const columnNodes = [];
|
|
329
|
+
let currentColumnLines = [];
|
|
330
|
+
let inColBlock = false;
|
|
331
|
+
i++;
|
|
332
|
+
while (i < lines.length) {
|
|
333
|
+
const curLine = lines[i];
|
|
334
|
+
const trimmed = curLine.trim();
|
|
335
|
+
if (/^:::col(?:umn)?$/i.test(trimmed)) {
|
|
336
|
+
if (currentColumnLines.length > 0) {
|
|
337
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
338
|
+
columnNodes.push({
|
|
339
|
+
type: "column",
|
|
340
|
+
children: subDoc.nodes
|
|
341
|
+
});
|
|
342
|
+
currentColumnLines = [];
|
|
343
|
+
}
|
|
344
|
+
inColBlock = true;
|
|
345
|
+
i++;
|
|
346
|
+
} else if (trimmed === ":::") {
|
|
347
|
+
if (inColBlock) {
|
|
348
|
+
if (currentColumnLines.length > 0) {
|
|
349
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
350
|
+
columnNodes.push({
|
|
351
|
+
type: "column",
|
|
352
|
+
children: subDoc.nodes
|
|
353
|
+
});
|
|
354
|
+
currentColumnLines = [];
|
|
355
|
+
}
|
|
356
|
+
inColBlock = false;
|
|
357
|
+
i++;
|
|
358
|
+
} else {
|
|
359
|
+
if (currentColumnLines.length > 0) {
|
|
360
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
361
|
+
columnNodes.push({
|
|
362
|
+
type: "column",
|
|
363
|
+
children: subDoc.nodes
|
|
364
|
+
});
|
|
365
|
+
currentColumnLines = [];
|
|
366
|
+
}
|
|
367
|
+
i++;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
currentColumnLines.push(curLine);
|
|
372
|
+
i++;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (currentColumnLines.length > 0) {
|
|
376
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
377
|
+
columnNodes.push({
|
|
378
|
+
type: "column",
|
|
379
|
+
children: subDoc.nodes
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
nodes.push({
|
|
383
|
+
type: "columns",
|
|
384
|
+
columnsCount: columnNodes.length > 0 ? columnNodes.length : colsCount,
|
|
385
|
+
columnGap: colGap,
|
|
386
|
+
children: columnNodes
|
|
387
|
+
});
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
const fnDefMatch = line.match(/^\[\^([\w-]+)\]:\s+(.+)$/);
|
|
391
|
+
if (fnDefMatch) {
|
|
392
|
+
const fnId = fnDefMatch[1];
|
|
393
|
+
const fnText = fnDefMatch[2].trim();
|
|
394
|
+
const inlines = parseInlineSpans(fnText);
|
|
395
|
+
footnoteDefs.push({
|
|
396
|
+
id: fnId,
|
|
397
|
+
text: fnText,
|
|
398
|
+
inlines
|
|
399
|
+
});
|
|
400
|
+
nodes.push({
|
|
401
|
+
type: "footnoteDef",
|
|
402
|
+
footnoteId: fnId,
|
|
403
|
+
text: fnText,
|
|
404
|
+
inlines
|
|
405
|
+
});
|
|
406
|
+
i++;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
242
409
|
const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
|
|
243
410
|
if (calloutMatch) {
|
|
244
411
|
const calloutType = calloutMatch[1].toUpperCase();
|
|
@@ -362,7 +529,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
362
529
|
continue;
|
|
363
530
|
}
|
|
364
531
|
const paraLines = [];
|
|
365
|
-
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+/)) {
|
|
532
|
+
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+/)) {
|
|
366
533
|
paraLines.push(lines[i]);
|
|
367
534
|
i++;
|
|
368
535
|
}
|
|
@@ -373,12 +540,19 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
373
540
|
inlines: parseInlineSpans(paraText)
|
|
374
541
|
});
|
|
375
542
|
}
|
|
543
|
+
if (metadata.numberHeadings) {
|
|
544
|
+
const opts = typeof metadata.numberHeadings === "boolean" ? { enabled: metadata.numberHeadings } : metadata.numberHeadings;
|
|
545
|
+
if (opts.enabled !== false) {
|
|
546
|
+
applyHeadingNumbering(nodes, tocEntries, opts);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
376
549
|
return {
|
|
377
550
|
metadata,
|
|
378
551
|
content: cleanContent,
|
|
379
552
|
nodes,
|
|
380
553
|
tocEntries,
|
|
381
|
-
inlinedStyles
|
|
554
|
+
inlinedStyles,
|
|
555
|
+
footnoteDefs
|
|
382
556
|
};
|
|
383
557
|
}
|
|
384
558
|
|
|
@@ -466,9 +640,14 @@ async function resolveImage(src, baseDir = process.cwd()) {
|
|
|
466
640
|
memoryImageCache.set(cacheKey, resolved2);
|
|
467
641
|
return resolved2;
|
|
468
642
|
}
|
|
469
|
-
|
|
643
|
+
let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
|
|
470
644
|
if (!fs.existsSync(localPath)) {
|
|
471
|
-
|
|
645
|
+
const cwdPath = path.resolve(process.cwd(), src);
|
|
646
|
+
if (fs.existsSync(cwdPath)) {
|
|
647
|
+
localPath = cwdPath;
|
|
648
|
+
} else {
|
|
649
|
+
return null;
|
|
650
|
+
}
|
|
472
651
|
}
|
|
473
652
|
const buffer = fs.readFileSync(localPath);
|
|
474
653
|
const mimeType = getMimeType(localPath);
|
|
@@ -873,6 +1052,8 @@ import * as path4 from "path";
|
|
|
873
1052
|
import * as os from "os";
|
|
874
1053
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
875
1054
|
import { spawnSync } from "child_process";
|
|
1055
|
+
import { PDFDocument } from "pdf-lib";
|
|
1056
|
+
import { encryptPDF } from "@pdfsmaller/pdf-encrypt";
|
|
876
1057
|
|
|
877
1058
|
// src/core/html/htmlBuilder.ts
|
|
878
1059
|
import * as fs3 from "fs";
|
|
@@ -1115,6 +1296,12 @@ var DEFAULT_CONFIG = {
|
|
|
1115
1296
|
},
|
|
1116
1297
|
toc: false,
|
|
1117
1298
|
watermark: void 0,
|
|
1299
|
+
signatures: void 0,
|
|
1300
|
+
coverPage: void 0,
|
|
1301
|
+
backCover: void 0,
|
|
1302
|
+
numberHeadings: void 0,
|
|
1303
|
+
security: void 0,
|
|
1304
|
+
math: true,
|
|
1118
1305
|
embedImages: true,
|
|
1119
1306
|
metadata: void 0,
|
|
1120
1307
|
watch: false,
|
|
@@ -1271,7 +1458,8 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
|
|
|
1271
1458
|
return str;
|
|
1272
1459
|
}
|
|
1273
1460
|
function replaceDocumentTokens(template = "", meta) {
|
|
1274
|
-
|
|
1461
|
+
const currentYear = meta.year || (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1462
|
+
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 || "");
|
|
1275
1463
|
}
|
|
1276
1464
|
function normalizeWatermark(rawWatermark) {
|
|
1277
1465
|
if (!rawWatermark) {
|
|
@@ -1347,6 +1535,190 @@ function normalizeHeaderFooter(raw, meta) {
|
|
|
1347
1535
|
dividerColor: raw.dividerColor || "#E2E8F0"
|
|
1348
1536
|
};
|
|
1349
1537
|
}
|
|
1538
|
+
function normalizeSignatures(raw, meta = {}) {
|
|
1539
|
+
if (!raw) return void 0;
|
|
1540
|
+
let rawItems = [];
|
|
1541
|
+
let rawConfig = {};
|
|
1542
|
+
if (Array.isArray(raw)) {
|
|
1543
|
+
rawItems = raw;
|
|
1544
|
+
} else if (typeof raw === "object" && Array.isArray(raw.items)) {
|
|
1545
|
+
rawItems = raw.items;
|
|
1546
|
+
rawConfig = raw;
|
|
1547
|
+
}
|
|
1548
|
+
if (rawItems.length === 0) return void 0;
|
|
1549
|
+
const cappedItems = rawItems.slice(0, 4);
|
|
1550
|
+
const items = cappedItems.map((item) => {
|
|
1551
|
+
const tokenCtx = meta;
|
|
1552
|
+
const rawName = typeof item.name === "string" ? item.name : "";
|
|
1553
|
+
const name = replaceDocumentTokens(rawName, tokenCtx).trim();
|
|
1554
|
+
const title = item.title ? replaceDocumentTokens(item.title, tokenCtx).trim() : void 0;
|
|
1555
|
+
const role = item.role ? replaceDocumentTokens(item.role, tokenCtx).trim() : void 0;
|
|
1556
|
+
let dateStr;
|
|
1557
|
+
if (typeof item.date === "string") {
|
|
1558
|
+
dateStr = replaceDocumentTokens(item.date, tokenCtx).trim();
|
|
1559
|
+
} else if (item.date === true) {
|
|
1560
|
+
dateStr = meta.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1561
|
+
}
|
|
1562
|
+
let signatureHeight = 60;
|
|
1563
|
+
if (typeof item.signatureHeight === "number") {
|
|
1564
|
+
signatureHeight = item.signatureHeight;
|
|
1565
|
+
} else if (typeof item.signatureHeight === "string") {
|
|
1566
|
+
const parsed = parseFloat(item.signatureHeight);
|
|
1567
|
+
if (!isNaN(parsed)) signatureHeight = parsed;
|
|
1568
|
+
}
|
|
1569
|
+
return {
|
|
1570
|
+
title,
|
|
1571
|
+
name: name || "Authorized Signatory",
|
|
1572
|
+
role,
|
|
1573
|
+
date: dateStr,
|
|
1574
|
+
image: item.image,
|
|
1575
|
+
signatureHeight
|
|
1576
|
+
};
|
|
1577
|
+
});
|
|
1578
|
+
const align = rawConfig.align || (items.length === 1 ? "right" : "space-between");
|
|
1579
|
+
const style = rawConfig.style || "line";
|
|
1580
|
+
const borderColor = rawConfig.borderColor || "#CBD5E1";
|
|
1581
|
+
const titleColor = rawConfig.titleColor || "#64748B";
|
|
1582
|
+
const nameColor = rawConfig.nameColor || "#0F172A";
|
|
1583
|
+
const roleColor = rawConfig.roleColor || "#64748B";
|
|
1584
|
+
const spacingBeforeRaw = rawConfig.spacingBefore ?? "2.5rem";
|
|
1585
|
+
const spacingBefore = formatMarginCss(spacingBeforeRaw, "2.5rem");
|
|
1586
|
+
const spacingBeforeTwip = parseMarginToTwip(spacingBeforeRaw, 600);
|
|
1587
|
+
return {
|
|
1588
|
+
items,
|
|
1589
|
+
align,
|
|
1590
|
+
style,
|
|
1591
|
+
borderColor,
|
|
1592
|
+
titleColor,
|
|
1593
|
+
nameColor,
|
|
1594
|
+
roleColor,
|
|
1595
|
+
spacingBefore,
|
|
1596
|
+
spacingBeforeTwip
|
|
1597
|
+
};
|
|
1598
|
+
}
|
|
1599
|
+
function normalizeCoverPage(rawCover, tokenCtx = {}) {
|
|
1600
|
+
if (!rawCover) return void 0;
|
|
1601
|
+
const cfg = typeof rawCover === "object" ? rawCover : {};
|
|
1602
|
+
if (cfg.enabled === false) return void 0;
|
|
1603
|
+
const preset = cfg.preset || "modern";
|
|
1604
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title || "Document Title";
|
|
1605
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle;
|
|
1606
|
+
const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author;
|
|
1607
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
|
|
1608
|
+
const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version;
|
|
1609
|
+
let dateStr;
|
|
1610
|
+
if (typeof cfg.date === "string") {
|
|
1611
|
+
dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
|
|
1612
|
+
} else if (cfg.date === true) {
|
|
1613
|
+
dateStr = tokenCtx.date || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1614
|
+
} else {
|
|
1615
|
+
dateStr = tokenCtx.date;
|
|
1616
|
+
}
|
|
1617
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1618
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1619
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1620
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1621
|
+
const logoWidth = cfg.logoWidth;
|
|
1622
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
|
|
1623
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1624
|
+
const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
|
|
1625
|
+
return {
|
|
1626
|
+
enabled: true,
|
|
1627
|
+
preset,
|
|
1628
|
+
title,
|
|
1629
|
+
subtitle,
|
|
1630
|
+
author,
|
|
1631
|
+
company,
|
|
1632
|
+
version,
|
|
1633
|
+
date: dateStr,
|
|
1634
|
+
badge,
|
|
1635
|
+
badgeColor,
|
|
1636
|
+
badgeTextColor,
|
|
1637
|
+
logo,
|
|
1638
|
+
logoWidth,
|
|
1639
|
+
bgGradient,
|
|
1640
|
+
textColor,
|
|
1641
|
+
footerText
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
function normalizeBackCover(rawBack, tokenCtx = {}) {
|
|
1645
|
+
if (!rawBack) return void 0;
|
|
1646
|
+
const cfg = typeof rawBack === "object" ? rawBack : {};
|
|
1647
|
+
if (cfg.enabled === false) return void 0;
|
|
1648
|
+
const preset = cfg.preset || "modern";
|
|
1649
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
|
|
1650
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
|
|
1651
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company;
|
|
1652
|
+
const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
|
|
1653
|
+
const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
|
|
1654
|
+
const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
|
|
1655
|
+
const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
|
|
1656
|
+
let socialMap;
|
|
1657
|
+
if (cfg.social && typeof cfg.social === "object") {
|
|
1658
|
+
socialMap = {};
|
|
1659
|
+
for (const [k, v] of Object.entries(cfg.social)) {
|
|
1660
|
+
if (typeof v === "string") {
|
|
1661
|
+
socialMap[k] = replaceDocumentTokens(v, tokenCtx);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1666
|
+
const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
|
|
1667
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1668
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1669
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1670
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1671
|
+
const logoWidth = cfg.logoWidth;
|
|
1672
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : void 0;
|
|
1673
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1674
|
+
return {
|
|
1675
|
+
enabled: true,
|
|
1676
|
+
preset,
|
|
1677
|
+
title,
|
|
1678
|
+
subtitle,
|
|
1679
|
+
company,
|
|
1680
|
+
address,
|
|
1681
|
+
email,
|
|
1682
|
+
phone,
|
|
1683
|
+
website,
|
|
1684
|
+
social: socialMap,
|
|
1685
|
+
copyright,
|
|
1686
|
+
badge,
|
|
1687
|
+
badgeColor,
|
|
1688
|
+
badgeTextColor,
|
|
1689
|
+
logo,
|
|
1690
|
+
logoWidth,
|
|
1691
|
+
bgGradient,
|
|
1692
|
+
textColor
|
|
1693
|
+
};
|
|
1694
|
+
}
|
|
1695
|
+
function normalizeNumberHeadings(raw) {
|
|
1696
|
+
if (raw === void 0 || raw === false) return void 0;
|
|
1697
|
+
if (raw === true) {
|
|
1698
|
+
return { enabled: true, depth: 3, skipH1: false, prefix: "" };
|
|
1699
|
+
}
|
|
1700
|
+
if (typeof raw === "object") {
|
|
1701
|
+
const obj = raw;
|
|
1702
|
+
if (obj.enabled === false) return void 0;
|
|
1703
|
+
return {
|
|
1704
|
+
enabled: true,
|
|
1705
|
+
depth: obj.depth ?? 3,
|
|
1706
|
+
skipH1: obj.skipH1 ?? false,
|
|
1707
|
+
prefix: obj.prefix ?? ""
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
return void 0;
|
|
1711
|
+
}
|
|
1712
|
+
function normalizeSecurity(raw) {
|
|
1713
|
+
if (!raw) return void 0;
|
|
1714
|
+
const sec = raw;
|
|
1715
|
+
if (!sec.userPassword && !sec.ownerPassword && !sec.permissions) return void 0;
|
|
1716
|
+
return {
|
|
1717
|
+
userPassword: sec.userPassword,
|
|
1718
|
+
ownerPassword: sec.ownerPassword,
|
|
1719
|
+
permissions: sec.permissions
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1350
1722
|
function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
1351
1723
|
const configMeta = userConfig.metadata || {};
|
|
1352
1724
|
const mergedMeta = { ...configMeta, ...frontmatter };
|
|
@@ -1387,6 +1759,17 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1387
1759
|
const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
|
|
1388
1760
|
const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
|
|
1389
1761
|
const watermark = normalizeWatermark(rawWatermark);
|
|
1762
|
+
const rawSignatures = mergedMeta.signatures || userConfig.signatures;
|
|
1763
|
+
const signatures = normalizeSignatures(rawSignatures, tokenContext);
|
|
1764
|
+
const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
|
|
1765
|
+
const coverPage = normalizeCoverPage(rawCover, tokenContext);
|
|
1766
|
+
const rawBack = mergedMeta.backCover !== void 0 ? mergedMeta.backCover : userConfig.backCover;
|
|
1767
|
+
const backCover = normalizeBackCover(rawBack, tokenContext);
|
|
1768
|
+
const rawNumberHeadings = mergedMeta.numberHeadings !== void 0 ? mergedMeta.numberHeadings : userConfig.numberHeadings;
|
|
1769
|
+
const numberHeadings = normalizeNumberHeadings(rawNumberHeadings);
|
|
1770
|
+
const rawSecurity = mergedMeta.security || userConfig.security;
|
|
1771
|
+
const security = normalizeSecurity(rawSecurity);
|
|
1772
|
+
const math = mergedMeta.math !== false && userConfig.math !== false;
|
|
1390
1773
|
const cssList = [];
|
|
1391
1774
|
const addCss = (item) => {
|
|
1392
1775
|
if (!item) return;
|
|
@@ -1414,7 +1797,13 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1414
1797
|
header,
|
|
1415
1798
|
footer,
|
|
1416
1799
|
toc,
|
|
1800
|
+
signatures,
|
|
1417
1801
|
watermark,
|
|
1802
|
+
coverPage,
|
|
1803
|
+
backCover,
|
|
1804
|
+
numberHeadings,
|
|
1805
|
+
security,
|
|
1806
|
+
math,
|
|
1418
1807
|
css: cssList,
|
|
1419
1808
|
embedImages,
|
|
1420
1809
|
bundleHtml,
|
|
@@ -1422,6 +1811,45 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1422
1811
|
};
|
|
1423
1812
|
}
|
|
1424
1813
|
|
|
1814
|
+
// src/core/math/mathRenderer.ts
|
|
1815
|
+
import katex from "katex";
|
|
1816
|
+
function renderMathToHtml(latex, displayMode = false) {
|
|
1817
|
+
try {
|
|
1818
|
+
return katex.renderToString(latex.trim(), {
|
|
1819
|
+
displayMode,
|
|
1820
|
+
throwOnError: false,
|
|
1821
|
+
output: "htmlAndMathml",
|
|
1822
|
+
strict: false
|
|
1823
|
+
});
|
|
1824
|
+
} catch {
|
|
1825
|
+
return `<span class="katex-fallback">${latex}</span>`;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
var KATEX_INLINE_CSS = `
|
|
1829
|
+
.katex { font: normal 1.21em KaTeX_Main, Times New Roman, serif; line-height: 1.2; text-indent: 0; text-rendering: auto; border-color: currentColor; }
|
|
1830
|
+
.katex * { -ms-high-contrast-adjust: none !important; }
|
|
1831
|
+
.katex .katex-html { display: inline-block; }
|
|
1832
|
+
.katex .katex-mathml { clip: rect(1px, 1px, 1px, 1px); border: 0; height: 1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
|
|
1833
|
+
.katex-display { display: block; margin: 1em 0; text-align: center; }
|
|
1834
|
+
.katex-display > .katex { display: inline-block; text-align: initial; }
|
|
1835
|
+
.katex .base { position: relative; white-space: nowrap; width: min-content; }
|
|
1836
|
+
.katex .strut { display: inline-block; }
|
|
1837
|
+
.katex .mord { display: inline-block; }
|
|
1838
|
+
.katex .mbin { display: inline-block; }
|
|
1839
|
+
.katex .mrel { display: inline-block; }
|
|
1840
|
+
.katex .mopen { display: inline-block; }
|
|
1841
|
+
.katex .mclose { display: inline-block; }
|
|
1842
|
+
.katex .mpunct { display: inline-block; }
|
|
1843
|
+
.katex .minner { display: inline-block; }
|
|
1844
|
+
.katex .mop { display: inline-block; }
|
|
1845
|
+
.katex .frac-line { width: 100%; border-bottom-style: solid; }
|
|
1846
|
+
.katex .vlist-t { display: inline-table; table-layout: fixed; }
|
|
1847
|
+
.katex .vlist-r { display: table-row; }
|
|
1848
|
+
.katex .vlist { display: table-cell; vertical-align: bottom; position: relative; }
|
|
1849
|
+
.katex .msupsub { text-align: left; }
|
|
1850
|
+
.katex .sqrt > .root { margin-left: 0.27777778em; margin-right: -0.55555556em; }
|
|
1851
|
+
`;
|
|
1852
|
+
|
|
1425
1853
|
// src/core/html/htmlBuilder.ts
|
|
1426
1854
|
function escapeHtml(str) {
|
|
1427
1855
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -1464,6 +1892,15 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1464
1892
|
result += `<code>${escapeHtml(span.content)}</code>`;
|
|
1465
1893
|
continue;
|
|
1466
1894
|
}
|
|
1895
|
+
if (span.type === "mathInline") {
|
|
1896
|
+
result += renderMathToHtml(span.content, false);
|
|
1897
|
+
continue;
|
|
1898
|
+
}
|
|
1899
|
+
if (span.type === "footnoteRef") {
|
|
1900
|
+
const id = escapeHtml(span.footnoteId || span.content);
|
|
1901
|
+
result += `<sup><a href="#fn-${id}" id="fnref-${id}" class="markforge-fnref">[${escapeHtml(span.content)}]</a></sup>`;
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1467
1904
|
if (span.type === "htmlInline") {
|
|
1468
1905
|
result += span.content;
|
|
1469
1906
|
continue;
|
|
@@ -1472,66 +1909,9 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1472
1909
|
}
|
|
1473
1910
|
return result;
|
|
1474
1911
|
}
|
|
1475
|
-
async function
|
|
1476
|
-
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
1477
|
-
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
1478
|
-
let customCss = "";
|
|
1479
|
-
for (const cssPath of resolved.css) {
|
|
1480
|
-
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
1481
|
-
if (fs3.existsSync(fullCssPath)) {
|
|
1482
|
-
customCss += `
|
|
1483
|
-
/* Custom CSS: ${cssPath} */
|
|
1484
|
-
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
1912
|
+
async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd()) {
|
|
1488
1913
|
let bodyHtml = "";
|
|
1489
|
-
|
|
1490
|
-
bodyHtml += ` <header class="document-header">
|
|
1491
|
-
`;
|
|
1492
|
-
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
1493
|
-
`;
|
|
1494
|
-
if (resolved.subtitle) {
|
|
1495
|
-
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
1496
|
-
`;
|
|
1497
|
-
}
|
|
1498
|
-
if (resolved.author || resolved.date || resolved.version) {
|
|
1499
|
-
bodyHtml += ` <div class="document-meta">
|
|
1500
|
-
`;
|
|
1501
|
-
if (resolved.author) {
|
|
1502
|
-
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
1503
|
-
`;
|
|
1504
|
-
}
|
|
1505
|
-
if (resolved.version) {
|
|
1506
|
-
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
1507
|
-
`;
|
|
1508
|
-
}
|
|
1509
|
-
if (resolved.date) {
|
|
1510
|
-
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
1511
|
-
`;
|
|
1512
|
-
}
|
|
1513
|
-
bodyHtml += ` </div>
|
|
1514
|
-
`;
|
|
1515
|
-
}
|
|
1516
|
-
bodyHtml += ` </header>
|
|
1517
|
-
`;
|
|
1518
|
-
}
|
|
1519
|
-
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
1520
|
-
bodyHtml += ` <nav class="table-of-contents">
|
|
1521
|
-
`;
|
|
1522
|
-
bodyHtml += ` <h2>Table of Contents</h2>
|
|
1523
|
-
<ul>
|
|
1524
|
-
`;
|
|
1525
|
-
for (const entry of doc.tocEntries) {
|
|
1526
|
-
const indent = " ".repeat(entry.level);
|
|
1527
|
-
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
1528
|
-
`;
|
|
1529
|
-
}
|
|
1530
|
-
bodyHtml += ` </ul>
|
|
1531
|
-
</nav>
|
|
1532
|
-
`;
|
|
1533
|
-
}
|
|
1534
|
-
for (const node of doc.nodes) {
|
|
1914
|
+
for (const node of nodes) {
|
|
1535
1915
|
if (node.type === "heading") {
|
|
1536
1916
|
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
1537
1917
|
bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
|
|
@@ -1541,6 +1921,26 @@ async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1541
1921
|
if (node.type === "paragraph") {
|
|
1542
1922
|
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
1543
1923
|
bodyHtml += ` <p>${inner}</p>
|
|
1924
|
+
`;
|
|
1925
|
+
continue;
|
|
1926
|
+
}
|
|
1927
|
+
if (node.type === "mathBlock") {
|
|
1928
|
+
bodyHtml += ` <div class="math-block">${renderMathToHtml(node.text || "", true)}</div>
|
|
1929
|
+
`;
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
if (node.type === "columns") {
|
|
1933
|
+
const cols = node.columnsCount || 2;
|
|
1934
|
+
const gap = node.columnGap || "1.5rem";
|
|
1935
|
+
let colChildrenHtml = "";
|
|
1936
|
+
for (const col of node.children || []) {
|
|
1937
|
+
const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir);
|
|
1938
|
+
colChildrenHtml += ` <div class="markforge-col">
|
|
1939
|
+
${colInner} </div>
|
|
1940
|
+
`;
|
|
1941
|
+
}
|
|
1942
|
+
bodyHtml += ` <div class="markforge-columns" style="--cols: ${cols}; --col-gap: ${gap};">
|
|
1943
|
+
${colChildrenHtml} </div>
|
|
1544
1944
|
`;
|
|
1545
1945
|
continue;
|
|
1546
1946
|
}
|
|
@@ -1629,87 +2029,734 @@ ${escapeHtml(node.text || "")}
|
|
|
1629
2029
|
continue;
|
|
1630
2030
|
}
|
|
1631
2031
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
transform: translate(-50%, -50%) rotate(${wm.rotate}deg);
|
|
1642
|
-
font-size: ${wm.fontSize}pt;
|
|
1643
|
-
font-weight: 900;
|
|
1644
|
-
color: ${wm.color};
|
|
1645
|
-
opacity: ${wm.opacity};
|
|
1646
|
-
pointer-events: none;
|
|
1647
|
-
z-index: 0;
|
|
1648
|
-
user-select: none;
|
|
1649
|
-
text-transform: uppercase;
|
|
1650
|
-
letter-spacing: 0.15em;
|
|
1651
|
-
white-space: nowrap;
|
|
2032
|
+
return bodyHtml;
|
|
2033
|
+
}
|
|
2034
|
+
async function renderCoverPageHtml(cover, baseDir = process.cwd()) {
|
|
2035
|
+
let logoHtml = "";
|
|
2036
|
+
if (cover.logo) {
|
|
2037
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
2038
|
+
const src = resolvedLogo ? resolvedLogo.dataUri : cover.logo;
|
|
2039
|
+
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;";
|
|
2040
|
+
logoHtml = `<div class="cover-logo"><img src="${src}" alt="Logo" style="${widthStyle} object-fit: contain;" /></div>`;
|
|
1652
2041
|
}
|
|
1653
|
-
.
|
|
2042
|
+
const badgeHtml = cover.badge ? `<div class="cover-badge" style="${cover.badgeColor ? `background-color: ${cover.badgeColor};` : ""}${cover.badgeTextColor ? `color: ${cover.badgeTextColor};` : ""}">${escapeHtml(cover.badge)}</div>` : "";
|
|
2043
|
+
const titleHtml = `<h1 class="cover-title">${escapeHtml(cover.title)}</h1>`;
|
|
2044
|
+
const subtitleHtml = cover.subtitle ? `<div class="cover-subtitle">${escapeHtml(cover.subtitle)}</div>` : "";
|
|
2045
|
+
const metaItems = [];
|
|
2046
|
+
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>`);
|
|
2047
|
+
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>`);
|
|
2048
|
+
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>`);
|
|
2049
|
+
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>`);
|
|
2050
|
+
const metaHtml = metaItems.length > 0 ? `<div class="cover-meta">${metaItems.join("\n")}</div>` : "";
|
|
2051
|
+
const footerHtml = cover.footerText ? `<div class="cover-footer-text">${escapeHtml(cover.footerText)}</div>` : "";
|
|
2052
|
+
const css = `
|
|
2053
|
+
.markforge-cover {
|
|
2054
|
+
min-height: 100vh;
|
|
2055
|
+
box-sizing: border-box;
|
|
2056
|
+
display: flex;
|
|
2057
|
+
flex-direction: column;
|
|
2058
|
+
justify-content: space-between;
|
|
2059
|
+
padding: 4rem 3.5rem;
|
|
2060
|
+
page-break-after: always;
|
|
2061
|
+
break-after: page;
|
|
1654
2062
|
position: relative;
|
|
1655
|
-
z-index:
|
|
2063
|
+
z-index: 2;
|
|
2064
|
+
background: ${cover.bgGradient || "#FFFFFF"};
|
|
2065
|
+
-webkit-print-color-adjust: exact;
|
|
2066
|
+
print-color-adjust: exact;
|
|
2067
|
+
color: ${cover.textColor || "#0F172A"};
|
|
2068
|
+
}
|
|
2069
|
+
.markforge-cover.cover-modern {
|
|
2070
|
+
border-top: 8px solid #0D998D;
|
|
2071
|
+
}
|
|
2072
|
+
.markforge-cover.cover-corporate-split {
|
|
2073
|
+
border-left: 12px solid #0D998D;
|
|
2074
|
+
}
|
|
2075
|
+
.markforge-cover.cover-card {
|
|
2076
|
+
background: #F8FAFC;
|
|
2077
|
+
}
|
|
2078
|
+
.cover-top {
|
|
2079
|
+
display: flex;
|
|
2080
|
+
justify-content: space-between;
|
|
2081
|
+
align-items: flex-start;
|
|
2082
|
+
width: 100%;
|
|
2083
|
+
}
|
|
2084
|
+
.cover-badge {
|
|
2085
|
+
display: inline-block;
|
|
2086
|
+
padding: 0.35rem 0.85rem;
|
|
2087
|
+
font-size: 0.78rem;
|
|
2088
|
+
font-weight: 700;
|
|
2089
|
+
letter-spacing: 0.08em;
|
|
2090
|
+
text-transform: uppercase;
|
|
2091
|
+
background-color: #ECFDFD;
|
|
2092
|
+
color: #0D998D;
|
|
2093
|
+
border-radius: 4px;
|
|
2094
|
+
border: 1px solid #33CDCF;
|
|
2095
|
+
}
|
|
2096
|
+
.cover-body {
|
|
2097
|
+
margin: auto 0;
|
|
2098
|
+
}
|
|
2099
|
+
.cover-title {
|
|
2100
|
+
font-size: 2.8rem;
|
|
2101
|
+
font-weight: 800;
|
|
2102
|
+
line-height: 1.15;
|
|
2103
|
+
margin: 0 0 1rem 0;
|
|
2104
|
+
color: inherit;
|
|
2105
|
+
}
|
|
2106
|
+
.cover-subtitle {
|
|
2107
|
+
font-size: 1.35rem;
|
|
2108
|
+
font-weight: 400;
|
|
2109
|
+
color: #64748B;
|
|
2110
|
+
margin: 0 0 2rem 0;
|
|
2111
|
+
line-height: 1.4;
|
|
2112
|
+
}
|
|
2113
|
+
.cover-meta {
|
|
2114
|
+
display: flex;
|
|
2115
|
+
flex-direction: column;
|
|
2116
|
+
gap: 0.5rem;
|
|
2117
|
+
border-top: 1.5px solid #E2E8F0;
|
|
2118
|
+
padding-top: 1.5rem;
|
|
2119
|
+
max-width: 480px;
|
|
2120
|
+
}
|
|
2121
|
+
.cover-meta-item {
|
|
2122
|
+
font-size: 0.92rem;
|
|
2123
|
+
display: flex;
|
|
2124
|
+
gap: 0.75rem;
|
|
2125
|
+
}
|
|
2126
|
+
.cover-meta-label {
|
|
2127
|
+
font-weight: 600;
|
|
2128
|
+
color: #64748B;
|
|
2129
|
+
min-width: 110px;
|
|
2130
|
+
}
|
|
2131
|
+
.cover-meta-value {
|
|
2132
|
+
font-weight: 500;
|
|
2133
|
+
color: #0F172A;
|
|
2134
|
+
}
|
|
2135
|
+
.cover-bottom {
|
|
2136
|
+
display: flex;
|
|
2137
|
+
justify-content: space-between;
|
|
2138
|
+
align-items: flex-end;
|
|
2139
|
+
font-size: 0.82rem;
|
|
2140
|
+
color: #94A3B8;
|
|
2141
|
+
}
|
|
2142
|
+
@media print {
|
|
2143
|
+
.markforge-cover {
|
|
2144
|
+
page-break-after: always;
|
|
2145
|
+
break-after: page;
|
|
2146
|
+
height: 100vh;
|
|
2147
|
+
min-height: 100vh;
|
|
2148
|
+
max-height: 100vh;
|
|
2149
|
+
box-sizing: border-box;
|
|
2150
|
+
overflow: hidden;
|
|
2151
|
+
margin: 0;
|
|
2152
|
+
-webkit-print-color-adjust: exact;
|
|
2153
|
+
print-color-adjust: exact;
|
|
2154
|
+
}
|
|
1656
2155
|
}
|
|
1657
2156
|
`;
|
|
1658
|
-
|
|
2157
|
+
const html = ` <section class="markforge-cover cover-${cover.preset}">
|
|
2158
|
+
<div class="cover-top">
|
|
2159
|
+
${logoHtml}
|
|
2160
|
+
${badgeHtml}
|
|
2161
|
+
</div>
|
|
2162
|
+
<div class="cover-body">
|
|
2163
|
+
${titleHtml}
|
|
2164
|
+
${subtitleHtml}
|
|
2165
|
+
${metaHtml}
|
|
2166
|
+
</div>
|
|
2167
|
+
<div class="cover-bottom">
|
|
2168
|
+
${footerHtml}
|
|
2169
|
+
</div>
|
|
2170
|
+
</section>
|
|
1659
2171
|
`;
|
|
2172
|
+
return { html, css };
|
|
2173
|
+
}
|
|
2174
|
+
async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
|
|
2175
|
+
let logoHtml = "";
|
|
2176
|
+
if (backCover.logo) {
|
|
2177
|
+
const resolved = await resolveImage(backCover.logo, baseDir);
|
|
2178
|
+
const src = resolved ? resolved.dataUri : backCover.logo;
|
|
2179
|
+
const widthStyle = backCover.logoWidth ? `style="width: ${typeof backCover.logoWidth === "number" ? `${backCover.logoWidth}px` : backCover.logoWidth}; max-width: 100%;"` : `style="max-width: 160px; height: auto;"`;
|
|
2180
|
+
logoHtml = `<div class="back-logo"><img src="${src}" alt="Brand Logo" ${widthStyle} /></div>`;
|
|
1660
2181
|
}
|
|
1661
|
-
const
|
|
1662
|
-
const
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
2182
|
+
const badgeHtml = backCover.badge ? `<div class="back-badge" style="${backCover.badgeColor ? `background-color: ${backCover.badgeColor};` : ""}${backCover.badgeTextColor ? `color: ${backCover.badgeTextColor};` : ""}">${escapeHtml(backCover.badge)}</div>` : "";
|
|
2183
|
+
const titleHtml = `<h1 class="back-title">${escapeHtml(backCover.title)}</h1>`;
|
|
2184
|
+
const subtitleHtml = backCover.subtitle ? `<div class="back-subtitle">${escapeHtml(backCover.subtitle)}</div>` : "";
|
|
2185
|
+
const contactItems = [];
|
|
2186
|
+
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>`);
|
|
2187
|
+
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>`);
|
|
2188
|
+
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>`);
|
|
2189
|
+
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>`);
|
|
2190
|
+
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>`);
|
|
2191
|
+
if (backCover.social) {
|
|
2192
|
+
for (const [network, url] of Object.entries(backCover.social)) {
|
|
2193
|
+
if (url) {
|
|
2194
|
+
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>`);
|
|
1674
2195
|
}
|
|
1675
|
-
}
|
|
1676
|
-
</script>` : "";
|
|
1677
|
-
return `<!DOCTYPE html>
|
|
1678
|
-
<html lang="${resolved.lang}">
|
|
1679
|
-
<head>
|
|
1680
|
-
<meta charset="UTF-8">
|
|
1681
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1682
|
-
<title>${escapeHtml(resolved.title)}</title>
|
|
1683
|
-
<style>
|
|
1684
|
-
${THEME_COMPONENTS}
|
|
1685
|
-
${baseThemeCss}
|
|
1686
|
-
${customCss}
|
|
1687
|
-
${inlinedCss}
|
|
1688
|
-
${watermarkCss}
|
|
1689
|
-
</style>
|
|
1690
|
-
</head>
|
|
1691
|
-
<body>
|
|
1692
|
-
${watermarkHtml} <div class="document-container">
|
|
1693
|
-
${bodyHtml} </div>
|
|
1694
|
-
${mermaidScript}
|
|
1695
|
-
</body>
|
|
1696
|
-
</html>`;
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
// src/core/pdf/pdfBuilder.ts
|
|
1700
|
-
function findChromeExecutable() {
|
|
1701
|
-
if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
|
|
1702
|
-
return process.env.CHROME_PATH;
|
|
2196
|
+
}
|
|
1703
2197
|
}
|
|
1704
|
-
|
|
1705
|
-
|
|
2198
|
+
const contactHtml = contactItems.length > 0 ? `<div class="back-contact-grid">${contactItems.join("\n")}</div>` : "";
|
|
2199
|
+
const copyrightHtml = backCover.copyright ? `<div class="back-copyright">${escapeHtml(backCover.copyright)}</div>` : "";
|
|
2200
|
+
const isDark = backCover.preset === "corporate";
|
|
2201
|
+
const css = `
|
|
2202
|
+
.markforge-back-cover {
|
|
2203
|
+
min-height: 100vh;
|
|
2204
|
+
box-sizing: border-box;
|
|
2205
|
+
display: flex;
|
|
2206
|
+
flex-direction: column;
|
|
2207
|
+
justify-content: space-between;
|
|
2208
|
+
padding: 4rem 3.5rem;
|
|
2209
|
+
page-break-before: always;
|
|
2210
|
+
break-before: page;
|
|
2211
|
+
position: relative;
|
|
2212
|
+
z-index: 2;
|
|
2213
|
+
background: ${backCover.bgGradient || (isDark ? "#0F172A" : "#FFFFFF")};
|
|
2214
|
+
color: ${backCover.textColor || (isDark ? "#F8FAFC" : "#0F172A")};
|
|
1706
2215
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
2216
|
+
.markforge-back-cover.back-modern {
|
|
2217
|
+
border-bottom: 8px solid #0D998D;
|
|
2218
|
+
}
|
|
2219
|
+
.markforge-back-cover.back-corporate {
|
|
2220
|
+
border-left: 12px solid #33CDCF;
|
|
2221
|
+
}
|
|
2222
|
+
.markforge-back-cover.back-card {
|
|
2223
|
+
background: #F8FAFC;
|
|
2224
|
+
}
|
|
2225
|
+
.back-top {
|
|
2226
|
+
display: flex;
|
|
2227
|
+
justify-content: space-between;
|
|
2228
|
+
align-items: flex-start;
|
|
2229
|
+
width: 100%;
|
|
2230
|
+
}
|
|
2231
|
+
.back-badge {
|
|
2232
|
+
display: inline-block;
|
|
2233
|
+
padding: 0.35rem 0.85rem;
|
|
2234
|
+
font-size: 0.78rem;
|
|
2235
|
+
font-weight: 700;
|
|
2236
|
+
letter-spacing: 0.08em;
|
|
2237
|
+
text-transform: uppercase;
|
|
2238
|
+
background-color: #ECFDFD;
|
|
2239
|
+
color: #0D998D;
|
|
2240
|
+
border-radius: 4px;
|
|
2241
|
+
border: 1px solid #33CDCF;
|
|
2242
|
+
}
|
|
2243
|
+
.back-body {
|
|
2244
|
+
margin: auto 0;
|
|
2245
|
+
}
|
|
2246
|
+
.back-title {
|
|
2247
|
+
font-size: 2.6rem;
|
|
2248
|
+
font-weight: 800;
|
|
2249
|
+
line-height: 1.15;
|
|
2250
|
+
margin: 0 0 0.75rem 0;
|
|
2251
|
+
color: inherit;
|
|
2252
|
+
}
|
|
2253
|
+
.back-subtitle {
|
|
2254
|
+
font-size: 1.25rem;
|
|
2255
|
+
font-weight: 400;
|
|
2256
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2257
|
+
margin: 0 0 2rem 0;
|
|
2258
|
+
line-height: 1.4;
|
|
2259
|
+
}
|
|
2260
|
+
.back-contact-grid {
|
|
2261
|
+
display: flex;
|
|
2262
|
+
flex-direction: column;
|
|
2263
|
+
gap: 0.6rem;
|
|
2264
|
+
border-top: 1.5px solid ${isDark ? "#334155" : "#E2E8F0"};
|
|
2265
|
+
padding-top: 1.5rem;
|
|
2266
|
+
max-width: 540px;
|
|
2267
|
+
}
|
|
2268
|
+
.back-contact-item {
|
|
2269
|
+
font-size: 0.92rem;
|
|
2270
|
+
display: flex;
|
|
2271
|
+
gap: 0.75rem;
|
|
2272
|
+
}
|
|
2273
|
+
.back-contact-label {
|
|
2274
|
+
font-weight: 600;
|
|
2275
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2276
|
+
min-width: 110px;
|
|
2277
|
+
}
|
|
2278
|
+
.back-contact-link {
|
|
2279
|
+
color: #0D998D;
|
|
2280
|
+
text-decoration: none;
|
|
2281
|
+
font-weight: 600;
|
|
2282
|
+
}
|
|
2283
|
+
.back-contact-link:hover {
|
|
2284
|
+
text-decoration: underline;
|
|
2285
|
+
}
|
|
2286
|
+
.back-copyright {
|
|
2287
|
+
font-size: 0.82rem;
|
|
2288
|
+
color: ${isDark ? "#64748B" : "#94A3B8"};
|
|
2289
|
+
border-top: 1px solid ${isDark ? "#1E293B" : "#F1F5F9"};
|
|
2290
|
+
padding-top: 1rem;
|
|
2291
|
+
margin-top: 2rem;
|
|
2292
|
+
}
|
|
2293
|
+
@media print {
|
|
2294
|
+
.markforge-back-cover {
|
|
2295
|
+
page: back-cover-page;
|
|
2296
|
+
page-break-before: always;
|
|
2297
|
+
break-before: page;
|
|
2298
|
+
page-break-after: avoid;
|
|
2299
|
+
break-after: avoid;
|
|
2300
|
+
min-height: 100vh;
|
|
2301
|
+
height: 100vh;
|
|
2302
|
+
max-height: 100vh;
|
|
2303
|
+
margin: 0;
|
|
2304
|
+
box-sizing: border-box;
|
|
2305
|
+
overflow: hidden;
|
|
2306
|
+
-webkit-print-color-adjust: exact;
|
|
2307
|
+
print-color-adjust: exact;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
`;
|
|
2311
|
+
const html = ` <section class="markforge-back-cover back-${backCover.preset}">
|
|
2312
|
+
<div class="back-top">
|
|
2313
|
+
${logoHtml}
|
|
2314
|
+
${badgeHtml}
|
|
2315
|
+
</div>
|
|
2316
|
+
<div class="back-body">
|
|
2317
|
+
${titleHtml}
|
|
2318
|
+
${subtitleHtml}
|
|
2319
|
+
${contactHtml}
|
|
2320
|
+
</div>
|
|
2321
|
+
${copyrightHtml}
|
|
2322
|
+
</section>
|
|
2323
|
+
`;
|
|
2324
|
+
return { html, css };
|
|
2325
|
+
}
|
|
2326
|
+
async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
|
|
2327
|
+
var _a;
|
|
2328
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2329
|
+
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
2330
|
+
let customCss = "";
|
|
2331
|
+
for (const cssPath of resolved.css) {
|
|
2332
|
+
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
2333
|
+
if (fs3.existsSync(fullCssPath)) {
|
|
2334
|
+
customCss += `
|
|
2335
|
+
/* Custom CSS: ${cssPath} */
|
|
2336
|
+
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
2340
|
+
const extraCss = `
|
|
2341
|
+
.markforge-columns {
|
|
2342
|
+
display: grid;
|
|
2343
|
+
grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
|
|
2344
|
+
gap: var(--col-gap, 1.5rem);
|
|
2345
|
+
margin: 1.5rem 0;
|
|
2346
|
+
}
|
|
2347
|
+
.markforge-col {
|
|
2348
|
+
min-width: 0;
|
|
2349
|
+
}
|
|
2350
|
+
.markforge-fnref {
|
|
2351
|
+
text-decoration: none;
|
|
2352
|
+
font-size: 0.8em;
|
|
2353
|
+
vertical-align: super;
|
|
2354
|
+
color: #0D998D;
|
|
2355
|
+
font-weight: 700;
|
|
2356
|
+
}
|
|
2357
|
+
.markforge-footnotes {
|
|
2358
|
+
margin-top: 3rem;
|
|
2359
|
+
padding-top: 1rem;
|
|
2360
|
+
font-size: 0.88rem;
|
|
2361
|
+
color: #64748B;
|
|
2362
|
+
}
|
|
2363
|
+
.markforge-footnotes hr {
|
|
2364
|
+
border: 0;
|
|
2365
|
+
border-top: 1px solid #E2E8F0;
|
|
2366
|
+
margin-bottom: 1rem;
|
|
2367
|
+
}
|
|
2368
|
+
.markforge-fn-return {
|
|
2369
|
+
text-decoration: none;
|
|
2370
|
+
color: #0D998D;
|
|
2371
|
+
}
|
|
2372
|
+
.math-block {
|
|
2373
|
+
margin: 1.5rem 0;
|
|
2374
|
+
text-align: center;
|
|
2375
|
+
overflow-x: auto;
|
|
2376
|
+
}
|
|
2377
|
+
`;
|
|
2378
|
+
let coverHtml = "";
|
|
2379
|
+
let coverCss = "";
|
|
2380
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
2381
|
+
const coverRes = await renderCoverPageHtml(resolved.coverPage, baseDir);
|
|
2382
|
+
coverHtml = coverRes.html;
|
|
2383
|
+
coverCss = coverRes.css;
|
|
2384
|
+
}
|
|
2385
|
+
let backHtml = "";
|
|
2386
|
+
let backCss = "";
|
|
2387
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
2388
|
+
const backRes = await renderBackCoverHtml(resolved.backCover, baseDir);
|
|
2389
|
+
backHtml = backRes.html;
|
|
2390
|
+
backCss = backRes.css;
|
|
2391
|
+
}
|
|
2392
|
+
let bodyHtml = "";
|
|
2393
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2394
|
+
bodyHtml += ` <header class="document-header">
|
|
2395
|
+
`;
|
|
2396
|
+
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
2397
|
+
`;
|
|
2398
|
+
if (resolved.subtitle) {
|
|
2399
|
+
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
2400
|
+
`;
|
|
2401
|
+
}
|
|
2402
|
+
if (resolved.author || resolved.date || resolved.version) {
|
|
2403
|
+
bodyHtml += ` <div class="document-meta">
|
|
2404
|
+
`;
|
|
2405
|
+
if (resolved.author) {
|
|
2406
|
+
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
2407
|
+
`;
|
|
2408
|
+
}
|
|
2409
|
+
if (resolved.version) {
|
|
2410
|
+
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
2411
|
+
`;
|
|
2412
|
+
}
|
|
2413
|
+
if (resolved.date) {
|
|
2414
|
+
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
2415
|
+
`;
|
|
2416
|
+
}
|
|
2417
|
+
bodyHtml += ` </div>
|
|
2418
|
+
`;
|
|
2419
|
+
}
|
|
2420
|
+
bodyHtml += ` </header>
|
|
2421
|
+
`;
|
|
2422
|
+
}
|
|
2423
|
+
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
2424
|
+
bodyHtml += ` <nav class="table-of-contents">
|
|
2425
|
+
`;
|
|
2426
|
+
bodyHtml += ` <h2>Table of Contents</h2>
|
|
2427
|
+
<ul>
|
|
2428
|
+
`;
|
|
2429
|
+
for (const entry of doc.tocEntries) {
|
|
2430
|
+
const indent = " ".repeat(entry.level);
|
|
2431
|
+
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
2432
|
+
`;
|
|
2433
|
+
}
|
|
2434
|
+
bodyHtml += ` </ul>
|
|
2435
|
+
</nav>
|
|
2436
|
+
`;
|
|
2437
|
+
}
|
|
2438
|
+
bodyHtml += await renderNodesToHtml(doc.nodes, resolved, baseDir);
|
|
2439
|
+
let footnotesHtml = "";
|
|
2440
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
2441
|
+
let fnListHtml = "";
|
|
2442
|
+
for (const def of doc.footnoteDefs) {
|
|
2443
|
+
const defInner = await renderInlinesToHtml(def.inlines, baseDir);
|
|
2444
|
+
fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">↩</a></li>
|
|
2445
|
+
`;
|
|
2446
|
+
}
|
|
2447
|
+
footnotesHtml = `
|
|
2448
|
+
<footer class="markforge-footnotes">
|
|
2449
|
+
<hr />
|
|
2450
|
+
<ol>
|
|
2451
|
+
${fnListHtml} </ol>
|
|
2452
|
+
</footer>
|
|
2453
|
+
`;
|
|
2454
|
+
}
|
|
2455
|
+
let watermarkCss = "";
|
|
2456
|
+
let watermarkHtml = "";
|
|
2457
|
+
if (resolved.watermark) {
|
|
2458
|
+
const wm = resolved.watermark;
|
|
2459
|
+
watermarkCss = `
|
|
2460
|
+
.document-watermark {
|
|
2461
|
+
position: fixed;
|
|
2462
|
+
top: 0;
|
|
2463
|
+
left: 0;
|
|
2464
|
+
right: 0;
|
|
2465
|
+
bottom: 0;
|
|
2466
|
+
width: 100%;
|
|
2467
|
+
height: 100%;
|
|
2468
|
+
pointer-events: none;
|
|
2469
|
+
z-index: 0;
|
|
2470
|
+
user-select: none;
|
|
2471
|
+
-webkit-user-select: none;
|
|
2472
|
+
-webkit-print-color-adjust: exact;
|
|
2473
|
+
print-color-adjust: exact;
|
|
2474
|
+
}
|
|
2475
|
+
.document-container {
|
|
2476
|
+
position: relative;
|
|
2477
|
+
z-index: 1;
|
|
2478
|
+
}
|
|
2479
|
+
@media print {
|
|
2480
|
+
.document-watermark {
|
|
2481
|
+
position: fixed;
|
|
2482
|
+
top: 0;
|
|
2483
|
+
left: 0;
|
|
2484
|
+
right: 0;
|
|
2485
|
+
bottom: 0;
|
|
2486
|
+
width: 100vw;
|
|
2487
|
+
height: 100vh;
|
|
2488
|
+
pointer-events: none;
|
|
2489
|
+
z-index: 0;
|
|
2490
|
+
-webkit-print-color-adjust: exact;
|
|
2491
|
+
print-color-adjust: exact;
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
`;
|
|
2495
|
+
watermarkHtml = ` <div id="markforge-watermark" class="document-watermark" aria-hidden="true"></div>
|
|
2496
|
+
<script>
|
|
2497
|
+
(function() {
|
|
2498
|
+
try {
|
|
2499
|
+
var canvas = document.createElement('canvas');
|
|
2500
|
+
var dpr = 2;
|
|
2501
|
+
var width = 1200;
|
|
2502
|
+
var height = 1600;
|
|
2503
|
+
canvas.width = width * dpr;
|
|
2504
|
+
canvas.height = height * dpr;
|
|
2505
|
+
var ctx = canvas.getContext('2d');
|
|
2506
|
+
if (ctx) {
|
|
2507
|
+
ctx.scale(dpr, dpr);
|
|
2508
|
+
ctx.translate(width / 2, height / 2);
|
|
2509
|
+
ctx.rotate((-Math.abs(${wm.rotate || 45}) * Math.PI) / 180);
|
|
2510
|
+
ctx.textAlign = 'center';
|
|
2511
|
+
ctx.textBaseline = 'middle';
|
|
2512
|
+
ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
|
|
2513
|
+
ctx.fillStyle = '${wm.color}';
|
|
2514
|
+
ctx.globalAlpha = ${wm.opacity};
|
|
2515
|
+
try { ctx.letterSpacing = '0.15em'; } catch(e) {}
|
|
2516
|
+
ctx.fillText(${JSON.stringify(wm.text.toUpperCase())}, 0, 0);
|
|
2517
|
+
var dataUrl = canvas.toDataURL('image/png');
|
|
2518
|
+
var wmEl = document.getElementById('markforge-watermark');
|
|
2519
|
+
if (wmEl) {
|
|
2520
|
+
wmEl.style.backgroundImage = 'url("' + dataUrl + '")';
|
|
2521
|
+
wmEl.style.backgroundRepeat = 'no-repeat';
|
|
2522
|
+
wmEl.style.backgroundPosition = 'center center';
|
|
2523
|
+
wmEl.style.backgroundSize = 'contain';
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
} catch(err) {}
|
|
2527
|
+
})();
|
|
2528
|
+
</script>
|
|
2529
|
+
`;
|
|
2530
|
+
}
|
|
2531
|
+
let signaturesHtml = "";
|
|
2532
|
+
let signaturesCss = "";
|
|
2533
|
+
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
2534
|
+
const sig = resolved.signatures;
|
|
2535
|
+
const numItems = sig.items.length;
|
|
2536
|
+
signaturesCss = `
|
|
2537
|
+
.markforge-signatures {
|
|
2538
|
+
margin-top: ${sig.spacingBefore};
|
|
2539
|
+
display: grid;
|
|
2540
|
+
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))`};
|
|
2541
|
+
gap: 2rem;
|
|
2542
|
+
page-break-inside: avoid;
|
|
2543
|
+
break-inside: avoid;
|
|
2544
|
+
}
|
|
2545
|
+
.markforge-signature-card {
|
|
2546
|
+
${numItems === 1 && sig.align === "center" ? "grid-column: 2;" : ""}
|
|
2547
|
+
${numItems === 1 && sig.align === "right" ? "grid-column: 2;" : ""}
|
|
2548
|
+
display: flex;
|
|
2549
|
+
flex-direction: column;
|
|
2550
|
+
${sig.style === "box" ? `border: 1px solid ${sig.borderColor}; border-radius: 6px; padding: 14px 18px; background-color: var(--mf-card-bg, #F8FAFC);` : ""}
|
|
2551
|
+
}
|
|
2552
|
+
.markforge-sig-title {
|
|
2553
|
+
font-size: 0.85rem;
|
|
2554
|
+
color: ${sig.titleColor};
|
|
2555
|
+
font-weight: 600;
|
|
2556
|
+
margin-bottom: 6px;
|
|
2557
|
+
}
|
|
2558
|
+
.markforge-sig-space {
|
|
2559
|
+
height: var(--sig-height, 60px);
|
|
2560
|
+
display: flex;
|
|
2561
|
+
align-items: center;
|
|
2562
|
+
justify-content: center;
|
|
2563
|
+
margin-bottom: 6px;
|
|
2564
|
+
}
|
|
2565
|
+
.markforge-sig-space img {
|
|
2566
|
+
max-height: 100%;
|
|
2567
|
+
max-width: 100%;
|
|
2568
|
+
object-fit: contain;
|
|
2569
|
+
}
|
|
2570
|
+
.markforge-sig-line {
|
|
2571
|
+
${sig.style === "line" ? `border-bottom: 1.5px solid ${sig.borderColor}; margin-bottom: 8px;` : ""}
|
|
2572
|
+
}
|
|
2573
|
+
.markforge-sig-name {
|
|
2574
|
+
font-size: 0.95rem;
|
|
2575
|
+
font-weight: 700;
|
|
2576
|
+
color: ${sig.nameColor};
|
|
2577
|
+
}
|
|
2578
|
+
.markforge-sig-role {
|
|
2579
|
+
font-size: 0.82rem;
|
|
2580
|
+
color: ${sig.roleColor};
|
|
2581
|
+
margin-top: 2px;
|
|
2582
|
+
}
|
|
2583
|
+
.markforge-sig-date {
|
|
2584
|
+
font-size: 0.78rem;
|
|
2585
|
+
color: ${sig.roleColor};
|
|
2586
|
+
margin-top: 2px;
|
|
2587
|
+
}
|
|
2588
|
+
@media print {
|
|
2589
|
+
.markforge-signatures {
|
|
2590
|
+
page-break-inside: avoid;
|
|
2591
|
+
break-inside: avoid;
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
`;
|
|
2595
|
+
const itemCards = sig.items.map((item) => {
|
|
2596
|
+
const titleHtml = item.title ? `<div class="markforge-sig-title">${escapeHtml(item.title)}</div>` : "";
|
|
2597
|
+
let signSpaceHtml = "";
|
|
2598
|
+
if (item.image) {
|
|
2599
|
+
signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"><img src="${escapeHtml(item.image)}" alt="Signature" /></div>`;
|
|
2600
|
+
} else {
|
|
2601
|
+
signSpaceHtml = `<div class="markforge-sig-space" style="--sig-height: ${item.signatureHeight}px;"></div>`;
|
|
2602
|
+
}
|
|
2603
|
+
const lineHtml = sig.style === "line" ? `<div class="markforge-sig-line"></div>` : "";
|
|
2604
|
+
const nameHtml = `<div class="markforge-sig-name">${escapeHtml(item.name)}</div>`;
|
|
2605
|
+
const roleHtml = item.role ? `<div class="markforge-sig-role">${escapeHtml(item.role)}</div>` : "";
|
|
2606
|
+
const dateHtml = item.date ? `<div class="markforge-sig-date">Date: ${escapeHtml(item.date)}</div>` : "";
|
|
2607
|
+
return ` <div class="markforge-signature-card">
|
|
2608
|
+
${titleHtml}
|
|
2609
|
+
${signSpaceHtml}
|
|
2610
|
+
${lineHtml}
|
|
2611
|
+
${nameHtml}
|
|
2612
|
+
${roleHtml}
|
|
2613
|
+
${dateHtml}
|
|
2614
|
+
</div>`;
|
|
2615
|
+
}).join("\n");
|
|
2616
|
+
signaturesHtml = `
|
|
2617
|
+
<div class="markforge-signatures">
|
|
2618
|
+
${itemCards}
|
|
2619
|
+
</div>
|
|
2620
|
+
`;
|
|
2621
|
+
}
|
|
2622
|
+
const hasMermaid = doc.nodes.some((n) => n.type === "mermaid");
|
|
2623
|
+
const mermaidScript = hasMermaid ? `<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
2624
|
+
<script>
|
|
2625
|
+
mermaid.initialize({
|
|
2626
|
+
startOnLoad: true,
|
|
2627
|
+
theme: 'neutral',
|
|
2628
|
+
themeVariables: {
|
|
2629
|
+
primaryColor: '#33CDCF',
|
|
2630
|
+
primaryTextColor: '#0F172A',
|
|
2631
|
+
primaryBorderColor: '#009DA0',
|
|
2632
|
+
lineColor: '#009DA0',
|
|
2633
|
+
secondaryColor: '#ECFDFD',
|
|
2634
|
+
tertiaryColor: '#F8FAFC'
|
|
2635
|
+
}
|
|
2636
|
+
});
|
|
2637
|
+
</script>` : "";
|
|
2638
|
+
return `<!DOCTYPE html>
|
|
2639
|
+
<html lang="${resolved.lang}">
|
|
2640
|
+
<head>
|
|
2641
|
+
<meta charset="UTF-8">
|
|
2642
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2643
|
+
<title>${escapeHtml(resolved.title)}</title>
|
|
2644
|
+
<style>
|
|
2645
|
+
${THEME_COMPONENTS}
|
|
2646
|
+
${baseThemeCss}
|
|
2647
|
+
${KATEX_INLINE_CSS}
|
|
2648
|
+
${extraCss}
|
|
2649
|
+
${coverCss}
|
|
2650
|
+
${backCss}
|
|
2651
|
+
${customCss}
|
|
2652
|
+
${inlinedCss}
|
|
2653
|
+
${watermarkCss}
|
|
2654
|
+
${signaturesCss}
|
|
2655
|
+
</style>
|
|
2656
|
+
</head>
|
|
2657
|
+
<body>
|
|
2658
|
+
${watermarkHtml}${coverHtml} <div class="document-container">
|
|
2659
|
+
${bodyHtml}${footnotesHtml}${signaturesHtml} </div>
|
|
2660
|
+
${mermaidScript}
|
|
2661
|
+
${backHtml}</body>
|
|
2662
|
+
</html>`;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2665
|
+
// src/core/pdf/pdfBuilder.ts
|
|
2666
|
+
function escapeXml(str) {
|
|
2667
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2668
|
+
}
|
|
2669
|
+
function generateWatermarkPngBuffer(chromePath, wm) {
|
|
2670
|
+
const tmpHtml = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.html`);
|
|
2671
|
+
const tmpPng = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
|
|
2672
|
+
try {
|
|
2673
|
+
const text = escapeXml(wm.text.toUpperCase());
|
|
2674
|
+
const fontSize = (wm.fontSize || 52) * 1.5;
|
|
2675
|
+
const color = wm.color || "#E11D48";
|
|
2676
|
+
const opacity = wm.opacity !== void 0 ? wm.opacity : 0.12;
|
|
2677
|
+
const rotate = wm.rotate !== void 0 ? wm.rotate : -45;
|
|
2678
|
+
const html = `<!DOCTYPE html>
|
|
2679
|
+
<html>
|
|
2680
|
+
<head>
|
|
2681
|
+
<meta charset="utf-8">
|
|
2682
|
+
<style>
|
|
2683
|
+
html, body {
|
|
2684
|
+
margin: 0;
|
|
2685
|
+
padding: 0;
|
|
2686
|
+
width: 1200px;
|
|
2687
|
+
height: 1600px;
|
|
2688
|
+
background: transparent;
|
|
2689
|
+
overflow: hidden;
|
|
2690
|
+
}
|
|
2691
|
+
.wm-box {
|
|
2692
|
+
width: 1200px;
|
|
2693
|
+
height: 1600px;
|
|
2694
|
+
display: flex;
|
|
2695
|
+
align-items: center;
|
|
2696
|
+
justify-content: center;
|
|
2697
|
+
transform: rotate(${rotate}deg);
|
|
2698
|
+
}
|
|
2699
|
+
.wm-text {
|
|
2700
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
2701
|
+
font-weight: 900;
|
|
2702
|
+
font-size: ${fontSize}px;
|
|
2703
|
+
color: ${color};
|
|
2704
|
+
opacity: ${opacity};
|
|
2705
|
+
letter-spacing: 0.15em;
|
|
2706
|
+
text-transform: uppercase;
|
|
2707
|
+
white-space: nowrap;
|
|
2708
|
+
}
|
|
2709
|
+
</style>
|
|
2710
|
+
</head>
|
|
2711
|
+
<body>
|
|
2712
|
+
<div class="wm-box"><span class="wm-text">${text}</span></div>
|
|
2713
|
+
</body>
|
|
2714
|
+
</html>`;
|
|
2715
|
+
fs4.writeFileSync(tmpHtml, html, "utf8");
|
|
2716
|
+
const fileUrl = pathToFileURL2(tmpHtml).href;
|
|
2717
|
+
const isWin = process.platform === "win32";
|
|
2718
|
+
spawnSync(
|
|
2719
|
+
chromePath,
|
|
2720
|
+
[
|
|
2721
|
+
"--headless=new",
|
|
2722
|
+
"--disable-gpu",
|
|
2723
|
+
"--disable-sync",
|
|
2724
|
+
"--disable-extensions",
|
|
2725
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
2726
|
+
`--screenshot=${tmpPng}`,
|
|
2727
|
+
"--window-size=1200,1600",
|
|
2728
|
+
"--default-background-color=00000000",
|
|
2729
|
+
fileUrl
|
|
2730
|
+
],
|
|
2731
|
+
{ timeout: 15e3, windowsHide: true }
|
|
2732
|
+
);
|
|
2733
|
+
if (fs4.existsSync(tmpPng) && fs4.statSync(tmpPng).size > 0) {
|
|
2734
|
+
return fs4.readFileSync(tmpPng);
|
|
2735
|
+
}
|
|
2736
|
+
return null;
|
|
2737
|
+
} catch {
|
|
2738
|
+
return null;
|
|
2739
|
+
} finally {
|
|
2740
|
+
try {
|
|
2741
|
+
if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
|
|
2742
|
+
if (fs4.existsSync(tmpPng)) fs4.unlinkSync(tmpPng);
|
|
2743
|
+
} catch {
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
function findChromeExecutable() {
|
|
2748
|
+
if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
|
|
2749
|
+
return process.env.CHROME_PATH;
|
|
2750
|
+
}
|
|
2751
|
+
if (process.env.PUPPETEER_EXECUTABLE_PATH && fs4.existsSync(process.env.PUPPETEER_EXECUTABLE_PATH)) {
|
|
2752
|
+
return process.env.PUPPETEER_EXECUTABLE_PATH;
|
|
2753
|
+
}
|
|
2754
|
+
const isWin = process.platform === "win32";
|
|
2755
|
+
const winLocalAppData = process.env.LOCALAPPDATA ?? "";
|
|
2756
|
+
const winProgramFiles = process.env.PROGRAMFILES ?? "C:\\Program Files";
|
|
2757
|
+
const winProgramFilesX86 = process.env["PROGRAMFILES(X86)"] ?? "C:\\Program Files (x86)";
|
|
2758
|
+
const candidates = [
|
|
2759
|
+
// Linux
|
|
1713
2760
|
"/usr/bin/google-chrome",
|
|
1714
2761
|
"/usr/bin/google-chrome-stable",
|
|
1715
2762
|
"/usr/bin/chromium",
|
|
@@ -1761,7 +2808,7 @@ function findChromeExecutable() {
|
|
|
1761
2808
|
return null;
|
|
1762
2809
|
}
|
|
1763
2810
|
function injectPagedMediaStyles(html, config, metadata) {
|
|
1764
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2811
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1765
2812
|
const resolved = resolveDocumentConfig(metadata || {}, config);
|
|
1766
2813
|
const size = resolved.paperSize;
|
|
1767
2814
|
const orientation = resolved.orientation;
|
|
@@ -1805,6 +2852,38 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
1805
2852
|
${fontStyle}
|
|
1806
2853
|
}`;
|
|
1807
2854
|
};
|
|
2855
|
+
const coverPageCss = ((_a = resolved.coverPage) == null ? void 0 : _a.enabled) ? `
|
|
2856
|
+
@page :first {
|
|
2857
|
+
margin-top: 0;
|
|
2858
|
+
margin-bottom: 0;
|
|
2859
|
+
margin-left: 0;
|
|
2860
|
+
margin-right: 0;
|
|
2861
|
+
background-image: none !important;
|
|
2862
|
+
@top-left { content: none; }
|
|
2863
|
+
@top-center { content: none; }
|
|
2864
|
+
@top-right { content: none; }
|
|
2865
|
+
@bottom-left { content: none; }
|
|
2866
|
+
@bottom-center { content: none; }
|
|
2867
|
+
@bottom-right { content: none; }
|
|
2868
|
+
}` : "";
|
|
2869
|
+
const backCoverCss = ((_b = resolved.backCover) == null ? void 0 : _b.enabled) ? `
|
|
2870
|
+
@page back-cover-page {
|
|
2871
|
+
size: ${size} ${orientation};
|
|
2872
|
+
margin: 0;
|
|
2873
|
+
background-image: none !important;
|
|
2874
|
+
@top-left { content: none; }
|
|
2875
|
+
@top-center { content: none; }
|
|
2876
|
+
@top-right { content: none; }
|
|
2877
|
+
@bottom-left { content: none; }
|
|
2878
|
+
@bottom-center { content: none; }
|
|
2879
|
+
@bottom-right { content: none; }
|
|
2880
|
+
}
|
|
2881
|
+
.markforge-back-cover {
|
|
2882
|
+
page: back-cover-page;
|
|
2883
|
+
min-height: 100vh;
|
|
2884
|
+
height: 100vh;
|
|
2885
|
+
box-sizing: border-box;
|
|
2886
|
+
}` : "";
|
|
1808
2887
|
const pagedCss = `
|
|
1809
2888
|
@page {
|
|
1810
2889
|
size: ${size} ${orientation};
|
|
@@ -1812,15 +2891,18 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
1812
2891
|
margin-bottom: ${bottom};
|
|
1813
2892
|
margin-left: ${left};
|
|
1814
2893
|
margin-right: ${right};
|
|
1815
|
-
${buildZoneCss("top-left", (
|
|
1816
|
-
${buildZoneCss("top-center", (
|
|
1817
|
-
${buildZoneCss("top-right", (
|
|
1818
|
-
${buildZoneCss("bottom-left", (
|
|
1819
|
-
${buildZoneCss("bottom-center", (
|
|
1820
|
-
${buildZoneCss("bottom-right", (
|
|
2894
|
+
${buildZoneCss("top-left", (_c = resolved.header) == null ? void 0 : _c.left)}
|
|
2895
|
+
${buildZoneCss("top-center", (_d = resolved.header) == null ? void 0 : _d.center)}
|
|
2896
|
+
${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
|
|
2897
|
+
${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
|
|
2898
|
+
${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
|
|
2899
|
+
${buildZoneCss("bottom-right", (_h = resolved.footer) == null ? void 0 : _h.right, true)}
|
|
1821
2900
|
}
|
|
2901
|
+
${coverPageCss}
|
|
2902
|
+
${backCoverCss}
|
|
1822
2903
|
@media print {
|
|
1823
2904
|
body { padding: 0; }
|
|
2905
|
+
.document-watermark { display: none !important; }
|
|
1824
2906
|
h1, h2, h3, pre, table, blockquote, .callout {
|
|
1825
2907
|
break-inside: avoid;
|
|
1826
2908
|
}
|
|
@@ -1868,6 +2950,7 @@ startxref
|
|
|
1868
2950
|
return Buffer.from(pdfBody, "utf-8");
|
|
1869
2951
|
}
|
|
1870
2952
|
async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
2953
|
+
var _a, _b, _c;
|
|
1871
2954
|
const baseHtml = await buildHtmlDocument(doc, config, baseDir);
|
|
1872
2955
|
const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
|
|
1873
2956
|
const chromePath = findChromeExecutable();
|
|
@@ -1877,6 +2960,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1877
2960
|
const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
|
|
1878
2961
|
const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
|
|
1879
2962
|
const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
|
|
2963
|
+
const isWin = process.platform === "win32";
|
|
1880
2964
|
const isolatedFlags = [
|
|
1881
2965
|
`--user-data-dir=${tmpProfile}`,
|
|
1882
2966
|
"--no-first-run",
|
|
@@ -1887,7 +2971,6 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1887
2971
|
"--disable-default-apps",
|
|
1888
2972
|
"--disable-extensions",
|
|
1889
2973
|
"--disable-domain-reliability",
|
|
1890
|
-
"--disable-client-side-phishing-detection",
|
|
1891
2974
|
"--disable-breakpad",
|
|
1892
2975
|
"--disable-component-extensions-with-background-pages",
|
|
1893
2976
|
"--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
|
|
@@ -1896,12 +2979,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1896
2979
|
"--mute-audio",
|
|
1897
2980
|
"--no-service-autorun",
|
|
1898
2981
|
"--disable-gpu",
|
|
1899
|
-
"--no-sandbox",
|
|
1900
|
-
"--disable-setuid-sandbox",
|
|
1901
|
-
"--allow-file-access-from-files",
|
|
1902
|
-
"--disable-web-security",
|
|
2982
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
1903
2983
|
"--force-color-profile=srgb",
|
|
1904
|
-
"--no-pdf-header-footer"
|
|
2984
|
+
"--no-pdf-header-footer",
|
|
2985
|
+
"--window-size=1200,1600"
|
|
1905
2986
|
];
|
|
1906
2987
|
try {
|
|
1907
2988
|
fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
|
|
@@ -1916,7 +2997,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1916
2997
|
`--print-to-pdf=${tmpPdf}`,
|
|
1917
2998
|
fileUrl
|
|
1918
2999
|
],
|
|
1919
|
-
{ timeout: 3e4 }
|
|
3000
|
+
{ timeout: 3e4, windowsHide: true }
|
|
1920
3001
|
);
|
|
1921
3002
|
if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
|
|
1922
3003
|
res = spawnSync(
|
|
@@ -1927,12 +3008,78 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
1927
3008
|
`--print-to-pdf=${tmpPdf}`,
|
|
1928
3009
|
fileUrl
|
|
1929
3010
|
],
|
|
1930
|
-
{ timeout: 3e4 }
|
|
3011
|
+
{ timeout: 3e4, windowsHide: true }
|
|
1931
3012
|
);
|
|
1932
3013
|
}
|
|
1933
3014
|
if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
|
|
1934
3015
|
const pdfBuffer = fs4.readFileSync(tmpPdf);
|
|
1935
|
-
|
|
3016
|
+
try {
|
|
3017
|
+
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
|
3018
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
3019
|
+
if (resolved.title) pdfDoc.setTitle(resolved.title);
|
|
3020
|
+
if (resolved.author) pdfDoc.setAuthor(resolved.author);
|
|
3021
|
+
if (resolved.subtitle) pdfDoc.setSubject(resolved.subtitle);
|
|
3022
|
+
pdfDoc.setCreator("MarkForge Enterprise Document Generator");
|
|
3023
|
+
pdfDoc.setProducer("MarkForge (by Ma'sum)");
|
|
3024
|
+
pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
|
|
3025
|
+
if (((_a = resolved.backCover) == null ? void 0 : _a.enabled) && pdfDoc.getPageCount() > 2) {
|
|
3026
|
+
pdfDoc.removePage(pdfDoc.getPageCount() - 1);
|
|
3027
|
+
}
|
|
3028
|
+
if (resolved.watermark) {
|
|
3029
|
+
const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
|
|
3030
|
+
if (wmPng) {
|
|
3031
|
+
const embeddedPng = await pdfDoc.embedPng(wmPng);
|
|
3032
|
+
const pages = pdfDoc.getPages();
|
|
3033
|
+
const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
|
|
3034
|
+
const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? pages.length - 1 : pages.length;
|
|
3035
|
+
for (let i = startPageIndex; i < endPageIndex; i++) {
|
|
3036
|
+
const page = pages[i];
|
|
3037
|
+
const { width, height } = page.getSize();
|
|
3038
|
+
page.drawImage(embeddedPng, {
|
|
3039
|
+
x: 0,
|
|
3040
|
+
y: 0,
|
|
3041
|
+
width,
|
|
3042
|
+
height
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
const savedBytes = await pdfDoc.save();
|
|
3048
|
+
let finalBuffer = Buffer.from(savedBytes);
|
|
3049
|
+
if (resolved.security) {
|
|
3050
|
+
const sec = resolved.security;
|
|
3051
|
+
const hasUserPassword = typeof sec.userPassword === "string" && sec.userPassword.length > 0;
|
|
3052
|
+
const hasOwnerPassword = typeof sec.ownerPassword === "string" && sec.ownerPassword.length > 0;
|
|
3053
|
+
if (hasUserPassword || hasOwnerPassword) {
|
|
3054
|
+
try {
|
|
3055
|
+
const userPass = sec.userPassword ?? "";
|
|
3056
|
+
const ownerPass = sec.ownerPassword ?? userPass;
|
|
3057
|
+
const perms = sec.permissions;
|
|
3058
|
+
const encryptedBytes = await encryptPDF(
|
|
3059
|
+
new Uint8Array(finalBuffer),
|
|
3060
|
+
userPass,
|
|
3061
|
+
{
|
|
3062
|
+
ownerPassword: ownerPass,
|
|
3063
|
+
algorithm: "AES-256",
|
|
3064
|
+
allowPrinting: (perms == null ? void 0 : perms.printing) !== "none",
|
|
3065
|
+
allowHighQualityPrint: (perms == null ? void 0 : perms.printing) === "highResolution",
|
|
3066
|
+
allowModifying: (perms == null ? void 0 : perms.modifying) ?? true,
|
|
3067
|
+
allowCopying: (perms == null ? void 0 : perms.copying) ?? true,
|
|
3068
|
+
allowAnnotating: (perms == null ? void 0 : perms.annotating) ?? true,
|
|
3069
|
+
allowFillingForms: (perms == null ? void 0 : perms.fillingForms) ?? true,
|
|
3070
|
+
allowExtraction: (perms == null ? void 0 : perms.contentAccessibility) ?? true,
|
|
3071
|
+
allowAssembly: (perms == null ? void 0 : perms.documentAssembly) ?? true
|
|
3072
|
+
}
|
|
3073
|
+
);
|
|
3074
|
+
finalBuffer = Buffer.from(encryptedBytes);
|
|
3075
|
+
} catch {
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
return finalBuffer;
|
|
3080
|
+
} catch {
|
|
3081
|
+
return pdfBuffer;
|
|
3082
|
+
}
|
|
1936
3083
|
}
|
|
1937
3084
|
} catch {
|
|
1938
3085
|
} finally {
|
|
@@ -2187,6 +3334,29 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2187
3334
|
);
|
|
2188
3335
|
continue;
|
|
2189
3336
|
}
|
|
3337
|
+
if (span.type === "mathInline") {
|
|
3338
|
+
runs.push(
|
|
3339
|
+
new TextRun({
|
|
3340
|
+
text: span.content,
|
|
3341
|
+
font: "Cambria Math",
|
|
3342
|
+
italics: true,
|
|
3343
|
+
size: options.size,
|
|
3344
|
+
color: options.color || "0F172A"
|
|
3345
|
+
})
|
|
3346
|
+
);
|
|
3347
|
+
continue;
|
|
3348
|
+
}
|
|
3349
|
+
if (span.type === "footnoteRef") {
|
|
3350
|
+
runs.push(
|
|
3351
|
+
new TextRun({
|
|
3352
|
+
text: `[${span.content}]`,
|
|
3353
|
+
superScript: true,
|
|
3354
|
+
color: "009DA0",
|
|
3355
|
+
bold: true
|
|
3356
|
+
})
|
|
3357
|
+
);
|
|
3358
|
+
continue;
|
|
3359
|
+
}
|
|
2190
3360
|
runs.push(
|
|
2191
3361
|
new TextRun({
|
|
2192
3362
|
text: span.content,
|
|
@@ -2201,7 +3371,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2201
3371
|
return runs;
|
|
2202
3372
|
}
|
|
2203
3373
|
async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
2204
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
3374
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
2205
3375
|
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2206
3376
|
const docElements = [];
|
|
2207
3377
|
const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
|
|
@@ -2212,7 +3382,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2212
3382
|
const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
|
|
2213
3383
|
const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
|
|
2214
3384
|
const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
|
|
2215
|
-
if (resolved.title) {
|
|
3385
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2216
3386
|
docElements.push(
|
|
2217
3387
|
new Paragraph({
|
|
2218
3388
|
children: [
|
|
@@ -2628,7 +3798,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2628
3798
|
}
|
|
2629
3799
|
if (node.type === "table" && node.children) {
|
|
2630
3800
|
const tableRows = [];
|
|
2631
|
-
const numCols = ((
|
|
3801
|
+
const numCols = ((_c = (_b = node.children[0]) == null ? void 0 : _b.children) == null ? void 0 : _c.length) || 1;
|
|
2632
3802
|
const colWidth = Math.floor(9e3 / numCols);
|
|
2633
3803
|
for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
|
|
2634
3804
|
const rowNode = node.children[rowIdx];
|
|
@@ -2638,7 +3808,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2638
3808
|
if (rowNode.children) {
|
|
2639
3809
|
for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
|
|
2640
3810
|
const cellNode = rowNode.children[colIdx];
|
|
2641
|
-
const align = (
|
|
3811
|
+
const align = (_d = node.align) == null ? void 0 : _d[colIdx];
|
|
2642
3812
|
let alignment = AlignmentType.LEFT;
|
|
2643
3813
|
if (align === "center") alignment = AlignmentType.CENTER;
|
|
2644
3814
|
if (align === "right") alignment = AlignmentType.RIGHT;
|
|
@@ -2777,15 +3947,175 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2777
3947
|
}
|
|
2778
3948
|
continue;
|
|
2779
3949
|
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
3950
|
+
if (node.type === "mathBlock") {
|
|
3951
|
+
docElements.push(
|
|
3952
|
+
new Paragraph({
|
|
3953
|
+
alignment: AlignmentType.CENTER,
|
|
3954
|
+
children: [
|
|
3955
|
+
new TextRun({
|
|
3956
|
+
text: node.text || "",
|
|
3957
|
+
font: "Cambria Math",
|
|
3958
|
+
italics: true,
|
|
3959
|
+
size: 24,
|
|
3960
|
+
// 12pt
|
|
3961
|
+
color: textHex
|
|
3962
|
+
})
|
|
3963
|
+
],
|
|
3964
|
+
spacing: { before: 180, after: 180 },
|
|
3965
|
+
shading: { fill: cardBgHex, type: ShadingType.CLEAR },
|
|
3966
|
+
border: {
|
|
3967
|
+
top: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
3968
|
+
bottom: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
3969
|
+
left: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
3970
|
+
right: { style: BorderStyle.SINGLE, size: 4, color: borderHex }
|
|
3971
|
+
}
|
|
3972
|
+
})
|
|
3973
|
+
);
|
|
3974
|
+
continue;
|
|
3975
|
+
}
|
|
3976
|
+
if (node.type === "columns") {
|
|
3977
|
+
const cols = node.columnsCount || 2;
|
|
3978
|
+
const contentWidth = Math.max(
|
|
3979
|
+
1e3,
|
|
3980
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
3981
|
+
);
|
|
3982
|
+
const cellWidthDxa = Math.floor(contentWidth / cols);
|
|
3983
|
+
const cells = [];
|
|
3984
|
+
for (const col of node.children || []) {
|
|
3985
|
+
const colParagraphs = [];
|
|
3986
|
+
for (const childNode of col.children || []) {
|
|
3987
|
+
if (childNode.type === "heading") {
|
|
3988
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, bold: true, size: 24, color: primaryDarkHex });
|
|
3989
|
+
colParagraphs.push(new Paragraph({ children: runs, spacing: { before: 120, after: 60 } }));
|
|
3990
|
+
} else if (childNode.type === "paragraph") {
|
|
3991
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
3992
|
+
colParagraphs.push(new Paragraph({ children: runs, spacing: { after: 100 } }));
|
|
3993
|
+
} else if (childNode.type === "list" && childNode.children) {
|
|
3994
|
+
for (const item of childNode.children) {
|
|
3995
|
+
const runs = await convertInlinesToTextRuns(item.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
3996
|
+
colParagraphs.push(new Paragraph({ children: [new TextRun({ text: "\u2022 ", font: defaultFont, color: primaryHex }), ...runs], spacing: { after: 40 } }));
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
}
|
|
4000
|
+
if (colParagraphs.length === 0) colParagraphs.push(new Paragraph({}));
|
|
4001
|
+
cells.push(
|
|
4002
|
+
new TableCell({
|
|
4003
|
+
width: { size: cellWidthDxa, type: WidthType.DXA },
|
|
4004
|
+
borders: {
|
|
4005
|
+
top: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4006
|
+
bottom: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4007
|
+
left: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4008
|
+
right: { style: BorderStyle.NONE, size: 0, color: "auto" }
|
|
4009
|
+
},
|
|
4010
|
+
margins: { top: 60, bottom: 60, left: 100, right: 100 },
|
|
4011
|
+
children: colParagraphs
|
|
4012
|
+
})
|
|
4013
|
+
);
|
|
4014
|
+
}
|
|
4015
|
+
docElements.push(
|
|
4016
|
+
new Table({
|
|
4017
|
+
width: { size: 100, type: WidthType.PERCENTAGE },
|
|
4018
|
+
rows: [new TableRow({ children: cells })]
|
|
4019
|
+
})
|
|
4020
|
+
);
|
|
4021
|
+
docElements.push(new Paragraph({ spacing: { after: 120 } }));
|
|
4022
|
+
continue;
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
4025
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
4026
|
+
docElements.push(
|
|
4027
|
+
new Paragraph({
|
|
4028
|
+
border: {
|
|
4029
|
+
top: { style: BorderStyle.SINGLE, size: 4, color: borderHex, space: 8 }
|
|
4030
|
+
},
|
|
4031
|
+
spacing: { before: 360, after: 120 }
|
|
4032
|
+
})
|
|
4033
|
+
);
|
|
4034
|
+
for (const def of doc.footnoteDefs) {
|
|
4035
|
+
const defRuns = await convertInlinesToTextRuns(def.inlines, baseDir, {
|
|
4036
|
+
font: defaultFont,
|
|
4037
|
+
size: 18,
|
|
4038
|
+
// 9pt
|
|
4039
|
+
color: textMutedHex
|
|
4040
|
+
});
|
|
4041
|
+
docElements.push(
|
|
4042
|
+
new Paragraph({
|
|
4043
|
+
children: [
|
|
4044
|
+
new TextRun({
|
|
4045
|
+
text: `[${def.id}] `,
|
|
4046
|
+
bold: true,
|
|
4047
|
+
color: primaryDarkHex,
|
|
4048
|
+
font: defaultFont,
|
|
4049
|
+
size: 18
|
|
4050
|
+
}),
|
|
4051
|
+
...defRuns
|
|
4052
|
+
],
|
|
4053
|
+
spacing: { after: 60 }
|
|
4054
|
+
})
|
|
4055
|
+
);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
4059
|
+
const sig = resolved.signatures;
|
|
4060
|
+
const numItems = sig.items.length;
|
|
4061
|
+
const contentWidth = Math.max(
|
|
4062
|
+
1e3,
|
|
4063
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4064
|
+
);
|
|
4065
|
+
docElements.push(new Paragraph({ spacing: { before: sig.spacingBeforeTwip } }));
|
|
4066
|
+
const sigCells = [];
|
|
4067
|
+
const colWidths = [];
|
|
4068
|
+
if (numItems === 1) {
|
|
4069
|
+
const cardWidth = Math.min(3400, Math.floor(contentWidth * 0.42));
|
|
4070
|
+
const spacerWidth = contentWidth - cardWidth;
|
|
4071
|
+
const cardCell = await buildDocxSignatureCell(sig.items[0], sig, cardWidth, defaultFont, baseDir);
|
|
4072
|
+
if (sig.align === "left") {
|
|
4073
|
+
colWidths.push(cardWidth, spacerWidth);
|
|
4074
|
+
sigCells.push(cardCell, createEmptyDocxCell(spacerWidth));
|
|
4075
|
+
} else if (sig.align === "center") {
|
|
4076
|
+
const sideWidth = Math.floor(spacerWidth / 2);
|
|
4077
|
+
colWidths.push(sideWidth, cardWidth, sideWidth);
|
|
4078
|
+
sigCells.push(createEmptyDocxCell(sideWidth), cardCell, createEmptyDocxCell(sideWidth));
|
|
4079
|
+
} else {
|
|
4080
|
+
colWidths.push(spacerWidth, cardWidth);
|
|
4081
|
+
sigCells.push(createEmptyDocxCell(spacerWidth), cardCell);
|
|
4082
|
+
}
|
|
4083
|
+
} else {
|
|
4084
|
+
const colWidth = Math.floor(contentWidth / numItems);
|
|
4085
|
+
for (let i = 0; i < numItems; i++) {
|
|
4086
|
+
colWidths.push(colWidth);
|
|
4087
|
+
const cell = await buildDocxSignatureCell(sig.items[i], sig, colWidth, defaultFont, baseDir);
|
|
4088
|
+
sigCells.push(cell);
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
const sigTable = new Table({
|
|
4092
|
+
width: { size: 100, type: WidthType.PERCENTAGE },
|
|
4093
|
+
columnWidths: colWidths,
|
|
4094
|
+
borders: {
|
|
4095
|
+
top: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4096
|
+
bottom: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4097
|
+
left: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4098
|
+
right: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4099
|
+
insideHorizontal: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4100
|
+
insideVertical: { style: BorderStyle.NONE, size: 0, color: "auto" }
|
|
4101
|
+
},
|
|
4102
|
+
rows: [
|
|
4103
|
+
new TableRow({
|
|
4104
|
+
cantSplit: true,
|
|
4105
|
+
children: sigCells
|
|
4106
|
+
})
|
|
4107
|
+
]
|
|
4108
|
+
});
|
|
4109
|
+
docElements.push(sigTable);
|
|
4110
|
+
}
|
|
4111
|
+
const contentWidthTwip = Math.max(
|
|
4112
|
+
1e3,
|
|
4113
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4114
|
+
);
|
|
4115
|
+
const centerPos = Math.round(contentWidthTwip / 2);
|
|
4116
|
+
const rightPos = contentWidthTwip;
|
|
4117
|
+
const headerRuns = [];
|
|
4118
|
+
if ((_e = resolved.header) == null ? void 0 : _e.left) {
|
|
2789
4119
|
headerRuns.push(
|
|
2790
4120
|
new TextRun({
|
|
2791
4121
|
text: resolved.header.left.text,
|
|
@@ -2798,7 +4128,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2798
4128
|
);
|
|
2799
4129
|
}
|
|
2800
4130
|
headerRuns.push(new TextRun({ text: " " }));
|
|
2801
|
-
if ((
|
|
4131
|
+
if ((_f = resolved.header) == null ? void 0 : _f.center) {
|
|
2802
4132
|
headerRuns.push(
|
|
2803
4133
|
new TextRun({
|
|
2804
4134
|
text: resolved.header.center.text,
|
|
@@ -2811,7 +4141,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2811
4141
|
);
|
|
2812
4142
|
}
|
|
2813
4143
|
headerRuns.push(new TextRun({ text: " " }));
|
|
2814
|
-
if ((
|
|
4144
|
+
if ((_g = resolved.header) == null ? void 0 : _g.right) {
|
|
2815
4145
|
headerRuns.push(
|
|
2816
4146
|
new TextRun({
|
|
2817
4147
|
text: resolved.header.right.text,
|
|
@@ -2850,7 +4180,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2850
4180
|
]
|
|
2851
4181
|
}) : void 0;
|
|
2852
4182
|
const footerRuns = [];
|
|
2853
|
-
if ((
|
|
4183
|
+
if ((_h = resolved.footer) == null ? void 0 : _h.left) {
|
|
2854
4184
|
footerRuns.push(
|
|
2855
4185
|
new TextRun({
|
|
2856
4186
|
text: resolved.footer.left.text,
|
|
@@ -2863,7 +4193,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2863
4193
|
);
|
|
2864
4194
|
}
|
|
2865
4195
|
footerRuns.push(new TextRun({ text: " " }));
|
|
2866
|
-
if ((
|
|
4196
|
+
if ((_i = resolved.footer) == null ? void 0 : _i.center) {
|
|
2867
4197
|
footerRuns.push(
|
|
2868
4198
|
new TextRun({
|
|
2869
4199
|
text: resolved.footer.center.text,
|
|
@@ -2876,7 +4206,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2876
4206
|
);
|
|
2877
4207
|
}
|
|
2878
4208
|
footerRuns.push(new TextRun({ text: " " }));
|
|
2879
|
-
if ((
|
|
4209
|
+
if ((_j = resolved.footer) == null ? void 0 : _j.right) {
|
|
2880
4210
|
const rZone = resolved.footer.right;
|
|
2881
4211
|
const rColor = rZone.color.replace("#", "");
|
|
2882
4212
|
const rSize = (rZone.fontSize || 9) * 2;
|
|
@@ -2961,6 +4291,91 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2961
4291
|
]
|
|
2962
4292
|
}) : void 0;
|
|
2963
4293
|
const isLandscape = resolved.orientation === "landscape";
|
|
4294
|
+
const docSections = [];
|
|
4295
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
4296
|
+
const coverElements = await buildDocxCoverPageElements(
|
|
4297
|
+
resolved.coverPage,
|
|
4298
|
+
defaultFont,
|
|
4299
|
+
textHex,
|
|
4300
|
+
primaryHex,
|
|
4301
|
+
primaryDarkHex,
|
|
4302
|
+
textMutedHex,
|
|
4303
|
+
baseDir
|
|
4304
|
+
);
|
|
4305
|
+
docSections.push({
|
|
4306
|
+
properties: {
|
|
4307
|
+
page: {
|
|
4308
|
+
size: {
|
|
4309
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4310
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4311
|
+
orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
4312
|
+
},
|
|
4313
|
+
margin: {
|
|
4314
|
+
top: resolved.margins.topTwip,
|
|
4315
|
+
bottom: resolved.margins.bottomTwip,
|
|
4316
|
+
left: resolved.margins.leftTwip,
|
|
4317
|
+
right: resolved.margins.rightTwip
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
},
|
|
4321
|
+
headers: void 0,
|
|
4322
|
+
footers: void 0,
|
|
4323
|
+
children: coverElements
|
|
4324
|
+
});
|
|
4325
|
+
}
|
|
4326
|
+
docSections.push({
|
|
4327
|
+
properties: {
|
|
4328
|
+
page: {
|
|
4329
|
+
size: {
|
|
4330
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4331
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4332
|
+
orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
4333
|
+
},
|
|
4334
|
+
margin: {
|
|
4335
|
+
top: resolved.margins.topTwip,
|
|
4336
|
+
bottom: resolved.margins.bottomTwip,
|
|
4337
|
+
left: resolved.margins.leftTwip,
|
|
4338
|
+
right: resolved.margins.rightTwip,
|
|
4339
|
+
header: 720,
|
|
4340
|
+
footer: 720
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
},
|
|
4344
|
+
headers: docHeader ? { default: docHeader } : void 0,
|
|
4345
|
+
footers: docFooter ? { default: docFooter } : void 0,
|
|
4346
|
+
children: docElements
|
|
4347
|
+
});
|
|
4348
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
4349
|
+
const backElements = await buildDocxBackCoverElements(
|
|
4350
|
+
resolved.backCover,
|
|
4351
|
+
defaultFont,
|
|
4352
|
+
textHex,
|
|
4353
|
+
primaryHex,
|
|
4354
|
+
primaryDarkHex,
|
|
4355
|
+
textMutedHex,
|
|
4356
|
+
baseDir
|
|
4357
|
+
);
|
|
4358
|
+
docSections.push({
|
|
4359
|
+
properties: {
|
|
4360
|
+
page: {
|
|
4361
|
+
size: {
|
|
4362
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4363
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4364
|
+
orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
4365
|
+
},
|
|
4366
|
+
margin: {
|
|
4367
|
+
top: resolved.margins.topTwip,
|
|
4368
|
+
bottom: resolved.margins.bottomTwip,
|
|
4369
|
+
left: resolved.margins.leftTwip,
|
|
4370
|
+
right: resolved.margins.rightTwip
|
|
4371
|
+
}
|
|
4372
|
+
}
|
|
4373
|
+
},
|
|
4374
|
+
headers: void 0,
|
|
4375
|
+
footers: void 0,
|
|
4376
|
+
children: backElements
|
|
4377
|
+
});
|
|
4378
|
+
}
|
|
2964
4379
|
const document = new Document({
|
|
2965
4380
|
styles: {
|
|
2966
4381
|
default: {
|
|
@@ -2981,32 +4396,383 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2981
4396
|
}
|
|
2982
4397
|
}
|
|
2983
4398
|
},
|
|
2984
|
-
sections:
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
4399
|
+
sections: docSections
|
|
4400
|
+
});
|
|
4401
|
+
return await Packer.toBuffer(document);
|
|
4402
|
+
}
|
|
4403
|
+
async function buildDocxBackCoverElements(backCover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4404
|
+
var _a;
|
|
4405
|
+
const elements = [];
|
|
4406
|
+
elements.push(new Paragraph({ spacing: { before: 1800 } }));
|
|
4407
|
+
if (backCover.logo) {
|
|
4408
|
+
const resolvedLogo = await resolveImage(backCover.logo, baseDir);
|
|
4409
|
+
if (resolvedLogo) {
|
|
4410
|
+
const logoW = typeof backCover.logoWidth === "number" ? backCover.logoWidth : 140;
|
|
4411
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4412
|
+
elements.push(
|
|
4413
|
+
new Paragraph({
|
|
4414
|
+
children: [
|
|
4415
|
+
new ImageRun({
|
|
4416
|
+
data: resolvedLogo.buffer,
|
|
4417
|
+
transformation: {
|
|
4418
|
+
width: logoW,
|
|
4419
|
+
height: Math.round(logoW * 0.75)
|
|
4420
|
+
},
|
|
4421
|
+
type: logoType
|
|
4422
|
+
})
|
|
4423
|
+
],
|
|
4424
|
+
spacing: { after: 240 }
|
|
4425
|
+
})
|
|
4426
|
+
);
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
if (backCover.badge) {
|
|
4430
|
+
elements.push(
|
|
4431
|
+
new Paragraph({
|
|
4432
|
+
children: [
|
|
4433
|
+
new TextRun({
|
|
4434
|
+
text: `[ ${backCover.badge.toUpperCase()} ]`,
|
|
4435
|
+
font: defaultFont,
|
|
4436
|
+
size: 20,
|
|
4437
|
+
bold: true,
|
|
4438
|
+
color: primaryDarkHex
|
|
4439
|
+
})
|
|
4440
|
+
],
|
|
4441
|
+
spacing: { after: 240 }
|
|
4442
|
+
})
|
|
4443
|
+
);
|
|
4444
|
+
}
|
|
4445
|
+
elements.push(
|
|
4446
|
+
new Paragraph({
|
|
4447
|
+
children: [
|
|
4448
|
+
new TextRun({
|
|
4449
|
+
text: backCover.title,
|
|
4450
|
+
font: defaultFont,
|
|
4451
|
+
size: 52,
|
|
4452
|
+
// 26pt
|
|
4453
|
+
bold: true,
|
|
4454
|
+
color: textHex
|
|
4455
|
+
})
|
|
4456
|
+
],
|
|
4457
|
+
spacing: { after: 140 }
|
|
4458
|
+
})
|
|
4459
|
+
);
|
|
4460
|
+
if (backCover.subtitle) {
|
|
4461
|
+
elements.push(
|
|
4462
|
+
new Paragraph({
|
|
4463
|
+
children: [
|
|
4464
|
+
new TextRun({
|
|
4465
|
+
text: backCover.subtitle,
|
|
4466
|
+
font: defaultFont,
|
|
4467
|
+
size: 24,
|
|
4468
|
+
// 12pt
|
|
4469
|
+
color: textMutedHex
|
|
4470
|
+
})
|
|
4471
|
+
],
|
|
4472
|
+
spacing: { after: 480 }
|
|
4473
|
+
})
|
|
4474
|
+
);
|
|
4475
|
+
}
|
|
4476
|
+
elements.push(
|
|
4477
|
+
new Paragraph({
|
|
4478
|
+
border: {
|
|
4479
|
+
bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4480
|
+
},
|
|
4481
|
+
spacing: { after: 480 }
|
|
4482
|
+
})
|
|
4483
|
+
);
|
|
4484
|
+
const contactRuns = [];
|
|
4485
|
+
if (backCover.company) contactRuns.push(new TextRun({ text: `Organization: ${backCover.company}
|
|
4486
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4487
|
+
if (backCover.address) contactRuns.push(new TextRun({ text: `Address: ${backCover.address}
|
|
4488
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4489
|
+
if (backCover.email) contactRuns.push(new TextRun({ text: `Email: ${backCover.email}
|
|
4490
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4491
|
+
if (backCover.phone) contactRuns.push(new TextRun({ text: `Phone: ${backCover.phone}
|
|
4492
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4493
|
+
if (backCover.website) contactRuns.push(new TextRun({ text: `Website: ${backCover.website}
|
|
4494
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4495
|
+
if (backCover.social) {
|
|
4496
|
+
for (const [net, url] of Object.entries(backCover.social)) {
|
|
4497
|
+
if (url) {
|
|
4498
|
+
contactRuns.push(new TextRun({ text: `${net.toUpperCase()}: ${url}
|
|
4499
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
if (contactRuns.length > 0) {
|
|
4504
|
+
elements.push(
|
|
4505
|
+
new Paragraph({
|
|
4506
|
+
children: contactRuns,
|
|
4507
|
+
spacing: { before: 360, after: 360 }
|
|
4508
|
+
})
|
|
4509
|
+
);
|
|
4510
|
+
}
|
|
4511
|
+
if (backCover.copyright) {
|
|
4512
|
+
elements.push(
|
|
4513
|
+
new Paragraph({
|
|
4514
|
+
children: [
|
|
4515
|
+
new TextRun({
|
|
4516
|
+
text: backCover.copyright,
|
|
4517
|
+
font: defaultFont,
|
|
4518
|
+
size: 18,
|
|
4519
|
+
color: "94A3B8"
|
|
4520
|
+
})
|
|
4521
|
+
],
|
|
4522
|
+
spacing: { before: 720 }
|
|
4523
|
+
})
|
|
4524
|
+
);
|
|
4525
|
+
}
|
|
4526
|
+
return elements;
|
|
4527
|
+
}
|
|
4528
|
+
async function buildDocxCoverPageElements(cover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4529
|
+
var _a;
|
|
4530
|
+
const elements = [];
|
|
4531
|
+
elements.push(new Paragraph({ spacing: { before: 1800 } }));
|
|
4532
|
+
if (cover.logo) {
|
|
4533
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
4534
|
+
if (resolvedLogo) {
|
|
4535
|
+
const logoW = typeof cover.logoWidth === "number" ? cover.logoWidth : 140;
|
|
4536
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4537
|
+
elements.push(
|
|
4538
|
+
new Paragraph({
|
|
4539
|
+
children: [
|
|
4540
|
+
new ImageRun({
|
|
4541
|
+
data: resolvedLogo.buffer,
|
|
4542
|
+
transformation: {
|
|
4543
|
+
width: logoW,
|
|
4544
|
+
height: Math.round(logoW * 0.75)
|
|
4545
|
+
},
|
|
4546
|
+
type: logoType
|
|
4547
|
+
})
|
|
4548
|
+
],
|
|
4549
|
+
spacing: { after: 240 }
|
|
4550
|
+
})
|
|
4551
|
+
);
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
if (cover.badge) {
|
|
4555
|
+
elements.push(
|
|
4556
|
+
new Paragraph({
|
|
4557
|
+
children: [
|
|
4558
|
+
new TextRun({
|
|
4559
|
+
text: `[ ${cover.badge.toUpperCase()} ]`,
|
|
4560
|
+
font: defaultFont,
|
|
4561
|
+
size: 20,
|
|
4562
|
+
bold: true,
|
|
4563
|
+
color: primaryDarkHex
|
|
4564
|
+
})
|
|
4565
|
+
],
|
|
4566
|
+
spacing: { after: 240 }
|
|
4567
|
+
})
|
|
4568
|
+
);
|
|
4569
|
+
}
|
|
4570
|
+
elements.push(
|
|
4571
|
+
new Paragraph({
|
|
4572
|
+
children: [
|
|
4573
|
+
new TextRun({
|
|
4574
|
+
text: cover.title,
|
|
4575
|
+
font: defaultFont,
|
|
4576
|
+
size: 56,
|
|
4577
|
+
// 28pt
|
|
4578
|
+
bold: true,
|
|
4579
|
+
color: textHex
|
|
4580
|
+
})
|
|
4581
|
+
],
|
|
4582
|
+
spacing: { after: 140 }
|
|
4583
|
+
})
|
|
4584
|
+
);
|
|
4585
|
+
if (cover.subtitle) {
|
|
4586
|
+
elements.push(
|
|
4587
|
+
new Paragraph({
|
|
4588
|
+
children: [
|
|
4589
|
+
new TextRun({
|
|
4590
|
+
text: cover.subtitle,
|
|
4591
|
+
font: defaultFont,
|
|
4592
|
+
size: 26,
|
|
4593
|
+
// 13pt
|
|
4594
|
+
color: textMutedHex
|
|
4595
|
+
})
|
|
4596
|
+
],
|
|
4597
|
+
spacing: { after: 480 }
|
|
4598
|
+
})
|
|
4599
|
+
);
|
|
4600
|
+
}
|
|
4601
|
+
elements.push(
|
|
4602
|
+
new Paragraph({
|
|
4603
|
+
border: {
|
|
4604
|
+
bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4605
|
+
},
|
|
4606
|
+
spacing: { after: 480 }
|
|
4607
|
+
})
|
|
4608
|
+
);
|
|
4609
|
+
if (cover.company || cover.author || cover.version || cover.date) {
|
|
4610
|
+
const metaRuns = [];
|
|
4611
|
+
if (cover.company) metaRuns.push(new TextRun({ text: `Organization: ${cover.company}
|
|
4612
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4613
|
+
if (cover.author) metaRuns.push(new TextRun({ text: `Author: ${cover.author}
|
|
4614
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4615
|
+
if (cover.version) metaRuns.push(new TextRun({ text: `Version: ${cover.version}
|
|
4616
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4617
|
+
if (cover.date) metaRuns.push(new TextRun({ text: `Date: ${cover.date}
|
|
4618
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4619
|
+
elements.push(
|
|
4620
|
+
new Paragraph({
|
|
4621
|
+
children: metaRuns,
|
|
4622
|
+
spacing: { before: 360, after: 360 }
|
|
4623
|
+
})
|
|
4624
|
+
);
|
|
4625
|
+
}
|
|
4626
|
+
if (cover.footerText) {
|
|
4627
|
+
elements.push(
|
|
4628
|
+
new Paragraph({
|
|
4629
|
+
children: [
|
|
4630
|
+
new TextRun({
|
|
4631
|
+
text: cover.footerText,
|
|
4632
|
+
font: defaultFont,
|
|
4633
|
+
size: 18,
|
|
4634
|
+
color: "94A3B8"
|
|
4635
|
+
})
|
|
4636
|
+
],
|
|
4637
|
+
spacing: { before: 720 }
|
|
4638
|
+
})
|
|
4639
|
+
);
|
|
4640
|
+
}
|
|
4641
|
+
return elements;
|
|
4642
|
+
}
|
|
4643
|
+
async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
|
|
4644
|
+
const cellParagraphs = [];
|
|
4645
|
+
if (item.title) {
|
|
4646
|
+
cellParagraphs.push(
|
|
4647
|
+
new Paragraph({
|
|
4648
|
+
children: [
|
|
4649
|
+
new TextRun({
|
|
4650
|
+
text: item.title,
|
|
4651
|
+
font: defaultFont,
|
|
4652
|
+
size: 18,
|
|
4653
|
+
// 9pt
|
|
4654
|
+
color: sig.titleColor.replace("#", ""),
|
|
4655
|
+
bold: true
|
|
4656
|
+
})
|
|
4657
|
+
],
|
|
4658
|
+
spacing: { after: 60 }
|
|
4659
|
+
})
|
|
4660
|
+
);
|
|
4661
|
+
}
|
|
4662
|
+
if (item.image) {
|
|
4663
|
+
const resolvedImg = await resolveImage(item.image, baseDir);
|
|
4664
|
+
if (resolvedImg) {
|
|
4665
|
+
cellParagraphs.push(
|
|
4666
|
+
new Paragraph({
|
|
4667
|
+
children: [
|
|
4668
|
+
new ImageRun({
|
|
4669
|
+
data: resolvedImg.buffer,
|
|
4670
|
+
transformation: {
|
|
4671
|
+
width: 140,
|
|
4672
|
+
height: 60
|
|
4673
|
+
},
|
|
4674
|
+
type: "png"
|
|
4675
|
+
})
|
|
4676
|
+
],
|
|
4677
|
+
spacing: { before: 40, after: 40 }
|
|
4678
|
+
})
|
|
4679
|
+
);
|
|
4680
|
+
} else {
|
|
4681
|
+
cellParagraphs.push(new Paragraph({ spacing: { before: 240, after: 240 } }));
|
|
4682
|
+
}
|
|
4683
|
+
} else {
|
|
4684
|
+
cellParagraphs.push(new Paragraph({ spacing: { before: 240, after: 240 } }));
|
|
4685
|
+
}
|
|
4686
|
+
if (sig.style === "line") {
|
|
4687
|
+
cellParagraphs.push(
|
|
4688
|
+
new Paragraph({
|
|
4689
|
+
border: {
|
|
4690
|
+
bottom: {
|
|
4691
|
+
style: BorderStyle.SINGLE,
|
|
4692
|
+
size: 6,
|
|
4693
|
+
space: 2,
|
|
4694
|
+
color: sig.borderColor.replace("#", "")
|
|
3001
4695
|
}
|
|
3002
4696
|
},
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
4697
|
+
spacing: { after: 60 }
|
|
4698
|
+
})
|
|
4699
|
+
);
|
|
4700
|
+
}
|
|
4701
|
+
cellParagraphs.push(
|
|
4702
|
+
new Paragraph({
|
|
4703
|
+
children: [
|
|
4704
|
+
new TextRun({
|
|
4705
|
+
text: item.name,
|
|
4706
|
+
font: defaultFont,
|
|
4707
|
+
size: 21,
|
|
4708
|
+
// 10.5pt
|
|
4709
|
+
bold: true,
|
|
4710
|
+
color: sig.nameColor.replace("#", "")
|
|
4711
|
+
})
|
|
4712
|
+
],
|
|
4713
|
+
spacing: { before: sig.style === "line" ? 40 : 20, after: 20 }
|
|
4714
|
+
})
|
|
4715
|
+
);
|
|
4716
|
+
if (item.role) {
|
|
4717
|
+
cellParagraphs.push(
|
|
4718
|
+
new Paragraph({
|
|
4719
|
+
children: [
|
|
4720
|
+
new TextRun({
|
|
4721
|
+
text: item.role,
|
|
4722
|
+
font: defaultFont,
|
|
4723
|
+
size: 18,
|
|
4724
|
+
// 9pt
|
|
4725
|
+
color: sig.roleColor.replace("#", "")
|
|
4726
|
+
})
|
|
4727
|
+
],
|
|
4728
|
+
spacing: { after: 20 }
|
|
4729
|
+
})
|
|
4730
|
+
);
|
|
4731
|
+
}
|
|
4732
|
+
if (item.date) {
|
|
4733
|
+
cellParagraphs.push(
|
|
4734
|
+
new Paragraph({
|
|
4735
|
+
children: [
|
|
4736
|
+
new TextRun({
|
|
4737
|
+
text: `Date: ${item.date}`,
|
|
4738
|
+
font: defaultFont,
|
|
4739
|
+
size: 17,
|
|
4740
|
+
// 8.5pt
|
|
4741
|
+
color: sig.roleColor.replace("#", "")
|
|
4742
|
+
})
|
|
4743
|
+
],
|
|
4744
|
+
spacing: { after: 20 }
|
|
4745
|
+
})
|
|
4746
|
+
);
|
|
4747
|
+
}
|
|
4748
|
+
const isBox = sig.style === "box";
|
|
4749
|
+
const boxBorder = { style: BorderStyle.SINGLE, size: 4, color: sig.borderColor.replace("#", "") };
|
|
4750
|
+
const noneBorder = { style: BorderStyle.NONE, size: 0, color: "auto" };
|
|
4751
|
+
return new TableCell({
|
|
4752
|
+
width: { size: widthDxa, type: WidthType.DXA },
|
|
4753
|
+
shading: isBox ? { fill: "F8FAFC", type: ShadingType.CLEAR } : void 0,
|
|
4754
|
+
margins: isBox ? { top: 140, bottom: 140, left: 160, right: 160 } : { top: 60, bottom: 60, left: 60, right: 60 },
|
|
4755
|
+
borders: {
|
|
4756
|
+
top: isBox ? boxBorder : noneBorder,
|
|
4757
|
+
bottom: isBox ? boxBorder : noneBorder,
|
|
4758
|
+
left: isBox ? boxBorder : noneBorder,
|
|
4759
|
+
right: isBox ? boxBorder : noneBorder
|
|
4760
|
+
},
|
|
4761
|
+
children: cellParagraphs
|
|
4762
|
+
});
|
|
4763
|
+
}
|
|
4764
|
+
function createEmptyDocxCell(widthDxa) {
|
|
4765
|
+
const noneBorder = { style: BorderStyle.NONE, size: 0, color: "auto" };
|
|
4766
|
+
return new TableCell({
|
|
4767
|
+
width: { size: widthDxa, type: WidthType.DXA },
|
|
4768
|
+
borders: {
|
|
4769
|
+
top: noneBorder,
|
|
4770
|
+
bottom: noneBorder,
|
|
4771
|
+
left: noneBorder,
|
|
4772
|
+
right: noneBorder
|
|
4773
|
+
},
|
|
4774
|
+
children: [new Paragraph({})]
|
|
3008
4775
|
});
|
|
3009
|
-
return await Packer.toBuffer(document);
|
|
3010
4776
|
}
|
|
3011
4777
|
|
|
3012
4778
|
// src/core/engine.ts
|
|
@@ -3101,14 +4867,885 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
|
|
|
3101
4867
|
};
|
|
3102
4868
|
}
|
|
3103
4869
|
|
|
4870
|
+
// src/server/previewServer.ts
|
|
4871
|
+
import * as http from "http";
|
|
4872
|
+
import * as fs7 from "fs";
|
|
4873
|
+
import * as path7 from "path";
|
|
4874
|
+
async function startPreviewServer(options) {
|
|
4875
|
+
const absoluteFilePath = path7.resolve(process.cwd(), options.filePath);
|
|
4876
|
+
if (!fs7.existsSync(absoluteFilePath)) {
|
|
4877
|
+
throw new Error(`MarkForge preview error: File not found at "${absoluteFilePath}"`);
|
|
4878
|
+
}
|
|
4879
|
+
const baseDir = path7.dirname(absoluteFilePath);
|
|
4880
|
+
const { config: fileConfig } = await loadConfig(void 0, baseDir);
|
|
4881
|
+
const baseConfig = options.config || fileConfig;
|
|
4882
|
+
const port = options.port || 3e3;
|
|
4883
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
4884
|
+
const broadcastReload = () => {
|
|
4885
|
+
sseClients.forEach((client) => {
|
|
4886
|
+
try {
|
|
4887
|
+
client.write(`event: reload
|
|
4888
|
+
data: ${Date.now()}
|
|
4889
|
+
|
|
4890
|
+
`);
|
|
4891
|
+
} catch {
|
|
4892
|
+
sseClients.delete(client);
|
|
4893
|
+
}
|
|
4894
|
+
});
|
|
4895
|
+
};
|
|
4896
|
+
let debounceTimer = null;
|
|
4897
|
+
const watcher = fs7.watch(baseDir, { recursive: false }, (_event, filename) => {
|
|
4898
|
+
if (!filename) return;
|
|
4899
|
+
const changedPath = path7.resolve(baseDir, filename);
|
|
4900
|
+
if (changedPath === absoluteFilePath || filename.includes("markforge") || filename.endsWith(".css")) {
|
|
4901
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
4902
|
+
debounceTimer = setTimeout(() => {
|
|
4903
|
+
broadcastReload();
|
|
4904
|
+
}, 150);
|
|
4905
|
+
}
|
|
4906
|
+
});
|
|
4907
|
+
const server = http.createServer(async (req, res) => {
|
|
4908
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
4909
|
+
if (url.pathname === "/events") {
|
|
4910
|
+
res.writeHead(200, {
|
|
4911
|
+
"Content-Type": "text/event-stream",
|
|
4912
|
+
"Cache-Control": "no-cache, no-transform",
|
|
4913
|
+
Connection: "keep-alive"
|
|
4914
|
+
});
|
|
4915
|
+
res.write(`data: connected
|
|
4916
|
+
|
|
4917
|
+
`);
|
|
4918
|
+
sseClients.add(res);
|
|
4919
|
+
req.on("close", () => {
|
|
4920
|
+
sseClients.delete(res);
|
|
4921
|
+
});
|
|
4922
|
+
return;
|
|
4923
|
+
}
|
|
4924
|
+
if (url.pathname === "/api/file-content" && req.method === "GET") {
|
|
4925
|
+
try {
|
|
4926
|
+
const content = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
4927
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4928
|
+
res.end(
|
|
4929
|
+
JSON.stringify({
|
|
4930
|
+
content,
|
|
4931
|
+
fileName: path7.basename(absoluteFilePath),
|
|
4932
|
+
filePath: absoluteFilePath
|
|
4933
|
+
})
|
|
4934
|
+
);
|
|
4935
|
+
} catch (err) {
|
|
4936
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4937
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
4938
|
+
res.end(JSON.stringify({ error: msg }));
|
|
4939
|
+
}
|
|
4940
|
+
return;
|
|
4941
|
+
}
|
|
4942
|
+
if (url.pathname === "/api/save-content" && req.method === "POST") {
|
|
4943
|
+
let body = "";
|
|
4944
|
+
req.on("data", (chunk) => {
|
|
4945
|
+
body += chunk;
|
|
4946
|
+
});
|
|
4947
|
+
req.on("end", () => {
|
|
4948
|
+
try {
|
|
4949
|
+
const parsed = JSON.parse(body);
|
|
4950
|
+
if (typeof parsed.content === "string") {
|
|
4951
|
+
fs7.writeFileSync(absoluteFilePath, parsed.content, "utf-8");
|
|
4952
|
+
broadcastReload();
|
|
4953
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4954
|
+
res.end(JSON.stringify({ success: true, savedAt: Date.now() }));
|
|
4955
|
+
} else {
|
|
4956
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
4957
|
+
res.end(JSON.stringify({ error: "Missing content field in request body" }));
|
|
4958
|
+
}
|
|
4959
|
+
} catch (err) {
|
|
4960
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4961
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
4962
|
+
res.end(JSON.stringify({ error: msg }));
|
|
4963
|
+
}
|
|
4964
|
+
});
|
|
4965
|
+
return;
|
|
4966
|
+
}
|
|
4967
|
+
if (url.pathname === "/api/export" && (req.method === "GET" || req.method === "POST")) {
|
|
4968
|
+
const format = url.searchParams.get("format") || "docx";
|
|
4969
|
+
try {
|
|
4970
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
4971
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
4972
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
4973
|
+
const mergedConfig = { ...baseConfig, ...resolvedConfig };
|
|
4974
|
+
const fileBase = path7.basename(absoluteFilePath, path7.extname(absoluteFilePath));
|
|
4975
|
+
if (format === "docx") {
|
|
4976
|
+
const buffer = await buildDocxDocument(doc, mergedConfig, baseDir);
|
|
4977
|
+
res.writeHead(200, {
|
|
4978
|
+
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
4979
|
+
"Content-Disposition": `attachment; filename="${fileBase}.docx"`
|
|
4980
|
+
});
|
|
4981
|
+
res.end(buffer);
|
|
4982
|
+
return;
|
|
4983
|
+
} else if (format === "pdf") {
|
|
4984
|
+
const buffer = await buildPdfDocument(doc, mergedConfig, baseDir);
|
|
4985
|
+
res.writeHead(200, {
|
|
4986
|
+
"Content-Type": "application/pdf",
|
|
4987
|
+
"Content-Disposition": `attachment; filename="${fileBase}.pdf"`
|
|
4988
|
+
});
|
|
4989
|
+
res.end(buffer);
|
|
4990
|
+
return;
|
|
4991
|
+
} else {
|
|
4992
|
+
const html = await buildHtmlDocument(doc, mergedConfig, baseDir);
|
|
4993
|
+
res.writeHead(200, {
|
|
4994
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
4995
|
+
"Content-Disposition": `attachment; filename="${fileBase}.html"`
|
|
4996
|
+
});
|
|
4997
|
+
res.end(html);
|
|
4998
|
+
return;
|
|
4999
|
+
}
|
|
5000
|
+
} catch (err) {
|
|
5001
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5002
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
5003
|
+
res.end(`Export failed: ${msg}`);
|
|
5004
|
+
return;
|
|
5005
|
+
}
|
|
5006
|
+
}
|
|
5007
|
+
if (url.pathname === "/document-content") {
|
|
5008
|
+
try {
|
|
5009
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5010
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
5011
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
5012
|
+
const html = await buildHtmlDocument(doc, { ...baseConfig, ...resolvedConfig }, baseDir);
|
|
5013
|
+
const injectedScript = `
|
|
5014
|
+
<script>
|
|
5015
|
+
(function() {
|
|
5016
|
+
var evtSource = new EventSource('/events');
|
|
5017
|
+
evtSource.addEventListener('reload', function() {
|
|
5018
|
+
var scrollPos = window.scrollY;
|
|
5019
|
+
sessionStorage.setItem('markforge_scroll', scrollPos);
|
|
5020
|
+
window.location.reload();
|
|
5021
|
+
});
|
|
5022
|
+
window.addEventListener('load', function() {
|
|
5023
|
+
var saved = sessionStorage.getItem('markforge_scroll');
|
|
5024
|
+
if (saved) {
|
|
5025
|
+
window.scrollTo(0, parseInt(saved, 10));
|
|
5026
|
+
}
|
|
5027
|
+
});
|
|
5028
|
+
})();
|
|
5029
|
+
</script>
|
|
5030
|
+
`;
|
|
5031
|
+
const finalHtml = html.replace("</body>", `${injectedScript}</body>`);
|
|
5032
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5033
|
+
res.end(finalHtml);
|
|
5034
|
+
} catch (err) {
|
|
5035
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5036
|
+
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
5037
|
+
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>`);
|
|
5038
|
+
}
|
|
5039
|
+
return;
|
|
5040
|
+
}
|
|
5041
|
+
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
5042
|
+
const fileName = path7.basename(absoluteFilePath);
|
|
5043
|
+
const initialContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5044
|
+
const appHtml = `<!DOCTYPE html>
|
|
5045
|
+
<html lang="en">
|
|
5046
|
+
<head>
|
|
5047
|
+
<meta charset="UTF-8">
|
|
5048
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5049
|
+
<title>MarkForge Live Studio - ${escapeHtml2(fileName)}</title>
|
|
5050
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
5051
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
5052
|
+
<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">
|
|
5053
|
+
<style>
|
|
5054
|
+
:root {
|
|
5055
|
+
--mf-primary: #0D998D;
|
|
5056
|
+
--mf-primary-dark: #008277;
|
|
5057
|
+
--mf-primary-light: #ECFDFD;
|
|
5058
|
+
--mf-primary-border: #33CDCF;
|
|
5059
|
+
--mf-dark: #0F172A;
|
|
5060
|
+
--mf-slate: #1E293B;
|
|
5061
|
+
--mf-editor-bg: #0F172A;
|
|
5062
|
+
--mf-editor-gutter: #1E293B;
|
|
5063
|
+
--mf-editor-text: #F8FAFC;
|
|
5064
|
+
--mf-muted: #64748B;
|
|
5065
|
+
--mf-light-border: #E2E8F0;
|
|
5066
|
+
--mf-bg: #F1F5F9;
|
|
5067
|
+
}
|
|
5068
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5069
|
+
body {
|
|
5070
|
+
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
5071
|
+
background: var(--mf-bg);
|
|
5072
|
+
color: var(--mf-dark);
|
|
5073
|
+
display: flex;
|
|
5074
|
+
flex-direction: column;
|
|
5075
|
+
height: 100vh;
|
|
5076
|
+
overflow: hidden;
|
|
5077
|
+
}
|
|
5078
|
+
header {
|
|
5079
|
+
background: #FFFFFF;
|
|
5080
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5081
|
+
min-height: 56px;
|
|
5082
|
+
display: flex;
|
|
5083
|
+
flex-wrap: wrap;
|
|
5084
|
+
align-items: center;
|
|
5085
|
+
justify-content: space-between;
|
|
5086
|
+
padding: 0.4rem 1.2rem;
|
|
5087
|
+
z-index: 10;
|
|
5088
|
+
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
|
5089
|
+
gap: 0.75rem;
|
|
5090
|
+
}
|
|
5091
|
+
.brand-section {
|
|
5092
|
+
display: flex;
|
|
5093
|
+
align-items: center;
|
|
5094
|
+
gap: 0.75rem;
|
|
5095
|
+
}
|
|
5096
|
+
.brand-badge {
|
|
5097
|
+
font-size: 0.72rem;
|
|
5098
|
+
font-weight: 800;
|
|
5099
|
+
letter-spacing: 0.08em;
|
|
5100
|
+
background: var(--mf-dark);
|
|
5101
|
+
color: #FFFFFF;
|
|
5102
|
+
padding: 0.25rem 0.55rem;
|
|
5103
|
+
border-radius: 4px;
|
|
5104
|
+
text-transform: uppercase;
|
|
5105
|
+
}
|
|
5106
|
+
.file-name {
|
|
5107
|
+
font-size: 0.9rem;
|
|
5108
|
+
font-weight: 700;
|
|
5109
|
+
color: var(--mf-dark);
|
|
5110
|
+
}
|
|
5111
|
+
.sync-status {
|
|
5112
|
+
display: flex;
|
|
5113
|
+
align-items: center;
|
|
5114
|
+
gap: 0.35rem;
|
|
5115
|
+
font-size: 0.75rem;
|
|
5116
|
+
font-weight: 600;
|
|
5117
|
+
color: var(--mf-primary-dark);
|
|
5118
|
+
background: var(--mf-primary-light);
|
|
5119
|
+
padding: 0.2rem 0.55rem;
|
|
5120
|
+
border-radius: 9999px;
|
|
5121
|
+
border: 1px solid var(--mf-primary-border);
|
|
5122
|
+
}
|
|
5123
|
+
.sync-dot {
|
|
5124
|
+
width: 7px;
|
|
5125
|
+
height: 7px;
|
|
5126
|
+
background-color: var(--mf-primary);
|
|
5127
|
+
border-radius: 50%;
|
|
5128
|
+
box-shadow: 0 0 0 2px rgba(13, 153, 141, 0.2);
|
|
5129
|
+
}
|
|
5130
|
+
.toolbar-section {
|
|
5131
|
+
display: flex;
|
|
5132
|
+
align-items: center;
|
|
5133
|
+
gap: 0.3rem;
|
|
5134
|
+
background: #F8FAFC;
|
|
5135
|
+
padding: 0.25rem 0.4rem;
|
|
5136
|
+
border-radius: 6px;
|
|
5137
|
+
border: 1px solid var(--mf-light-border);
|
|
5138
|
+
}
|
|
5139
|
+
.tool-btn {
|
|
5140
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5141
|
+
font-size: 0.75rem;
|
|
5142
|
+
font-weight: 600;
|
|
5143
|
+
padding: 0.25rem 0.45rem;
|
|
5144
|
+
background: transparent;
|
|
5145
|
+
border: 1px solid transparent;
|
|
5146
|
+
border-radius: 4px;
|
|
5147
|
+
cursor: pointer;
|
|
5148
|
+
color: var(--mf-slate);
|
|
5149
|
+
transition: all 0.1s ease;
|
|
5150
|
+
}
|
|
5151
|
+
.tool-btn:hover {
|
|
5152
|
+
background: #FFFFFF;
|
|
5153
|
+
border-color: var(--mf-light-border);
|
|
5154
|
+
color: var(--mf-primary-dark);
|
|
5155
|
+
}
|
|
5156
|
+
.tool-divider {
|
|
5157
|
+
width: 1px;
|
|
5158
|
+
height: 16px;
|
|
5159
|
+
background: var(--mf-light-border);
|
|
5160
|
+
margin: 0 0.15rem;
|
|
5161
|
+
}
|
|
5162
|
+
.controls {
|
|
5163
|
+
display: flex;
|
|
5164
|
+
align-items: center;
|
|
5165
|
+
gap: 0.5rem;
|
|
5166
|
+
}
|
|
5167
|
+
.view-toggles {
|
|
5168
|
+
display: flex;
|
|
5169
|
+
background: #F1F5F9;
|
|
5170
|
+
padding: 2px;
|
|
5171
|
+
border-radius: 6px;
|
|
5172
|
+
border: 1px solid var(--mf-light-border);
|
|
5173
|
+
}
|
|
5174
|
+
.toggle-btn {
|
|
5175
|
+
font-family: inherit;
|
|
5176
|
+
font-size: 0.74rem;
|
|
5177
|
+
font-weight: 600;
|
|
5178
|
+
padding: 0.25rem 0.55rem;
|
|
5179
|
+
border: none;
|
|
5180
|
+
background: transparent;
|
|
5181
|
+
border-radius: 4px;
|
|
5182
|
+
cursor: pointer;
|
|
5183
|
+
color: var(--mf-muted);
|
|
5184
|
+
transition: all 0.15s ease;
|
|
5185
|
+
}
|
|
5186
|
+
.toggle-btn.active {
|
|
5187
|
+
background: #FFFFFF;
|
|
5188
|
+
color: var(--mf-dark);
|
|
5189
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
|
5190
|
+
}
|
|
5191
|
+
.btn {
|
|
5192
|
+
font-family: inherit;
|
|
5193
|
+
font-size: 0.78rem;
|
|
5194
|
+
font-weight: 600;
|
|
5195
|
+
padding: 0.35rem 0.75rem;
|
|
5196
|
+
border-radius: 6px;
|
|
5197
|
+
cursor: pointer;
|
|
5198
|
+
text-decoration: none;
|
|
5199
|
+
transition: all 0.15s ease;
|
|
5200
|
+
display: inline-flex;
|
|
5201
|
+
align-items: center;
|
|
5202
|
+
gap: 0.3rem;
|
|
5203
|
+
border: 1px solid var(--mf-light-border);
|
|
5204
|
+
background: #FFFFFF;
|
|
5205
|
+
color: var(--mf-dark);
|
|
5206
|
+
}
|
|
5207
|
+
.btn:hover {
|
|
5208
|
+
background: #F8FAFC;
|
|
5209
|
+
border-color: #CBD5E1;
|
|
5210
|
+
}
|
|
5211
|
+
.btn-primary {
|
|
5212
|
+
background: var(--mf-primary);
|
|
5213
|
+
color: #FFFFFF;
|
|
5214
|
+
border-color: var(--mf-primary);
|
|
5215
|
+
}
|
|
5216
|
+
.btn-primary:hover {
|
|
5217
|
+
background: var(--mf-primary-dark);
|
|
5218
|
+
border-color: var(--mf-primary-dark);
|
|
5219
|
+
}
|
|
5220
|
+
.save-indicator {
|
|
5221
|
+
font-size: 0.75rem;
|
|
5222
|
+
font-weight: 600;
|
|
5223
|
+
color: var(--mf-muted);
|
|
5224
|
+
min-width: 65px;
|
|
5225
|
+
text-align: right;
|
|
5226
|
+
}
|
|
5227
|
+
.save-indicator.saved {
|
|
5228
|
+
color: var(--mf-primary-dark);
|
|
5229
|
+
}
|
|
5230
|
+
.save-indicator.saving {
|
|
5231
|
+
color: #D97706;
|
|
5232
|
+
}
|
|
5233
|
+
.save-indicator.unsaved {
|
|
5234
|
+
color: #E11D48;
|
|
5235
|
+
}
|
|
5236
|
+
|
|
5237
|
+
/* Main Workspace Splitter Layout */
|
|
5238
|
+
main.workspace {
|
|
5239
|
+
flex: 1;
|
|
5240
|
+
display: flex;
|
|
5241
|
+
height: calc(100vh - 56px);
|
|
5242
|
+
overflow: hidden;
|
|
5243
|
+
background: var(--mf-bg);
|
|
5244
|
+
position: relative;
|
|
5245
|
+
}
|
|
5246
|
+
.editor-pane {
|
|
5247
|
+
width: 50%;
|
|
5248
|
+
height: 100%;
|
|
5249
|
+
display: flex;
|
|
5250
|
+
flex-direction: column;
|
|
5251
|
+
background: var(--mf-editor-bg);
|
|
5252
|
+
border-right: 1px solid #334155;
|
|
5253
|
+
overflow: hidden;
|
|
5254
|
+
}
|
|
5255
|
+
.editor-header {
|
|
5256
|
+
background: #090D16;
|
|
5257
|
+
border-bottom: 1px solid #1E293B;
|
|
5258
|
+
padding: 0.4rem 0.8rem;
|
|
5259
|
+
display: flex;
|
|
5260
|
+
align-items: center;
|
|
5261
|
+
justify-content: space-between;
|
|
5262
|
+
color: #94A3B8;
|
|
5263
|
+
font-size: 0.74rem;
|
|
5264
|
+
font-weight: 500;
|
|
5265
|
+
}
|
|
5266
|
+
.editor-container {
|
|
5267
|
+
flex: 1;
|
|
5268
|
+
display: flex;
|
|
5269
|
+
position: relative;
|
|
5270
|
+
overflow: hidden;
|
|
5271
|
+
background: var(--mf-editor-bg);
|
|
5272
|
+
}
|
|
5273
|
+
.line-numbers {
|
|
5274
|
+
width: 44px;
|
|
5275
|
+
padding: 0.8rem 0.4rem;
|
|
5276
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5277
|
+
font-size: 13px;
|
|
5278
|
+
line-height: 1.55;
|
|
5279
|
+
color: #475569;
|
|
5280
|
+
text-align: right;
|
|
5281
|
+
user-select: none;
|
|
5282
|
+
background: var(--mf-editor-gutter);
|
|
5283
|
+
overflow: hidden;
|
|
5284
|
+
border-right: 1px solid #1E293B;
|
|
5285
|
+
}
|
|
5286
|
+
.code-editor {
|
|
5287
|
+
flex: 1;
|
|
5288
|
+
padding: 0.8rem 1rem;
|
|
5289
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5290
|
+
font-size: 13px;
|
|
5291
|
+
line-height: 1.55;
|
|
5292
|
+
color: var(--mf-editor-text);
|
|
5293
|
+
background: transparent;
|
|
5294
|
+
border: none;
|
|
5295
|
+
outline: none;
|
|
5296
|
+
resize: none;
|
|
5297
|
+
white-space: pre;
|
|
5298
|
+
overflow-wrap: normal;
|
|
5299
|
+
overflow: auto;
|
|
5300
|
+
tab-size: 2;
|
|
5301
|
+
}
|
|
5302
|
+
|
|
5303
|
+
/* Draggable Splitter Handle */
|
|
5304
|
+
.splitter {
|
|
5305
|
+
width: 8px;
|
|
5306
|
+
cursor: col-resize;
|
|
5307
|
+
background: #E2E8F0;
|
|
5308
|
+
transition: background 0.15s ease;
|
|
5309
|
+
position: relative;
|
|
5310
|
+
z-index: 5;
|
|
5311
|
+
}
|
|
5312
|
+
.splitter:hover, .splitter.active {
|
|
5313
|
+
background: var(--mf-primary);
|
|
5314
|
+
}
|
|
5315
|
+
|
|
5316
|
+
/* Right Preview Pane */
|
|
5317
|
+
.preview-pane {
|
|
5318
|
+
width: 50%;
|
|
5319
|
+
height: 100%;
|
|
5320
|
+
display: flex;
|
|
5321
|
+
flex-direction: column;
|
|
5322
|
+
background: #FFFFFF;
|
|
5323
|
+
overflow: hidden;
|
|
5324
|
+
}
|
|
5325
|
+
.preview-header {
|
|
5326
|
+
background: #FFFFFF;
|
|
5327
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5328
|
+
padding: 0.35rem 0.8rem;
|
|
5329
|
+
display: flex;
|
|
5330
|
+
align-items: center;
|
|
5331
|
+
justify-content: space-between;
|
|
5332
|
+
color: var(--mf-muted);
|
|
5333
|
+
font-size: 0.74rem;
|
|
5334
|
+
font-weight: 600;
|
|
5335
|
+
}
|
|
5336
|
+
.viewport-selector {
|
|
5337
|
+
display: flex;
|
|
5338
|
+
gap: 0.25rem;
|
|
5339
|
+
}
|
|
5340
|
+
.vp-btn {
|
|
5341
|
+
font-size: 0.72rem;
|
|
5342
|
+
padding: 0.15rem 0.4rem;
|
|
5343
|
+
border: 1px solid var(--mf-light-border);
|
|
5344
|
+
background: #F8FAFC;
|
|
5345
|
+
border-radius: 4px;
|
|
5346
|
+
cursor: pointer;
|
|
5347
|
+
color: var(--mf-muted);
|
|
5348
|
+
}
|
|
5349
|
+
.vp-btn.active {
|
|
5350
|
+
background: var(--mf-primary-light);
|
|
5351
|
+
color: var(--mf-primary-dark);
|
|
5352
|
+
border-color: var(--mf-primary-border);
|
|
5353
|
+
}
|
|
5354
|
+
.preview-wrapper {
|
|
5355
|
+
flex: 1;
|
|
5356
|
+
display: flex;
|
|
5357
|
+
justify-content: center;
|
|
5358
|
+
align-items: stretch;
|
|
5359
|
+
background: #F1F5F9;
|
|
5360
|
+
overflow: hidden;
|
|
5361
|
+
}
|
|
5362
|
+
iframe {
|
|
5363
|
+
width: 100%;
|
|
5364
|
+
height: 100%;
|
|
5365
|
+
border: none;
|
|
5366
|
+
background: #FFFFFF;
|
|
5367
|
+
transition: max-width 0.2s ease;
|
|
5368
|
+
}
|
|
5369
|
+
.author-footer {
|
|
5370
|
+
font-size: 0.72rem;
|
|
5371
|
+
color: var(--mf-muted);
|
|
5372
|
+
padding-right: 0.5rem;
|
|
5373
|
+
}
|
|
5374
|
+
.author-footer a {
|
|
5375
|
+
color: var(--mf-primary-dark);
|
|
5376
|
+
text-decoration: none;
|
|
5377
|
+
font-weight: 600;
|
|
5378
|
+
}
|
|
5379
|
+
</style>
|
|
5380
|
+
</head>
|
|
5381
|
+
<body>
|
|
5382
|
+
<header>
|
|
5383
|
+
<div class="brand-section">
|
|
5384
|
+
<span class="brand-badge">MARKFORGE STUDIO</span>
|
|
5385
|
+
<span class="file-name" title="${escapeHtml2(absoluteFilePath)}">${escapeHtml2(fileName)}</span>
|
|
5386
|
+
<div class="sync-status">
|
|
5387
|
+
<div class="sync-dot"></div>
|
|
5388
|
+
<span>Live Sync Active</span>
|
|
5389
|
+
</div>
|
|
5390
|
+
</div>
|
|
5391
|
+
|
|
5392
|
+
<!-- Quick Formatting Toolbar -->
|
|
5393
|
+
<div class="toolbar-section">
|
|
5394
|
+
<button class="tool-btn" onclick="insertFormat('h1')" title="Heading 1">H1</button>
|
|
5395
|
+
<button class="tool-btn" onclick="insertFormat('h2')" title="Heading 2">H2</button>
|
|
5396
|
+
<button class="tool-btn" onclick="insertFormat('h3')" title="Heading 3">H3</button>
|
|
5397
|
+
<div class="tool-divider"></div>
|
|
5398
|
+
<button class="tool-btn" onclick="insertFormat('bold')" title="Bold">B</button>
|
|
5399
|
+
<button class="tool-btn" onclick="insertFormat('italic')" title="Italic">I</button>
|
|
5400
|
+
<button class="tool-btn" onclick="insertFormat('code')" title="Inline Code"><></button>
|
|
5401
|
+
<button class="tool-btn" onclick="insertFormat('quote')" title="Blockquote">></button>
|
|
5402
|
+
<div class="tool-divider"></div>
|
|
5403
|
+
<button class="tool-btn" onclick="insertFormat('table')" title="GFM Table">Table</button>
|
|
5404
|
+
<button class="tool-btn" onclick="insertFormat('list')" title="List">List</button>
|
|
5405
|
+
<button class="tool-btn" onclick="insertFormat('task')" title="Task Checklist">Task</button>
|
|
5406
|
+
<div class="tool-divider"></div>
|
|
5407
|
+
<button class="tool-btn" onclick="insertFormat('callout')" title="Callout Box">Callout</button>
|
|
5408
|
+
<button class="tool-btn" onclick="insertFormat('math')" title="LaTeX Math">Math</button>
|
|
5409
|
+
<button class="tool-btn" onclick="insertFormat('columns')" title="Multi-Columns">Columns</button>
|
|
5410
|
+
<button class="tool-btn" onclick="insertFormat('footnote')" title="Footnote">Footnote</button>
|
|
5411
|
+
<button class="tool-btn" onclick="insertFormat('mermaid')" title="Mermaid Diagram">Mermaid</button>
|
|
5412
|
+
</div>
|
|
5413
|
+
|
|
5414
|
+
<!-- Controls & View Mode -->
|
|
5415
|
+
<div class="controls">
|
|
5416
|
+
<div class="view-toggles">
|
|
5417
|
+
<button class="toggle-btn active" id="btn-split" onclick="setViewMode('split')">Split</button>
|
|
5418
|
+
<button class="toggle-btn" id="btn-edit" onclick="setViewMode('edit')">Editor</button>
|
|
5419
|
+
<button class="toggle-btn" id="btn-prev" onclick="setViewMode('prev')">Preview</button>
|
|
5420
|
+
</div>
|
|
5421
|
+
<span class="save-indicator saved" id="save-status">Saved</span>
|
|
5422
|
+
<button class="btn btn-primary" onclick="saveContentManual()" title="Save (Ctrl+S)">Save</button>
|
|
5423
|
+
<button class="btn" onclick="exportDoc('docx')" title="Download Word Document">DOCX</button>
|
|
5424
|
+
<button class="btn" onclick="exportDoc('pdf')" title="Download PDF Document">PDF</button>
|
|
5425
|
+
<button class="btn" onclick="printDoc()" title="Print / PDF dialog">Print</button>
|
|
5426
|
+
</div>
|
|
5427
|
+
</header>
|
|
5428
|
+
|
|
5429
|
+
<main class="workspace" id="workspace">
|
|
5430
|
+
<!-- Left: Code Editor Pane -->
|
|
5431
|
+
<div class="editor-pane" id="editor-pane">
|
|
5432
|
+
<div class="editor-header">
|
|
5433
|
+
<span>MARKDOWN SOURCE</span>
|
|
5434
|
+
<span id="editor-stats">Lines: 1 | Words: 0 | UTF-8</span>
|
|
5435
|
+
</div>
|
|
5436
|
+
<div class="editor-container">
|
|
5437
|
+
<div class="line-numbers" id="line-numbers">1</div>
|
|
5438
|
+
<textarea class="code-editor" id="code-editor" spellcheck="false" placeholder="Write markdown here...">${escapeHtml2(initialContent)}</textarea>
|
|
5439
|
+
</div>
|
|
5440
|
+
</div>
|
|
5441
|
+
|
|
5442
|
+
<!-- Middle: Draggable Splitter Handle -->
|
|
5443
|
+
<div class="splitter" id="splitter"></div>
|
|
5444
|
+
|
|
5445
|
+
<!-- Right: Rendered Preview Pane -->
|
|
5446
|
+
<div class="preview-pane" id="preview-pane">
|
|
5447
|
+
<div class="preview-header">
|
|
5448
|
+
<span>RENDERED PREVIEW</span>
|
|
5449
|
+
<div class="viewport-selector">
|
|
5450
|
+
<button class="vp-btn active" onclick="setViewport('100%')" id="vp-full">100% Full</button>
|
|
5451
|
+
<button class="vp-btn" onclick="setViewport('820px')" id="vp-a4">A4 (820px)</button>
|
|
5452
|
+
<button class="vp-btn" onclick="setViewport('440px')" id="vp-mob">Mobile</button>
|
|
5453
|
+
</div>
|
|
5454
|
+
<span class="author-footer">Created by <a href="https://github.com/masumrpg" target="_blank">Ma'sum (@masumrpg)</a></span>
|
|
5455
|
+
</div>
|
|
5456
|
+
<div class="preview-wrapper">
|
|
5457
|
+
<iframe id="preview-frame" src="/document-content"></iframe>
|
|
5458
|
+
</div>
|
|
5459
|
+
</div>
|
|
5460
|
+
</main>
|
|
5461
|
+
|
|
5462
|
+
<script>
|
|
5463
|
+
var editor = document.getElementById('code-editor');
|
|
5464
|
+
var lineNumbers = document.getElementById('line-numbers');
|
|
5465
|
+
var stats = document.getElementById('editor-stats');
|
|
5466
|
+
var saveStatus = document.getElementById('save-status');
|
|
5467
|
+
var previewFrame = document.getElementById('preview-frame');
|
|
5468
|
+
var editorPane = document.getElementById('editor-pane');
|
|
5469
|
+
var previewPane = document.getElementById('preview-pane');
|
|
5470
|
+
var splitter = document.getElementById('splitter');
|
|
5471
|
+
var isDirty = false;
|
|
5472
|
+
var autoSaveTimeout = null;
|
|
5473
|
+
|
|
5474
|
+
// Update Line Numbers & Stats
|
|
5475
|
+
function updateStatsAndLines() {
|
|
5476
|
+
var lines = editor.value.split('\\n');
|
|
5477
|
+
var lineCount = lines.length;
|
|
5478
|
+
var numHtml = '';
|
|
5479
|
+
for (var i = 1; i <= lineCount; i++) {
|
|
5480
|
+
numHtml += i + '<br>';
|
|
5481
|
+
}
|
|
5482
|
+
lineNumbers.innerHTML = numHtml;
|
|
5483
|
+
|
|
5484
|
+
var words = editor.value.trim().length > 0 ? editor.value.trim().split(/\\s+/).length : 0;
|
|
5485
|
+
var chars = editor.value.length;
|
|
5486
|
+
stats.textContent = 'Lines: ' + lineCount + ' | Words: ' + words + ' | Chars: ' + chars + ' | UTF-8';
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5489
|
+
// Synchronize vertical scroll between Line Numbers and Textarea
|
|
5490
|
+
editor.addEventListener('scroll', function() {
|
|
5491
|
+
lineNumbers.scrollTop = editor.scrollTop;
|
|
5492
|
+
});
|
|
5493
|
+
|
|
5494
|
+
// Handle Input & Debounced Auto-Save
|
|
5495
|
+
editor.addEventListener('input', function() {
|
|
5496
|
+
updateStatsAndLines();
|
|
5497
|
+
setSaveState('unsaved');
|
|
5498
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5499
|
+
autoSaveTimeout = setTimeout(function() {
|
|
5500
|
+
saveContent();
|
|
5501
|
+
}, 600);
|
|
5502
|
+
});
|
|
5503
|
+
|
|
5504
|
+
function setSaveState(state) {
|
|
5505
|
+
if (state === 'saved') {
|
|
5506
|
+
saveStatus.textContent = 'Saved';
|
|
5507
|
+
saveStatus.className = 'save-indicator saved';
|
|
5508
|
+
isDirty = false;
|
|
5509
|
+
} else if (state === 'saving') {
|
|
5510
|
+
saveStatus.textContent = 'Saving...';
|
|
5511
|
+
saveStatus.className = 'save-indicator saving';
|
|
5512
|
+
} else {
|
|
5513
|
+
saveStatus.textContent = 'Changes...';
|
|
5514
|
+
saveStatus.className = 'save-indicator unsaved';
|
|
5515
|
+
isDirty = true;
|
|
5516
|
+
}
|
|
5517
|
+
}
|
|
5518
|
+
|
|
5519
|
+
// Save Content via API
|
|
5520
|
+
function saveContent(callback) {
|
|
5521
|
+
setSaveState('saving');
|
|
5522
|
+
fetch('/api/save-content', {
|
|
5523
|
+
method: 'POST',
|
|
5524
|
+
headers: { 'Content-Type': 'application/json' },
|
|
5525
|
+
body: JSON.stringify({ content: editor.value }),
|
|
5526
|
+
})
|
|
5527
|
+
.then(function(res) { return res.json(); })
|
|
5528
|
+
.then(function(data) {
|
|
5529
|
+
if (data.success) {
|
|
5530
|
+
setSaveState('saved');
|
|
5531
|
+
if (callback) callback();
|
|
5532
|
+
} else {
|
|
5533
|
+
saveStatus.textContent = 'Save Error';
|
|
5534
|
+
}
|
|
5535
|
+
})
|
|
5536
|
+
.catch(function() {
|
|
5537
|
+
saveStatus.textContent = 'Save Error';
|
|
5538
|
+
});
|
|
5539
|
+
}
|
|
5540
|
+
|
|
5541
|
+
function saveContentManual() {
|
|
5542
|
+
saveContent();
|
|
5543
|
+
}
|
|
5544
|
+
|
|
5545
|
+
// Keyboard Shortcuts: Tab (2 spaces), Shift+Tab, Ctrl+S
|
|
5546
|
+
editor.addEventListener('keydown', function(e) {
|
|
5547
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
5548
|
+
e.preventDefault();
|
|
5549
|
+
saveContent();
|
|
5550
|
+
return;
|
|
5551
|
+
}
|
|
5552
|
+
|
|
5553
|
+
if (e.key === 'Tab') {
|
|
5554
|
+
e.preventDefault();
|
|
5555
|
+
var start = this.selectionStart;
|
|
5556
|
+
var end = this.selectionEnd;
|
|
5557
|
+
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
|
|
5558
|
+
this.selectionStart = this.selectionEnd = start + 2;
|
|
5559
|
+
updateStatsAndLines();
|
|
5560
|
+
setSaveState('unsaved');
|
|
5561
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5562
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5563
|
+
}
|
|
5564
|
+
});
|
|
5565
|
+
|
|
5566
|
+
// Formatting Snippet Injector
|
|
5567
|
+
function insertFormat(type) {
|
|
5568
|
+
var start = editor.selectionStart;
|
|
5569
|
+
var end = editor.selectionEnd;
|
|
5570
|
+
var selected = editor.value.substring(start, end);
|
|
5571
|
+
var replacement = '';
|
|
5572
|
+
|
|
5573
|
+
switch (type) {
|
|
5574
|
+
case 'h1': replacement = '# ' + (selected || 'Heading 1'); break;
|
|
5575
|
+
case 'h2': replacement = '## ' + (selected || 'Heading 2'); break;
|
|
5576
|
+
case 'h3': replacement = '### ' + (selected || 'Heading 3'); break;
|
|
5577
|
+
case 'bold': replacement = '**' + (selected || 'bold text') + '**'; break;
|
|
5578
|
+
case 'italic': replacement = '*' + (selected || 'italic text') + '*'; break;
|
|
5579
|
+
case 'code': replacement = '\`' + (selected || 'inline code') + '\`'; break;
|
|
5580
|
+
case 'quote': replacement = '> ' + (selected || 'Quote text'); break;
|
|
5581
|
+
case 'table':
|
|
5582
|
+
replacement = '\\n| Column 1 | Column 2 | Column 3 |\\n| :--- | :---: | ---: |\\n| Data A | Data B | Data C |\\n| Data D | Data E | Data F |\\n';
|
|
5583
|
+
break;
|
|
5584
|
+
case 'list': replacement = '- ' + (selected || 'List item'); break;
|
|
5585
|
+
case 'task': replacement = '- [ ] ' + (selected || 'Task item'); break;
|
|
5586
|
+
case 'callout':
|
|
5587
|
+
replacement = '> [!NOTE]\\n> ' + (selected || 'This is an important callout note.');
|
|
5588
|
+
break;
|
|
5589
|
+
case 'math':
|
|
5590
|
+
replacement = '$$\\n' + (selected || '\\\\int_{-\\\\infty}^{\\\\infty} e^{-x^2} dx = \\\\sqrt{\\\\pi}') + '\\n$$';
|
|
5591
|
+
break;
|
|
5592
|
+
case 'columns':
|
|
5593
|
+
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:::';
|
|
5594
|
+
break;
|
|
5595
|
+
case 'footnote':
|
|
5596
|
+
replacement = (selected || 'Statement with footnote') + '[^1]\\n\\n[^1]: Note description text.';
|
|
5597
|
+
break;
|
|
5598
|
+
case 'mermaid':
|
|
5599
|
+
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';
|
|
5600
|
+
break;
|
|
5601
|
+
}
|
|
5602
|
+
|
|
5603
|
+
editor.value = editor.value.substring(0, start) + replacement + editor.value.substring(end);
|
|
5604
|
+
editor.selectionStart = editor.selectionEnd = start + replacement.length;
|
|
5605
|
+
editor.focus();
|
|
5606
|
+
updateStatsAndLines();
|
|
5607
|
+
setSaveState('unsaved');
|
|
5608
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5609
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5610
|
+
}
|
|
5611
|
+
|
|
5612
|
+
// View Mode Toggle (Split / Editor Only / Preview Only)
|
|
5613
|
+
function setViewMode(mode) {
|
|
5614
|
+
document.getElementById('btn-split').classList.remove('active');
|
|
5615
|
+
document.getElementById('btn-edit').classList.remove('active');
|
|
5616
|
+
document.getElementById('btn-prev').classList.remove('active');
|
|
5617
|
+
|
|
5618
|
+
if (mode === 'split') {
|
|
5619
|
+
document.getElementById('btn-split').classList.add('active');
|
|
5620
|
+
editorPane.style.display = 'flex';
|
|
5621
|
+
editorPane.style.width = '50%';
|
|
5622
|
+
previewPane.style.display = 'flex';
|
|
5623
|
+
previewPane.style.width = '50%';
|
|
5624
|
+
splitter.style.display = 'block';
|
|
5625
|
+
} else if (mode === 'edit') {
|
|
5626
|
+
document.getElementById('btn-edit').classList.add('active');
|
|
5627
|
+
editorPane.style.display = 'flex';
|
|
5628
|
+
editorPane.style.width = '100%';
|
|
5629
|
+
previewPane.style.display = 'none';
|
|
5630
|
+
splitter.style.display = 'none';
|
|
5631
|
+
} else if (mode === 'prev') {
|
|
5632
|
+
document.getElementById('btn-prev').classList.add('active');
|
|
5633
|
+
editorPane.style.display = 'none';
|
|
5634
|
+
previewPane.style.display = 'flex';
|
|
5635
|
+
previewPane.style.width = '100%';
|
|
5636
|
+
splitter.style.display = 'none';
|
|
5637
|
+
}
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5640
|
+
// Viewport Width Resizer
|
|
5641
|
+
function setViewport(width) {
|
|
5642
|
+
document.getElementById('vp-full').classList.remove('active');
|
|
5643
|
+
document.getElementById('vp-a4').classList.remove('active');
|
|
5644
|
+
document.getElementById('vp-mob').classList.remove('active');
|
|
5645
|
+
|
|
5646
|
+
if (width === '100%') {
|
|
5647
|
+
document.getElementById('vp-full').classList.add('active');
|
|
5648
|
+
previewFrame.style.maxWidth = '100%';
|
|
5649
|
+
} else if (width === '820px') {
|
|
5650
|
+
document.getElementById('vp-a4').classList.add('active');
|
|
5651
|
+
previewFrame.style.maxWidth = '820px';
|
|
5652
|
+
} else if (width === '440px') {
|
|
5653
|
+
document.getElementById('vp-mob').classList.add('active');
|
|
5654
|
+
previewFrame.style.maxWidth = '440px';
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
5657
|
+
|
|
5658
|
+
// Draggable Splitter Handle Logic
|
|
5659
|
+
var isDragging = false;
|
|
5660
|
+
splitter.addEventListener('mousedown', function(e) {
|
|
5661
|
+
isDragging = true;
|
|
5662
|
+
splitter.classList.add('active');
|
|
5663
|
+
document.body.style.cursor = 'col-resize';
|
|
5664
|
+
document.body.style.userSelect = 'none';
|
|
5665
|
+
});
|
|
5666
|
+
|
|
5667
|
+
window.addEventListener('mousemove', function(e) {
|
|
5668
|
+
if (!isDragging) return;
|
|
5669
|
+
var totalWidth = document.getElementById('workspace').clientWidth;
|
|
5670
|
+
var newEditorWidth = (e.clientX / totalWidth) * 100;
|
|
5671
|
+
if (newEditorWidth > 15 && newEditorWidth < 85) {
|
|
5672
|
+
editorPane.style.width = newEditorWidth + '%';
|
|
5673
|
+
previewPane.style.width = (100 - newEditorWidth) + '%';
|
|
5674
|
+
}
|
|
5675
|
+
});
|
|
5676
|
+
|
|
5677
|
+
window.addEventListener('mouseup', function() {
|
|
5678
|
+
if (isDragging) {
|
|
5679
|
+
isDragging = false;
|
|
5680
|
+
splitter.classList.remove('active');
|
|
5681
|
+
document.body.style.cursor = '';
|
|
5682
|
+
document.body.style.userSelect = '';
|
|
5683
|
+
}
|
|
5684
|
+
});
|
|
5685
|
+
|
|
5686
|
+
// Document Print Action
|
|
5687
|
+
function printDoc() {
|
|
5688
|
+
previewFrame.contentWindow.print();
|
|
5689
|
+
}
|
|
5690
|
+
|
|
5691
|
+
// Document Export Action
|
|
5692
|
+
function exportDoc(format) {
|
|
5693
|
+
saveContent(function() {
|
|
5694
|
+
window.location.href = '/api/export?format=' + format;
|
|
5695
|
+
});
|
|
5696
|
+
}
|
|
5697
|
+
|
|
5698
|
+
// Initialize line stats
|
|
5699
|
+
updateStatsAndLines();
|
|
5700
|
+
</script>
|
|
5701
|
+
</body>
|
|
5702
|
+
</html>`;
|
|
5703
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5704
|
+
res.end(appHtml);
|
|
5705
|
+
return;
|
|
5706
|
+
}
|
|
5707
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
5708
|
+
res.end("Not Found");
|
|
5709
|
+
});
|
|
5710
|
+
return new Promise((resolve6, reject) => {
|
|
5711
|
+
server.listen(port, () => {
|
|
5712
|
+
const url = `http://localhost:${port}`;
|
|
5713
|
+
resolve6({
|
|
5714
|
+
server,
|
|
5715
|
+
port,
|
|
5716
|
+
url,
|
|
5717
|
+
close: async () => {
|
|
5718
|
+
watcher.close();
|
|
5719
|
+
sseClients.forEach((client) => {
|
|
5720
|
+
try {
|
|
5721
|
+
client.end();
|
|
5722
|
+
} catch {
|
|
5723
|
+
}
|
|
5724
|
+
});
|
|
5725
|
+
sseClients.clear();
|
|
5726
|
+
return new Promise((res) => {
|
|
5727
|
+
server.close(() => res());
|
|
5728
|
+
});
|
|
5729
|
+
}
|
|
5730
|
+
});
|
|
5731
|
+
});
|
|
5732
|
+
server.on("error", (err) => {
|
|
5733
|
+
reject(err);
|
|
5734
|
+
});
|
|
5735
|
+
});
|
|
5736
|
+
}
|
|
5737
|
+
function escapeHtml2(str) {
|
|
5738
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5739
|
+
}
|
|
5740
|
+
|
|
3104
5741
|
// src/config/defineConfig.ts
|
|
3105
5742
|
function defineConfig(config) {
|
|
3106
5743
|
return config;
|
|
3107
5744
|
}
|
|
3108
5745
|
|
|
3109
5746
|
// src/version.ts
|
|
3110
|
-
import * as
|
|
3111
|
-
import * as
|
|
5747
|
+
import * as fs8 from "fs";
|
|
5748
|
+
import * as path8 from "path";
|
|
3112
5749
|
import { fileURLToPath } from "url";
|
|
3113
5750
|
try {
|
|
3114
5751
|
if (typeof globalThis !== "undefined" && (!globalThis.localStorage || typeof globalThis.localStorage.getItem !== "function")) {
|
|
@@ -3130,21 +5767,21 @@ try {
|
|
|
3130
5767
|
}
|
|
3131
5768
|
} catch {
|
|
3132
5769
|
}
|
|
3133
|
-
var FALLBACK_VERSION = "0.
|
|
5770
|
+
var FALLBACK_VERSION = "0.4.0";
|
|
3134
5771
|
function readVersionFromPackageJson(fromDir) {
|
|
3135
5772
|
let currentDir = fromDir;
|
|
3136
5773
|
for (let i = 0; i < 6; i++) {
|
|
3137
5774
|
try {
|
|
3138
|
-
const pkgJsonPath =
|
|
3139
|
-
if (
|
|
3140
|
-
const pkg = JSON.parse(
|
|
5775
|
+
const pkgJsonPath = path8.join(currentDir, "package.json");
|
|
5776
|
+
if (fs8.existsSync(pkgJsonPath)) {
|
|
5777
|
+
const pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
|
|
3141
5778
|
if (pkg.name === "@masumdev/markforge" && pkg.version) {
|
|
3142
5779
|
return pkg.version;
|
|
3143
5780
|
}
|
|
3144
5781
|
}
|
|
3145
5782
|
} catch {
|
|
3146
5783
|
}
|
|
3147
|
-
const parentDir =
|
|
5784
|
+
const parentDir = path8.dirname(currentDir);
|
|
3148
5785
|
if (parentDir === currentDir) break;
|
|
3149
5786
|
currentDir = parentDir;
|
|
3150
5787
|
}
|
|
@@ -3155,7 +5792,7 @@ function getPackageDir() {
|
|
|
3155
5792
|
return __dirname;
|
|
3156
5793
|
}
|
|
3157
5794
|
try {
|
|
3158
|
-
return
|
|
5795
|
+
return path8.dirname(fileURLToPath(import.meta.url));
|
|
3159
5796
|
} catch {
|
|
3160
5797
|
return process.cwd();
|
|
3161
5798
|
}
|
|
@@ -3166,6 +5803,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3166
5803
|
}
|
|
3167
5804
|
export {
|
|
3168
5805
|
DEFAULT_CONFIG,
|
|
5806
|
+
KATEX_INLINE_CSS,
|
|
3169
5807
|
MARKFORGE_VERSION,
|
|
3170
5808
|
Orientation,
|
|
3171
5809
|
OutputFormat,
|
|
@@ -3194,17 +5832,28 @@ export {
|
|
|
3194
5832
|
inlineHtmlImages,
|
|
3195
5833
|
loadConfig,
|
|
3196
5834
|
compileMarkdown as markforge,
|
|
5835
|
+
normalizeBackCover,
|
|
5836
|
+
normalizeCoverPage,
|
|
3197
5837
|
normalizeHeaderFooter,
|
|
3198
5838
|
normalizeHeaderFooterSlot,
|
|
5839
|
+
normalizeNumberHeadings,
|
|
5840
|
+
normalizeSecurity,
|
|
5841
|
+
normalizeSignatures,
|
|
3199
5842
|
normalizeWatermark,
|
|
3200
5843
|
parseInlineSpans,
|
|
3201
5844
|
parseMarginToTwip2 as parseMarginToTwip,
|
|
5845
|
+
parseMarkdownDocument as parseMarkdown,
|
|
3202
5846
|
parseMarkdownDocument,
|
|
5847
|
+
renderBackCoverHtml,
|
|
5848
|
+
renderCoverPageHtml,
|
|
3203
5849
|
renderInlinesToHtml,
|
|
5850
|
+
renderMathToHtml,
|
|
3204
5851
|
renderMermaidToPng,
|
|
5852
|
+
renderNodesToHtml,
|
|
3205
5853
|
replaceDocumentTokens,
|
|
3206
5854
|
resolveDocumentConfig,
|
|
3207
5855
|
resolveImage,
|
|
3208
5856
|
slugify,
|
|
5857
|
+
startPreviewServer,
|
|
3209
5858
|
tokenizeCodeLine
|
|
3210
5859
|
};
|