@masumdev/markforge 0.3.0 → 0.4.1
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 +167 -140
- package/dist/App-TPDCT2VL.mjs +393 -0
- package/dist/{chunk-IYGPJIK5.mjs → chunk-BPHDY6VB.mjs} +6 -0
- package/dist/{App-AM2CWXHJ.mjs → chunk-R6DT335C.mjs} +2140 -924
- package/dist/{chunk-HKB4CSPZ.mjs → chunk-UNWAEVDU.mjs} +4 -3
- package/dist/cli.mjs +38 -8
- package/dist/index.d.mts +554 -136
- package/dist/index.d.ts +554 -136
- package/dist/index.js +2652 -227
- package/dist/index.mjs +2643 -228
- package/dist/{loadConfig-PD6ENMAM.mjs → loadConfig-PGJKPE6G.mjs} +1 -1
- package/dist/previewServer-4EELOUAV.mjs +893 -0
- package/package.json +5 -1
- package/schema.json +142 -0
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
39
39
|
var src_exports = {};
|
|
40
40
|
__export(src_exports, {
|
|
41
41
|
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
42
|
+
KATEX_INLINE_CSS: () => KATEX_INLINE_CSS,
|
|
42
43
|
MARKFORGE_VERSION: () => MARKFORGE_VERSION,
|
|
43
44
|
Orientation: () => Orientation,
|
|
44
45
|
OutputFormat: () => OutputFormat,
|
|
@@ -51,6 +52,7 @@ __export(src_exports, {
|
|
|
51
52
|
THEME_DEFAULT: () => THEME_DEFAULT,
|
|
52
53
|
Theme: () => Theme,
|
|
53
54
|
WatermarkPosition: () => WatermarkPosition,
|
|
55
|
+
applyHeadingNumbering: () => applyHeadingNumbering,
|
|
54
56
|
buildDocxDocument: () => buildDocxDocument,
|
|
55
57
|
buildHtmlDocument: () => buildHtmlDocument,
|
|
56
58
|
buildPdfDocument: () => buildPdfDocument,
|
|
@@ -67,19 +69,29 @@ __export(src_exports, {
|
|
|
67
69
|
inlineHtmlImages: () => inlineHtmlImages,
|
|
68
70
|
loadConfig: () => loadConfig,
|
|
69
71
|
markforge: () => compileMarkdown,
|
|
72
|
+
normalizeBackCover: () => normalizeBackCover,
|
|
73
|
+
normalizeCoverPage: () => normalizeCoverPage,
|
|
70
74
|
normalizeHeaderFooter: () => normalizeHeaderFooter,
|
|
71
75
|
normalizeHeaderFooterSlot: () => normalizeHeaderFooterSlot,
|
|
76
|
+
normalizeNumberHeadings: () => normalizeNumberHeadings,
|
|
77
|
+
normalizeSecurity: () => normalizeSecurity,
|
|
72
78
|
normalizeSignatures: () => normalizeSignatures,
|
|
73
79
|
normalizeWatermark: () => normalizeWatermark,
|
|
74
80
|
parseInlineSpans: () => parseInlineSpans,
|
|
75
81
|
parseMarginToTwip: () => parseMarginToTwip2,
|
|
82
|
+
parseMarkdown: () => parseMarkdownDocument,
|
|
76
83
|
parseMarkdownDocument: () => parseMarkdownDocument,
|
|
84
|
+
renderBackCoverHtml: () => renderBackCoverHtml,
|
|
85
|
+
renderCoverPageHtml: () => renderCoverPageHtml,
|
|
77
86
|
renderInlinesToHtml: () => renderInlinesToHtml,
|
|
87
|
+
renderMathToHtml: () => renderMathToHtml,
|
|
78
88
|
renderMermaidToPng: () => renderMermaidToPng,
|
|
89
|
+
renderNodesToHtml: () => renderNodesToHtml,
|
|
79
90
|
replaceDocumentTokens: () => replaceDocumentTokens,
|
|
80
91
|
resolveDocumentConfig: () => resolveDocumentConfig,
|
|
81
92
|
resolveImage: () => resolveImage,
|
|
82
93
|
slugify: () => slugify,
|
|
94
|
+
startPreviewServer: () => startPreviewServer,
|
|
83
95
|
tokenizeCodeLine: () => tokenizeCodeLine
|
|
84
96
|
});
|
|
85
97
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -120,6 +132,26 @@ function parseInlineSpans(text) {
|
|
|
120
132
|
remaining = remaining.slice(imgMatch[0].length);
|
|
121
133
|
continue;
|
|
122
134
|
}
|
|
135
|
+
const fnMatch = remaining.match(/^\[\^([\w-]+)\]/);
|
|
136
|
+
if (fnMatch) {
|
|
137
|
+
const fnId = fnMatch[1];
|
|
138
|
+
spans.push({
|
|
139
|
+
type: "footnoteRef",
|
|
140
|
+
content: fnId,
|
|
141
|
+
footnoteId: fnId
|
|
142
|
+
});
|
|
143
|
+
remaining = remaining.slice(fnMatch[0].length);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
const mathMatch = remaining.match(/^\$([^$\n]+?)\$/);
|
|
147
|
+
if (mathMatch && !mathMatch[1].startsWith("$")) {
|
|
148
|
+
spans.push({
|
|
149
|
+
type: "mathInline",
|
|
150
|
+
content: mathMatch[1]
|
|
151
|
+
});
|
|
152
|
+
remaining = remaining.slice(mathMatch[0].length);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
123
155
|
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/);
|
|
124
156
|
if (linkMatch) {
|
|
125
157
|
spans.push({
|
|
@@ -219,7 +251,7 @@ function parseInlineSpans(text) {
|
|
|
219
251
|
remaining = remaining.slice(fullTag.length);
|
|
220
252
|
continue;
|
|
221
253
|
}
|
|
222
|
-
const nextSpecial = remaining.search(/[\*\_\[
|
|
254
|
+
const nextSpecial = remaining.search(/[\*\_\[\!`~<\$]/);
|
|
223
255
|
if (nextSpecial === -1) {
|
|
224
256
|
spans.push({
|
|
225
257
|
type: "text",
|
|
@@ -240,11 +272,51 @@ function parseInlineSpans(text) {
|
|
|
240
272
|
remaining = remaining.slice(nextSpecial);
|
|
241
273
|
}
|
|
242
274
|
}
|
|
243
|
-
|
|
275
|
+
const merged = [];
|
|
276
|
+
for (const s of spans) {
|
|
277
|
+
const prev = merged[merged.length - 1];
|
|
278
|
+
if (prev && prev.type === "text" && s.type === "text") {
|
|
279
|
+
prev.content += s.content;
|
|
280
|
+
} else {
|
|
281
|
+
merged.push(s);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return merged;
|
|
244
285
|
}
|
|
245
286
|
function slugify(text) {
|
|
246
287
|
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
247
288
|
}
|
|
289
|
+
function applyHeadingNumbering(nodes, tocEntries, options) {
|
|
290
|
+
const depth = options.depth ?? 3;
|
|
291
|
+
const skipH1 = options.skipH1 ?? false;
|
|
292
|
+
const prefix = options.prefix ?? "";
|
|
293
|
+
const counters = [0, 0, 0, 0, 0, 0];
|
|
294
|
+
for (const node of nodes) {
|
|
295
|
+
if (node.type === "heading" && node.level) {
|
|
296
|
+
if (node._numbered) continue;
|
|
297
|
+
const lvl = node.level;
|
|
298
|
+
if (lvl > depth) continue;
|
|
299
|
+
if (lvl === 1 && skipH1) continue;
|
|
300
|
+
const idx = lvl - 1;
|
|
301
|
+
counters[idx]++;
|
|
302
|
+
for (let c = idx + 1; c < counters.length; c++) {
|
|
303
|
+
counters[c] = 0;
|
|
304
|
+
}
|
|
305
|
+
const startIdx = skipH1 ? 1 : 0;
|
|
306
|
+
const parts = counters.slice(startIdx, idx + 1).filter((n) => n > 0);
|
|
307
|
+
const numberStr = parts.join(".") + ".";
|
|
308
|
+
const fullPrefix = prefix ? `${prefix} ${numberStr} ` : `${numberStr} `;
|
|
309
|
+
const originalText = node.text || "";
|
|
310
|
+
node.text = fullPrefix + originalText;
|
|
311
|
+
node.inlines = parseInlineSpans(node.text);
|
|
312
|
+
node._numbered = true;
|
|
313
|
+
const toc = tocEntries.find((t) => t.id === node.id);
|
|
314
|
+
if (toc) {
|
|
315
|
+
toc.text = node.text;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
248
320
|
function parseMarkdownDocument(rawMarkdown) {
|
|
249
321
|
const { data: frontmatter, content } = (0, import_gray_matter.default)(rawMarkdown);
|
|
250
322
|
const metadata = frontmatter || {};
|
|
@@ -256,6 +328,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
256
328
|
const lines = cleanContent.split(/\r?\n/);
|
|
257
329
|
const nodes = [];
|
|
258
330
|
const tocEntries = [];
|
|
331
|
+
const footnoteDefs = [];
|
|
259
332
|
let i = 0;
|
|
260
333
|
while (i < lines.length) {
|
|
261
334
|
const line = lines[i];
|
|
@@ -284,6 +357,29 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
284
357
|
i++;
|
|
285
358
|
continue;
|
|
286
359
|
}
|
|
360
|
+
if (line.trim().startsWith("$$")) {
|
|
361
|
+
const mathLines = [];
|
|
362
|
+
const singleLine = line.trim().match(/^\$\$(.+)\$\$$/);
|
|
363
|
+
if (singleLine) {
|
|
364
|
+
nodes.push({
|
|
365
|
+
type: "mathBlock",
|
|
366
|
+
text: singleLine[1].trim()
|
|
367
|
+
});
|
|
368
|
+
i++;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
i++;
|
|
372
|
+
while (i < lines.length && !lines[i].trim().startsWith("$$")) {
|
|
373
|
+
mathLines.push(lines[i]);
|
|
374
|
+
i++;
|
|
375
|
+
}
|
|
376
|
+
if (i < lines.length) i++;
|
|
377
|
+
nodes.push({
|
|
378
|
+
type: "mathBlock",
|
|
379
|
+
text: mathLines.join("\n").trim()
|
|
380
|
+
});
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
287
383
|
const codeBlockMatch = line.match(/^```(\w+)?/);
|
|
288
384
|
if (codeBlockMatch) {
|
|
289
385
|
const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
|
|
@@ -310,6 +406,100 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
310
406
|
}
|
|
311
407
|
continue;
|
|
312
408
|
}
|
|
409
|
+
const colsMatch = line.trim().match(/^:::columns(?:\s+\[?([\w\s=.-]+)\]?)?$/i);
|
|
410
|
+
if (colsMatch) {
|
|
411
|
+
const attrStr = colsMatch[1] || "";
|
|
412
|
+
let colsCount = 2;
|
|
413
|
+
let colGap = "1.5rem";
|
|
414
|
+
if (attrStr) {
|
|
415
|
+
const numMatch = attrStr.trim().match(/^(\d+)$/);
|
|
416
|
+
const cMatch = attrStr.match(/cols=(\d+)/i) || attrStr.match(/columns=(\d+)/i);
|
|
417
|
+
const gMatch = attrStr.match(/gap=([^\s]+)/i);
|
|
418
|
+
if (numMatch) colsCount = parseInt(numMatch[1], 10);
|
|
419
|
+
else if (cMatch) colsCount = parseInt(cMatch[1], 10);
|
|
420
|
+
if (gMatch) colGap = gMatch[1];
|
|
421
|
+
}
|
|
422
|
+
const columnNodes = [];
|
|
423
|
+
let currentColumnLines = [];
|
|
424
|
+
let inColBlock = false;
|
|
425
|
+
i++;
|
|
426
|
+
while (i < lines.length) {
|
|
427
|
+
const curLine = lines[i];
|
|
428
|
+
const trimmed = curLine.trim();
|
|
429
|
+
if (/^:::col(?:umn)?$/i.test(trimmed)) {
|
|
430
|
+
if (currentColumnLines.length > 0) {
|
|
431
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
432
|
+
columnNodes.push({
|
|
433
|
+
type: "column",
|
|
434
|
+
children: subDoc.nodes
|
|
435
|
+
});
|
|
436
|
+
currentColumnLines = [];
|
|
437
|
+
}
|
|
438
|
+
inColBlock = true;
|
|
439
|
+
i++;
|
|
440
|
+
} else if (trimmed === ":::") {
|
|
441
|
+
if (inColBlock) {
|
|
442
|
+
if (currentColumnLines.length > 0) {
|
|
443
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
444
|
+
columnNodes.push({
|
|
445
|
+
type: "column",
|
|
446
|
+
children: subDoc.nodes
|
|
447
|
+
});
|
|
448
|
+
currentColumnLines = [];
|
|
449
|
+
}
|
|
450
|
+
inColBlock = false;
|
|
451
|
+
i++;
|
|
452
|
+
} else {
|
|
453
|
+
if (currentColumnLines.length > 0) {
|
|
454
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
455
|
+
columnNodes.push({
|
|
456
|
+
type: "column",
|
|
457
|
+
children: subDoc.nodes
|
|
458
|
+
});
|
|
459
|
+
currentColumnLines = [];
|
|
460
|
+
}
|
|
461
|
+
i++;
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
} else {
|
|
465
|
+
currentColumnLines.push(curLine);
|
|
466
|
+
i++;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
if (currentColumnLines.length > 0) {
|
|
470
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
471
|
+
columnNodes.push({
|
|
472
|
+
type: "column",
|
|
473
|
+
children: subDoc.nodes
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
nodes.push({
|
|
477
|
+
type: "columns",
|
|
478
|
+
columnsCount: columnNodes.length > 0 ? columnNodes.length : colsCount,
|
|
479
|
+
columnGap: colGap,
|
|
480
|
+
children: columnNodes
|
|
481
|
+
});
|
|
482
|
+
continue;
|
|
483
|
+
}
|
|
484
|
+
const fnDefMatch = line.match(/^\[\^([\w-]+)\]:\s+(.+)$/);
|
|
485
|
+
if (fnDefMatch) {
|
|
486
|
+
const fnId = fnDefMatch[1];
|
|
487
|
+
const fnText = fnDefMatch[2].trim();
|
|
488
|
+
const inlines = parseInlineSpans(fnText);
|
|
489
|
+
footnoteDefs.push({
|
|
490
|
+
id: fnId,
|
|
491
|
+
text: fnText,
|
|
492
|
+
inlines
|
|
493
|
+
});
|
|
494
|
+
nodes.push({
|
|
495
|
+
type: "footnoteDef",
|
|
496
|
+
footnoteId: fnId,
|
|
497
|
+
text: fnText,
|
|
498
|
+
inlines
|
|
499
|
+
});
|
|
500
|
+
i++;
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
313
503
|
const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
|
|
314
504
|
if (calloutMatch) {
|
|
315
505
|
const calloutType = calloutMatch[1].toUpperCase();
|
|
@@ -433,7 +623,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
433
623
|
continue;
|
|
434
624
|
}
|
|
435
625
|
const paraLines = [];
|
|
436
|
-
while (i < lines.length && lines[i].trim() && !lines[i].match(/^#{1,6}\s+/) && !lines[i].startsWith("```") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !lines[i].match(/^(\s*)([-*+]|\d+\.)\s+/)) {
|
|
626
|
+
while (i < lines.length && lines[i].trim() && !lines[i].match(/^#{1,6}\s+/) && !lines[i].startsWith("```") && !lines[i].startsWith("$$") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !lines[i].match(/^:::columns/) && !lines[i].match(/^\[\^[\w-]+\]:\s+/) && !lines[i].match(/^(\s*)([-*+]|\d+\.)\s+/)) {
|
|
437
627
|
paraLines.push(lines[i]);
|
|
438
628
|
i++;
|
|
439
629
|
}
|
|
@@ -444,12 +634,19 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
444
634
|
inlines: parseInlineSpans(paraText)
|
|
445
635
|
});
|
|
446
636
|
}
|
|
637
|
+
if (metadata.numberHeadings) {
|
|
638
|
+
const opts = typeof metadata.numberHeadings === "boolean" ? { enabled: metadata.numberHeadings } : metadata.numberHeadings;
|
|
639
|
+
if (opts.enabled !== false) {
|
|
640
|
+
applyHeadingNumbering(nodes, tocEntries, opts);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
447
643
|
return {
|
|
448
644
|
metadata,
|
|
449
645
|
content: cleanContent,
|
|
450
646
|
nodes,
|
|
451
647
|
tocEntries,
|
|
452
|
-
inlinedStyles
|
|
648
|
+
inlinedStyles,
|
|
649
|
+
footnoteDefs
|
|
453
650
|
};
|
|
454
651
|
}
|
|
455
652
|
|
|
@@ -515,9 +712,14 @@ async function resolveImage(src, baseDir = process.cwd()) {
|
|
|
515
712
|
memoryImageCache.set(cacheKey, resolved2);
|
|
516
713
|
return resolved2;
|
|
517
714
|
}
|
|
518
|
-
|
|
715
|
+
let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
|
|
519
716
|
if (!fs.existsSync(localPath)) {
|
|
520
|
-
|
|
717
|
+
const cwdPath = path.resolve(process.cwd(), src);
|
|
718
|
+
if (fs.existsSync(cwdPath)) {
|
|
719
|
+
localPath = cwdPath;
|
|
720
|
+
} else {
|
|
721
|
+
return null;
|
|
722
|
+
}
|
|
521
723
|
}
|
|
522
724
|
const buffer = fs.readFileSync(localPath);
|
|
523
725
|
const mimeType = getMimeType(localPath);
|
|
@@ -922,6 +1124,8 @@ var path4 = __toESM(require("path"));
|
|
|
922
1124
|
var os = __toESM(require("os"));
|
|
923
1125
|
var import_node_url2 = require("url");
|
|
924
1126
|
var import_node_child_process = require("child_process");
|
|
1127
|
+
var import_pdf_lib = require("pdf-lib");
|
|
1128
|
+
var import_pdf_encrypt = require("@pdfsmaller/pdf-encrypt");
|
|
925
1129
|
|
|
926
1130
|
// src/core/html/htmlBuilder.ts
|
|
927
1131
|
var fs3 = __toESM(require("fs"));
|
|
@@ -993,7 +1197,7 @@ var THEME_COMPONENTS = `
|
|
|
993
1197
|
.document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
|
|
994
1198
|
|
|
995
1199
|
/* Table of Contents */
|
|
996
|
-
.table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; }
|
|
1200
|
+
.table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; page-break-after: always; break-after: page; }
|
|
997
1201
|
.table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
|
|
998
1202
|
.table-of-contents ul { list-style: none; padding: 0; margin: 0; }
|
|
999
1203
|
.table-of-contents li { padding: 0.25rem 0; }
|
|
@@ -1066,11 +1270,13 @@ var THEME_CORPORATE = `
|
|
|
1066
1270
|
}
|
|
1067
1271
|
body { background-color: var(--mf-bg); color: var(--mf-text); font-family: var(--mf-font-family); font-size: 15px; line-height: 1.65; margin: 0; padding: 2.5rem; }
|
|
1068
1272
|
.document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
|
|
1069
|
-
h1, h2, h3, h4, h5, h6 { color: var(--mf-
|
|
1070
|
-
h1 { font-size: 2.2rem; border-bottom:
|
|
1273
|
+
h1, h2, h3, h4, h5, h6 { color: var(--mf-primary-dark); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
|
|
1274
|
+
h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
|
|
1071
1275
|
h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
|
|
1072
|
-
h3 { font-size: 1.3rem; }
|
|
1073
|
-
h4 { font-size: 1.1rem; }
|
|
1276
|
+
h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
|
|
1277
|
+
h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
|
|
1278
|
+
h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
|
|
1279
|
+
h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
|
|
1074
1280
|
p { margin: 0.8rem 0; }
|
|
1075
1281
|
`;
|
|
1076
1282
|
var THEME_DEFAULT = THEME_CORPORATE;
|
|
@@ -1112,11 +1318,13 @@ function generateThemeCss(theme) {
|
|
|
1112
1318
|
}
|
|
1113
1319
|
body { background-color: var(--mf-bg); color: var(--mf-text); font-family: var(--mf-font-family); font-size: 15px; line-height: 1.65; margin: 0; padding: 2.5rem; }
|
|
1114
1320
|
.document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
|
|
1115
|
-
h1, h2, h3, h4, h5, h6 { color: var(--mf-
|
|
1116
|
-
h1 { font-size: 2.2rem; border-bottom:
|
|
1321
|
+
h1, h2, h3, h4, h5, h6 { color: var(--mf-primary-dark); font-weight: 700; margin-top: 1.8rem; margin-bottom: 0.8rem; line-height: 1.25; }
|
|
1322
|
+
h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
|
|
1117
1323
|
h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
|
|
1118
|
-
h3 { font-size: 1.3rem; }
|
|
1119
|
-
h4 { font-size: 1.1rem; }
|
|
1324
|
+
h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
|
|
1325
|
+
h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
|
|
1326
|
+
h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
|
|
1327
|
+
h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
|
|
1120
1328
|
p { margin: 0.8rem 0; }
|
|
1121
1329
|
${theme.customCss || ""}
|
|
1122
1330
|
`;
|
|
@@ -1164,6 +1372,12 @@ var DEFAULT_CONFIG = {
|
|
|
1164
1372
|
},
|
|
1165
1373
|
toc: false,
|
|
1166
1374
|
watermark: void 0,
|
|
1375
|
+
signatures: void 0,
|
|
1376
|
+
coverPage: void 0,
|
|
1377
|
+
backCover: void 0,
|
|
1378
|
+
numberHeadings: void 0,
|
|
1379
|
+
security: void 0,
|
|
1380
|
+
math: true,
|
|
1167
1381
|
embedImages: true,
|
|
1168
1382
|
metadata: void 0,
|
|
1169
1383
|
watch: false,
|
|
@@ -1319,15 +1533,45 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
|
|
|
1319
1533
|
if (/^[0-9.]+$/.test(str)) return `${str}pt`;
|
|
1320
1534
|
return str;
|
|
1321
1535
|
}
|
|
1322
|
-
function replaceDocumentTokens(template = "", meta) {
|
|
1323
|
-
|
|
1536
|
+
function replaceDocumentTokens(template = "", meta = {}) {
|
|
1537
|
+
if (!template) return "";
|
|
1538
|
+
const currentYear = meta.year ? String(meta.year) : (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1539
|
+
const tokenMap = {
|
|
1540
|
+
title: meta.title ? String(meta.title) : "",
|
|
1541
|
+
subtitle: meta.subtitle ? String(meta.subtitle) : "",
|
|
1542
|
+
author: meta.author ? Array.isArray(meta.author) ? meta.author.join(", ") : String(meta.author) : "",
|
|
1543
|
+
version: meta.version ? String(meta.version) : "",
|
|
1544
|
+
date: meta.date ? String(meta.date) : "",
|
|
1545
|
+
company: meta.company ? String(meta.company) : "",
|
|
1546
|
+
year: currentYear
|
|
1547
|
+
};
|
|
1548
|
+
if (meta.metadata && typeof meta.metadata === "object") {
|
|
1549
|
+
for (const [key, val] of Object.entries(meta.metadata)) {
|
|
1550
|
+
if (val !== void 0 && val !== null) {
|
|
1551
|
+
tokenMap[key.toLowerCase()] = String(val);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
for (const [key, val] of Object.entries(meta)) {
|
|
1556
|
+
if (val !== void 0 && val !== null && typeof val !== "object") {
|
|
1557
|
+
tokenMap[key.toLowerCase()] = String(val);
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
return template.replace(/\{([a-zA-Z0-9_\-]+)\}/gi, (match, tokenKey) => {
|
|
1561
|
+
const lowerKey = tokenKey.toLowerCase();
|
|
1562
|
+
if (lowerKey in tokenMap) {
|
|
1563
|
+
return tokenMap[lowerKey];
|
|
1564
|
+
}
|
|
1565
|
+
return match;
|
|
1566
|
+
});
|
|
1324
1567
|
}
|
|
1325
|
-
function normalizeWatermark(rawWatermark) {
|
|
1568
|
+
function normalizeWatermark(rawWatermark, tokens) {
|
|
1326
1569
|
if (!rawWatermark) {
|
|
1327
1570
|
return void 0;
|
|
1328
1571
|
}
|
|
1329
1572
|
if (typeof rawWatermark === "string") {
|
|
1330
|
-
|
|
1573
|
+
let text = rawWatermark.trim();
|
|
1574
|
+
if (tokens) text = replaceDocumentTokens(text, tokens);
|
|
1331
1575
|
if (!text) return void 0;
|
|
1332
1576
|
return {
|
|
1333
1577
|
text,
|
|
@@ -1340,8 +1584,10 @@ function normalizeWatermark(rawWatermark) {
|
|
|
1340
1584
|
}
|
|
1341
1585
|
if (typeof rawWatermark === "object") {
|
|
1342
1586
|
if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
|
|
1587
|
+
let text = rawWatermark.text.trim();
|
|
1588
|
+
if (tokens) text = replaceDocumentTokens(text, tokens);
|
|
1343
1589
|
return {
|
|
1344
|
-
text
|
|
1590
|
+
text,
|
|
1345
1591
|
color: rawWatermark.color || "#94a3b8",
|
|
1346
1592
|
opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
|
|
1347
1593
|
fontSize: rawWatermark.fontSize || 54,
|
|
@@ -1457,6 +1703,174 @@ function normalizeSignatures(raw, meta = {}) {
|
|
|
1457
1703
|
spacingBeforeTwip
|
|
1458
1704
|
};
|
|
1459
1705
|
}
|
|
1706
|
+
function normalizeCoverPage(rawCover, tokenCtx = {}) {
|
|
1707
|
+
if (!rawCover) return void 0;
|
|
1708
|
+
const cfg = typeof rawCover === "object" ? rawCover : {};
|
|
1709
|
+
if (cfg.enabled === false) return void 0;
|
|
1710
|
+
const preset = cfg.preset || "modern";
|
|
1711
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title ? String(tokenCtx.title) : "Document Title";
|
|
1712
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle ? String(tokenCtx.subtitle) : void 0;
|
|
1713
|
+
const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
|
|
1714
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
|
|
1715
|
+
const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
|
|
1716
|
+
let dateStr;
|
|
1717
|
+
if (typeof cfg.date === "string") {
|
|
1718
|
+
dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
|
|
1719
|
+
} else if (cfg.date === true) {
|
|
1720
|
+
dateStr = tokenCtx.date ? String(tokenCtx.date) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1721
|
+
} else {
|
|
1722
|
+
dateStr = tokenCtx.date ? String(tokenCtx.date) : void 0;
|
|
1723
|
+
}
|
|
1724
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1725
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1726
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1727
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1728
|
+
const logoWidth = cfg.logoWidth;
|
|
1729
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1730
|
+
const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1731
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1732
|
+
const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
|
|
1733
|
+
const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
|
|
1734
|
+
const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
|
|
1735
|
+
const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
|
|
1736
|
+
const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
|
|
1737
|
+
const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
|
|
1738
|
+
const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
|
|
1739
|
+
const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
|
|
1740
|
+
let socialMap;
|
|
1741
|
+
if (cfg.social && typeof cfg.social === "object") {
|
|
1742
|
+
socialMap = {};
|
|
1743
|
+
for (const [k, v] of Object.entries(cfg.social)) {
|
|
1744
|
+
if (typeof v === "string") {
|
|
1745
|
+
socialMap[k] = replaceDocumentTokens(v, tokenCtx);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1750
|
+
const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : void 0;
|
|
1751
|
+
return {
|
|
1752
|
+
enabled: true,
|
|
1753
|
+
preset,
|
|
1754
|
+
title,
|
|
1755
|
+
subtitle,
|
|
1756
|
+
author,
|
|
1757
|
+
company,
|
|
1758
|
+
version,
|
|
1759
|
+
date: dateStr,
|
|
1760
|
+
badge,
|
|
1761
|
+
badgeColor,
|
|
1762
|
+
badgeTextColor,
|
|
1763
|
+
logo,
|
|
1764
|
+
logoWidth,
|
|
1765
|
+
bgGradient,
|
|
1766
|
+
backgroundColor,
|
|
1767
|
+
textColor,
|
|
1768
|
+
titleColor,
|
|
1769
|
+
subtitleColor,
|
|
1770
|
+
accentColor,
|
|
1771
|
+
footerText,
|
|
1772
|
+
address,
|
|
1773
|
+
email,
|
|
1774
|
+
phone,
|
|
1775
|
+
website,
|
|
1776
|
+
social: socialMap,
|
|
1777
|
+
copyright
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
function normalizeBackCover(rawBack, tokenCtx = {}) {
|
|
1781
|
+
if (!rawBack) return void 0;
|
|
1782
|
+
const cfg = typeof rawBack === "object" ? rawBack : {};
|
|
1783
|
+
if (cfg.enabled === false) return void 0;
|
|
1784
|
+
const preset = cfg.preset || "modern";
|
|
1785
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
|
|
1786
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
|
|
1787
|
+
const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
|
|
1788
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
|
|
1789
|
+
const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
|
|
1790
|
+
const date = typeof cfg.date === "string" ? replaceDocumentTokens(cfg.date, tokenCtx) : tokenCtx.date ? String(tokenCtx.date) : void 0;
|
|
1791
|
+
const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
|
|
1792
|
+
const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
|
|
1793
|
+
const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
|
|
1794
|
+
const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
|
|
1795
|
+
let socialMap;
|
|
1796
|
+
if (cfg.social && typeof cfg.social === "object") {
|
|
1797
|
+
socialMap = {};
|
|
1798
|
+
for (const [k, v] of Object.entries(cfg.social)) {
|
|
1799
|
+
if (typeof v === "string") {
|
|
1800
|
+
socialMap[k] = replaceDocumentTokens(v, tokenCtx);
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1805
|
+
const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
|
|
1806
|
+
const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
|
|
1807
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1808
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1809
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1810
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1811
|
+
const logoWidth = cfg.logoWidth;
|
|
1812
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1813
|
+
const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1814
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1815
|
+
const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
|
|
1816
|
+
const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
|
|
1817
|
+
const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
|
|
1818
|
+
return {
|
|
1819
|
+
enabled: true,
|
|
1820
|
+
preset,
|
|
1821
|
+
title,
|
|
1822
|
+
subtitle,
|
|
1823
|
+
author,
|
|
1824
|
+
company,
|
|
1825
|
+
version,
|
|
1826
|
+
date,
|
|
1827
|
+
address,
|
|
1828
|
+
email,
|
|
1829
|
+
phone,
|
|
1830
|
+
website,
|
|
1831
|
+
social: socialMap,
|
|
1832
|
+
copyright,
|
|
1833
|
+
footerText,
|
|
1834
|
+
badge,
|
|
1835
|
+
badgeColor,
|
|
1836
|
+
badgeTextColor,
|
|
1837
|
+
logo,
|
|
1838
|
+
logoWidth,
|
|
1839
|
+
bgGradient,
|
|
1840
|
+
backgroundColor,
|
|
1841
|
+
textColor,
|
|
1842
|
+
titleColor,
|
|
1843
|
+
subtitleColor,
|
|
1844
|
+
accentColor
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
function normalizeNumberHeadings(raw) {
|
|
1848
|
+
if (raw === void 0 || raw === false) return void 0;
|
|
1849
|
+
if (raw === true) {
|
|
1850
|
+
return { enabled: true, depth: 3, skipH1: false, prefix: "" };
|
|
1851
|
+
}
|
|
1852
|
+
if (typeof raw === "object") {
|
|
1853
|
+
const obj = raw;
|
|
1854
|
+
if (obj.enabled === false) return void 0;
|
|
1855
|
+
return {
|
|
1856
|
+
enabled: true,
|
|
1857
|
+
depth: obj.depth ?? 3,
|
|
1858
|
+
skipH1: obj.skipH1 ?? false,
|
|
1859
|
+
prefix: obj.prefix ?? ""
|
|
1860
|
+
};
|
|
1861
|
+
}
|
|
1862
|
+
return void 0;
|
|
1863
|
+
}
|
|
1864
|
+
function normalizeSecurity(raw) {
|
|
1865
|
+
if (!raw) return void 0;
|
|
1866
|
+
const sec = raw;
|
|
1867
|
+
if (!sec.userPassword && !sec.ownerPassword && !sec.permissions) return void 0;
|
|
1868
|
+
return {
|
|
1869
|
+
userPassword: sec.userPassword,
|
|
1870
|
+
ownerPassword: sec.ownerPassword,
|
|
1871
|
+
permissions: sec.permissions
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1460
1874
|
function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
1461
1875
|
const configMeta = userConfig.metadata || {};
|
|
1462
1876
|
const mergedMeta = { ...configMeta, ...frontmatter };
|
|
@@ -1467,7 +1881,15 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1467
1881
|
const version = mergedMeta.version || void 0;
|
|
1468
1882
|
const company = mergedMeta.company || void 0;
|
|
1469
1883
|
const lang = mergedMeta.lang || "en";
|
|
1470
|
-
const tokenContext = {
|
|
1884
|
+
const tokenContext = {
|
|
1885
|
+
...mergedMeta,
|
|
1886
|
+
title,
|
|
1887
|
+
subtitle,
|
|
1888
|
+
author,
|
|
1889
|
+
version,
|
|
1890
|
+
date,
|
|
1891
|
+
company
|
|
1892
|
+
};
|
|
1471
1893
|
const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
|
|
1472
1894
|
const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
|
|
1473
1895
|
const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
|
|
@@ -1496,9 +1918,18 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1496
1918
|
const footer = normalizeHeaderFooter(rawFooter, tokenContext);
|
|
1497
1919
|
const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
|
|
1498
1920
|
const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
|
|
1499
|
-
const watermark = normalizeWatermark(rawWatermark);
|
|
1921
|
+
const watermark = normalizeWatermark(rawWatermark, tokenContext);
|
|
1500
1922
|
const rawSignatures = mergedMeta.signatures || userConfig.signatures;
|
|
1501
1923
|
const signatures = normalizeSignatures(rawSignatures, tokenContext);
|
|
1924
|
+
const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
|
|
1925
|
+
const coverPage = normalizeCoverPage(rawCover, tokenContext);
|
|
1926
|
+
const rawBack = mergedMeta.backCover !== void 0 ? mergedMeta.backCover : userConfig.backCover;
|
|
1927
|
+
const backCover = normalizeBackCover(rawBack, tokenContext);
|
|
1928
|
+
const rawNumberHeadings = mergedMeta.numberHeadings !== void 0 ? mergedMeta.numberHeadings : userConfig.numberHeadings;
|
|
1929
|
+
const numberHeadings = normalizeNumberHeadings(rawNumberHeadings);
|
|
1930
|
+
const rawSecurity = mergedMeta.security || userConfig.security;
|
|
1931
|
+
const security = normalizeSecurity(rawSecurity);
|
|
1932
|
+
const math = mergedMeta.math !== false && userConfig.math !== false;
|
|
1502
1933
|
const cssList = [];
|
|
1503
1934
|
const addCss = (item) => {
|
|
1504
1935
|
if (!item) return;
|
|
@@ -1528,6 +1959,11 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1528
1959
|
toc,
|
|
1529
1960
|
signatures,
|
|
1530
1961
|
watermark,
|
|
1962
|
+
coverPage,
|
|
1963
|
+
backCover,
|
|
1964
|
+
numberHeadings,
|
|
1965
|
+
security,
|
|
1966
|
+
math,
|
|
1531
1967
|
css: cssList,
|
|
1532
1968
|
embedImages,
|
|
1533
1969
|
bundleHtml,
|
|
@@ -1535,11 +1971,50 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1535
1971
|
};
|
|
1536
1972
|
}
|
|
1537
1973
|
|
|
1974
|
+
// src/core/math/mathRenderer.ts
|
|
1975
|
+
var import_katex = __toESM(require("katex"));
|
|
1976
|
+
function renderMathToHtml(latex, displayMode = false) {
|
|
1977
|
+
try {
|
|
1978
|
+
return import_katex.default.renderToString(latex.trim(), {
|
|
1979
|
+
displayMode,
|
|
1980
|
+
throwOnError: false,
|
|
1981
|
+
output: "htmlAndMathml",
|
|
1982
|
+
strict: false
|
|
1983
|
+
});
|
|
1984
|
+
} catch {
|
|
1985
|
+
return `<span class="katex-fallback">${latex}</span>`;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
var KATEX_INLINE_CSS = `
|
|
1989
|
+
.katex { font: normal 1.21em KaTeX_Main, Times New Roman, serif; line-height: 1.2; text-indent: 0; text-rendering: auto; border-color: currentColor; }
|
|
1990
|
+
.katex * { -ms-high-contrast-adjust: none !important; }
|
|
1991
|
+
.katex .katex-html { display: inline-block; }
|
|
1992
|
+
.katex .katex-mathml { clip: rect(1px, 1px, 1px, 1px); border: 0; height: 1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
|
|
1993
|
+
.katex-display { display: block; margin: 1em 0; text-align: center; }
|
|
1994
|
+
.katex-display > .katex { display: inline-block; text-align: initial; }
|
|
1995
|
+
.katex .base { position: relative; white-space: nowrap; width: min-content; }
|
|
1996
|
+
.katex .strut { display: inline-block; }
|
|
1997
|
+
.katex .mord { display: inline-block; }
|
|
1998
|
+
.katex .mbin { display: inline-block; }
|
|
1999
|
+
.katex .mrel { display: inline-block; }
|
|
2000
|
+
.katex .mopen { display: inline-block; }
|
|
2001
|
+
.katex .mclose { display: inline-block; }
|
|
2002
|
+
.katex .mpunct { display: inline-block; }
|
|
2003
|
+
.katex .minner { display: inline-block; }
|
|
2004
|
+
.katex .mop { display: inline-block; }
|
|
2005
|
+
.katex .frac-line { width: 100%; border-bottom-style: solid; }
|
|
2006
|
+
.katex .vlist-t { display: inline-table; table-layout: fixed; }
|
|
2007
|
+
.katex .vlist-r { display: table-row; }
|
|
2008
|
+
.katex .vlist { display: table-cell; vertical-align: bottom; position: relative; }
|
|
2009
|
+
.katex .msupsub { text-align: left; }
|
|
2010
|
+
.katex .sqrt > .root { margin-left: 0.27777778em; margin-right: -0.55555556em; }
|
|
2011
|
+
`;
|
|
2012
|
+
|
|
1538
2013
|
// src/core/html/htmlBuilder.ts
|
|
1539
2014
|
function escapeHtml(str) {
|
|
1540
2015
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1541
2016
|
}
|
|
1542
|
-
async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
2017
|
+
async function renderInlinesToHtml(spans = [], baseDir = process.cwd(), tokens) {
|
|
1543
2018
|
let result = "";
|
|
1544
2019
|
for (const span of spans) {
|
|
1545
2020
|
if (span.type === "image" && span.url) {
|
|
@@ -1553,23 +2028,23 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1553
2028
|
continue;
|
|
1554
2029
|
}
|
|
1555
2030
|
if (span.type === "link" && span.url) {
|
|
1556
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
2031
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1557
2032
|
const title = span.title ? ` title="${escapeHtml(span.title)}"` : "";
|
|
1558
2033
|
result += `<a href="${escapeHtml(span.url)}"${title}>${inner}</a>`;
|
|
1559
2034
|
continue;
|
|
1560
2035
|
}
|
|
1561
2036
|
if (span.type === "bold") {
|
|
1562
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
2037
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1563
2038
|
result += `<strong>${inner}</strong>`;
|
|
1564
2039
|
continue;
|
|
1565
2040
|
}
|
|
1566
2041
|
if (span.type === "italic") {
|
|
1567
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
2042
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1568
2043
|
result += `<em>${inner}</em>`;
|
|
1569
2044
|
continue;
|
|
1570
2045
|
}
|
|
1571
2046
|
if (span.type === "strikethrough") {
|
|
1572
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
2047
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1573
2048
|
result += `<del>${inner}</del>`;
|
|
1574
2049
|
continue;
|
|
1575
2050
|
}
|
|
@@ -1577,83 +2052,57 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1577
2052
|
result += `<code>${escapeHtml(span.content)}</code>`;
|
|
1578
2053
|
continue;
|
|
1579
2054
|
}
|
|
2055
|
+
if (span.type === "mathInline") {
|
|
2056
|
+
result += renderMathToHtml(span.content, false);
|
|
2057
|
+
continue;
|
|
2058
|
+
}
|
|
2059
|
+
if (span.type === "footnoteRef") {
|
|
2060
|
+
const id = escapeHtml(span.footnoteId || span.content);
|
|
2061
|
+
result += `<sup><a href="#fn-${id}" id="fnref-${id}" class="markforge-fnref">[${escapeHtml(span.content)}]</a></sup>`;
|
|
2062
|
+
continue;
|
|
2063
|
+
}
|
|
1580
2064
|
if (span.type === "htmlInline") {
|
|
1581
2065
|
result += span.content;
|
|
1582
2066
|
continue;
|
|
1583
2067
|
}
|
|
1584
|
-
|
|
2068
|
+
const content = tokens ? replaceDocumentTokens(span.content, tokens) : span.content;
|
|
2069
|
+
result += escapeHtml(content);
|
|
1585
2070
|
}
|
|
1586
2071
|
return result;
|
|
1587
2072
|
}
|
|
1588
|
-
async function
|
|
1589
|
-
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
1590
|
-
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
1591
|
-
let customCss = "";
|
|
1592
|
-
for (const cssPath of resolved.css) {
|
|
1593
|
-
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
1594
|
-
if (fs3.existsSync(fullCssPath)) {
|
|
1595
|
-
customCss += `
|
|
1596
|
-
/* Custom CSS: ${cssPath} */
|
|
1597
|
-
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
2073
|
+
async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd(), tokens) {
|
|
1601
2074
|
let bodyHtml = "";
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
`;
|
|
1605
|
-
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
1606
|
-
`;
|
|
1607
|
-
if (resolved.subtitle) {
|
|
1608
|
-
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
1609
|
-
`;
|
|
1610
|
-
}
|
|
1611
|
-
if (resolved.author || resolved.date || resolved.version) {
|
|
1612
|
-
bodyHtml += ` <div class="document-meta">
|
|
1613
|
-
`;
|
|
1614
|
-
if (resolved.author) {
|
|
1615
|
-
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
1616
|
-
`;
|
|
1617
|
-
}
|
|
1618
|
-
if (resolved.version) {
|
|
1619
|
-
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
1620
|
-
`;
|
|
1621
|
-
}
|
|
1622
|
-
if (resolved.date) {
|
|
1623
|
-
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
1624
|
-
`;
|
|
1625
|
-
}
|
|
1626
|
-
bodyHtml += ` </div>
|
|
1627
|
-
`;
|
|
1628
|
-
}
|
|
1629
|
-
bodyHtml += ` </header>
|
|
1630
|
-
`;
|
|
1631
|
-
}
|
|
1632
|
-
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
1633
|
-
bodyHtml += ` <nav class="table-of-contents">
|
|
1634
|
-
`;
|
|
1635
|
-
bodyHtml += ` <h2>Table of Contents</h2>
|
|
1636
|
-
<ul>
|
|
1637
|
-
`;
|
|
1638
|
-
for (const entry of doc.tocEntries) {
|
|
1639
|
-
const indent = " ".repeat(entry.level);
|
|
1640
|
-
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
1641
|
-
`;
|
|
1642
|
-
}
|
|
1643
|
-
bodyHtml += ` </ul>
|
|
1644
|
-
</nav>
|
|
1645
|
-
`;
|
|
1646
|
-
}
|
|
1647
|
-
for (const node of doc.nodes) {
|
|
2075
|
+
const tokenCtx = tokens || resolved;
|
|
2076
|
+
for (const node of nodes) {
|
|
1648
2077
|
if (node.type === "heading") {
|
|
1649
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2078
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1650
2079
|
bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
|
|
1651
2080
|
`;
|
|
1652
2081
|
continue;
|
|
1653
2082
|
}
|
|
1654
2083
|
if (node.type === "paragraph") {
|
|
1655
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2084
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1656
2085
|
bodyHtml += ` <p>${inner}</p>
|
|
2086
|
+
`;
|
|
2087
|
+
continue;
|
|
2088
|
+
}
|
|
2089
|
+
if (node.type === "mathBlock") {
|
|
2090
|
+
bodyHtml += ` <div class="math-block">${renderMathToHtml(node.text || "", true)}</div>
|
|
2091
|
+
`;
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
if (node.type === "columns") {
|
|
2095
|
+
const cols = node.columnsCount || 2;
|
|
2096
|
+
const gap = node.columnGap || "1.5rem";
|
|
2097
|
+
let colChildrenHtml = "";
|
|
2098
|
+
for (const col of node.children || []) {
|
|
2099
|
+
const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir, tokenCtx);
|
|
2100
|
+
colChildrenHtml += ` <div class="markforge-col">
|
|
2101
|
+
${colInner} </div>
|
|
2102
|
+
`;
|
|
2103
|
+
}
|
|
2104
|
+
bodyHtml += ` <div class="markforge-columns" style="--cols: ${cols}; --col-gap: ${gap};">
|
|
2105
|
+
${colChildrenHtml} </div>
|
|
1657
2106
|
`;
|
|
1658
2107
|
continue;
|
|
1659
2108
|
}
|
|
@@ -1673,7 +2122,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1673
2122
|
continue;
|
|
1674
2123
|
}
|
|
1675
2124
|
if (node.type === "callout") {
|
|
1676
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2125
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1677
2126
|
const CALLOUT_STYLES = {
|
|
1678
2127
|
NOTE: { bg: "#ECFDFD", border: "#33CDCF", titleColor: "#009DA0" },
|
|
1679
2128
|
TIP: { bg: "#ecfdf5", border: "#10b981", titleColor: "#10b981" },
|
|
@@ -1693,7 +2142,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1693
2142
|
continue;
|
|
1694
2143
|
}
|
|
1695
2144
|
if (node.type === "blockquote") {
|
|
1696
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2145
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1697
2146
|
bodyHtml += ` <blockquote>${inner}</blockquote>
|
|
1698
2147
|
`;
|
|
1699
2148
|
continue;
|
|
@@ -1707,7 +2156,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1707
2156
|
for (const cell of row.children || []) {
|
|
1708
2157
|
const tag = cell.isHeader ? "th" : "td";
|
|
1709
2158
|
const align = cell.align ? ` align="${cell.align}"` : "";
|
|
1710
|
-
const inner = await renderInlinesToHtml(cell.inlines, baseDir);
|
|
2159
|
+
const inner = await renderInlinesToHtml(cell.inlines, baseDir, tokenCtx);
|
|
1711
2160
|
bodyHtml += ` <${tag}${align}>${inner}</${tag}>
|
|
1712
2161
|
`;
|
|
1713
2162
|
}
|
|
@@ -1723,7 +2172,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1723
2172
|
bodyHtml += ` <${tag}>
|
|
1724
2173
|
`;
|
|
1725
2174
|
for (const item of node.children) {
|
|
1726
|
-
const inner = await renderInlinesToHtml(item.inlines, baseDir);
|
|
2175
|
+
const inner = await renderInlinesToHtml(item.inlines, baseDir, tokenCtx);
|
|
1727
2176
|
bodyHtml += ` <li>${inner}</li>
|
|
1728
2177
|
`;
|
|
1729
2178
|
}
|
|
@@ -1742,70 +2191,508 @@ ${escapeHtml(node.text || "")}
|
|
|
1742
2191
|
continue;
|
|
1743
2192
|
}
|
|
1744
2193
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
width: 100vw;
|
|
1755
|
-
height: 100vh;
|
|
1756
|
-
pointer-events: none;
|
|
1757
|
-
z-index: 0;
|
|
1758
|
-
user-select: none;
|
|
1759
|
-
-webkit-user-select: none;
|
|
2194
|
+
return bodyHtml;
|
|
2195
|
+
}
|
|
2196
|
+
async function renderCoverPageHtml(cover, baseDir = process.cwd()) {
|
|
2197
|
+
let logoHtml = "";
|
|
2198
|
+
if (cover.logo) {
|
|
2199
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
2200
|
+
const src = resolvedLogo ? resolvedLogo.dataUri : cover.logo;
|
|
2201
|
+
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;";
|
|
2202
|
+
logoHtml = `<div class="cover-logo"><img src="${src}" alt="Logo" style="${widthStyle} object-fit: contain;" /></div>`;
|
|
1760
2203
|
}
|
|
1761
|
-
.
|
|
2204
|
+
const badgeHtml = cover.badge ? `<div class="cover-badge" style="${cover.badgeColor ? `background-color: ${cover.badgeColor};` : ""}${cover.badgeTextColor ? `color: ${cover.badgeTextColor};` : ""}">${escapeHtml(cover.badge)}</div>` : "";
|
|
2205
|
+
const titleHtml = `<h1 class="cover-title">${escapeHtml(cover.title)}</h1>`;
|
|
2206
|
+
const subtitleHtml = cover.subtitle ? `<div class="cover-subtitle">${escapeHtml(cover.subtitle)}</div>` : "";
|
|
2207
|
+
const metaItems = [];
|
|
2208
|
+
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>`);
|
|
2209
|
+
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>`);
|
|
2210
|
+
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>`);
|
|
2211
|
+
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>`);
|
|
2212
|
+
const metaHtml = metaItems.length > 0 ? `<div class="cover-meta">${metaItems.join("\n")}</div>` : "";
|
|
2213
|
+
const footerHtml = cover.footerText ? `<div class="cover-footer-text">${escapeHtml(cover.footerText)}</div>` : "";
|
|
2214
|
+
const css = `
|
|
2215
|
+
.markforge-cover {
|
|
2216
|
+
min-height: 100vh;
|
|
2217
|
+
box-sizing: border-box;
|
|
2218
|
+
display: flex;
|
|
2219
|
+
flex-direction: column;
|
|
2220
|
+
justify-content: space-between;
|
|
2221
|
+
padding: 4rem 3.5rem;
|
|
2222
|
+
page-break-after: always;
|
|
2223
|
+
break-after: page;
|
|
1762
2224
|
position: relative;
|
|
1763
|
-
z-index:
|
|
2225
|
+
z-index: 2;
|
|
2226
|
+
background: ${cover.bgGradient || "#FFFFFF"};
|
|
2227
|
+
-webkit-print-color-adjust: exact;
|
|
2228
|
+
print-color-adjust: exact;
|
|
2229
|
+
color: ${cover.textColor || "#0F172A"};
|
|
2230
|
+
}
|
|
2231
|
+
.markforge-cover.cover-modern {
|
|
2232
|
+
border-top: 8px solid #0D998D;
|
|
2233
|
+
}
|
|
2234
|
+
.markforge-cover.cover-corporate-split {
|
|
2235
|
+
border-left: 12px solid #0D998D;
|
|
2236
|
+
}
|
|
2237
|
+
.markforge-cover.cover-card {
|
|
2238
|
+
background: #F8FAFC;
|
|
2239
|
+
}
|
|
2240
|
+
.cover-top {
|
|
2241
|
+
display: flex;
|
|
2242
|
+
justify-content: space-between;
|
|
2243
|
+
align-items: flex-start;
|
|
2244
|
+
width: 100%;
|
|
2245
|
+
}
|
|
2246
|
+
.cover-badge {
|
|
2247
|
+
display: inline-block;
|
|
2248
|
+
padding: 0.35rem 0.85rem;
|
|
2249
|
+
font-size: 0.78rem;
|
|
2250
|
+
font-weight: 700;
|
|
2251
|
+
letter-spacing: 0.08em;
|
|
2252
|
+
text-transform: uppercase;
|
|
2253
|
+
background-color: #ECFDFD;
|
|
2254
|
+
color: #0D998D;
|
|
2255
|
+
border-radius: 4px;
|
|
2256
|
+
border: 1px solid #33CDCF;
|
|
2257
|
+
}
|
|
2258
|
+
.cover-body {
|
|
2259
|
+
margin: auto 0;
|
|
2260
|
+
}
|
|
2261
|
+
.cover-title {
|
|
2262
|
+
font-size: 2.8rem;
|
|
2263
|
+
font-weight: 800;
|
|
2264
|
+
line-height: 1.15;
|
|
2265
|
+
margin: 0 0 1rem 0;
|
|
2266
|
+
color: inherit;
|
|
2267
|
+
}
|
|
2268
|
+
.cover-subtitle {
|
|
2269
|
+
font-size: 1.35rem;
|
|
2270
|
+
font-weight: 400;
|
|
2271
|
+
color: #64748B;
|
|
2272
|
+
margin: 0 0 2rem 0;
|
|
2273
|
+
line-height: 1.4;
|
|
2274
|
+
}
|
|
2275
|
+
.cover-meta {
|
|
2276
|
+
display: flex;
|
|
2277
|
+
flex-direction: column;
|
|
2278
|
+
gap: 0.5rem;
|
|
2279
|
+
border-top: 1.5px solid #E2E8F0;
|
|
2280
|
+
padding-top: 1.5rem;
|
|
2281
|
+
max-width: 480px;
|
|
2282
|
+
}
|
|
2283
|
+
.cover-meta-item {
|
|
2284
|
+
font-size: 0.92rem;
|
|
2285
|
+
display: flex;
|
|
2286
|
+
gap: 0.75rem;
|
|
2287
|
+
}
|
|
2288
|
+
.cover-meta-label {
|
|
2289
|
+
font-weight: 600;
|
|
2290
|
+
color: #64748B;
|
|
2291
|
+
min-width: 110px;
|
|
2292
|
+
}
|
|
2293
|
+
.cover-meta-value {
|
|
2294
|
+
font-weight: 500;
|
|
2295
|
+
color: #0F172A;
|
|
2296
|
+
}
|
|
2297
|
+
.cover-bottom {
|
|
2298
|
+
display: flex;
|
|
2299
|
+
justify-content: space-between;
|
|
2300
|
+
align-items: flex-end;
|
|
2301
|
+
font-size: 0.82rem;
|
|
2302
|
+
color: #94A3B8;
|
|
1764
2303
|
}
|
|
1765
2304
|
@media print {
|
|
1766
|
-
.
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
left: 0;
|
|
1770
|
-
width: 100vw;
|
|
2305
|
+
.markforge-cover {
|
|
2306
|
+
page-break-after: always;
|
|
2307
|
+
break-after: page;
|
|
1771
2308
|
height: 100vh;
|
|
2309
|
+
min-height: 100vh;
|
|
2310
|
+
max-height: 100vh;
|
|
2311
|
+
box-sizing: border-box;
|
|
2312
|
+
overflow: hidden;
|
|
2313
|
+
margin: 0;
|
|
1772
2314
|
-webkit-print-color-adjust: exact;
|
|
1773
2315
|
print-color-adjust: exact;
|
|
1774
2316
|
}
|
|
1775
2317
|
}
|
|
1776
2318
|
`;
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
2319
|
+
const html = ` <section class="markforge-cover cover-${cover.preset}">
|
|
2320
|
+
<div class="cover-top">
|
|
2321
|
+
${logoHtml}
|
|
2322
|
+
${badgeHtml}
|
|
2323
|
+
</div>
|
|
2324
|
+
<div class="cover-body">
|
|
2325
|
+
${titleHtml}
|
|
2326
|
+
${subtitleHtml}
|
|
2327
|
+
${metaHtml}
|
|
2328
|
+
</div>
|
|
2329
|
+
<div class="cover-bottom">
|
|
2330
|
+
${footerHtml}
|
|
2331
|
+
</div>
|
|
2332
|
+
</section>
|
|
2333
|
+
`;
|
|
2334
|
+
return { html, css };
|
|
2335
|
+
}
|
|
2336
|
+
async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
|
|
2337
|
+
let logoHtml = "";
|
|
2338
|
+
if (backCover.logo) {
|
|
2339
|
+
const resolved = await resolveImage(backCover.logo, baseDir);
|
|
2340
|
+
const src = resolved ? resolved.dataUri : backCover.logo;
|
|
2341
|
+
const widthStyle = backCover.logoWidth ? `style="width: ${typeof backCover.logoWidth === "number" ? `${backCover.logoWidth}px` : backCover.logoWidth}; max-width: 100%;"` : `style="max-width: 160px; height: auto;"`;
|
|
2342
|
+
logoHtml = `<div class="back-logo"><img src="${src}" alt="Brand Logo" ${widthStyle} /></div>`;
|
|
2343
|
+
}
|
|
2344
|
+
const badgeHtml = backCover.badge ? `<div class="back-badge" style="${backCover.badgeColor ? `background-color: ${backCover.badgeColor};` : ""}${backCover.badgeTextColor ? `color: ${backCover.badgeTextColor};` : ""}">${escapeHtml(backCover.badge)}</div>` : "";
|
|
2345
|
+
const titleHtml = `<h1 class="back-title">${escapeHtml(backCover.title)}</h1>`;
|
|
2346
|
+
const subtitleHtml = backCover.subtitle ? `<div class="back-subtitle">${escapeHtml(backCover.subtitle)}</div>` : "";
|
|
2347
|
+
const contactItems = [];
|
|
2348
|
+
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>`);
|
|
2349
|
+
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>`);
|
|
2350
|
+
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>`);
|
|
2351
|
+
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>`);
|
|
2352
|
+
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>`);
|
|
2353
|
+
if (backCover.social) {
|
|
2354
|
+
for (const [network, url] of Object.entries(backCover.social)) {
|
|
2355
|
+
if (url) {
|
|
2356
|
+
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>`);
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
const contactHtml = contactItems.length > 0 ? `<div class="back-contact-grid">${contactItems.join("\n")}</div>` : "";
|
|
2361
|
+
const copyrightHtml = backCover.copyright ? `<div class="back-copyright">${escapeHtml(backCover.copyright)}</div>` : "";
|
|
2362
|
+
const isDark = backCover.preset === "corporate";
|
|
2363
|
+
const css = `
|
|
2364
|
+
.markforge-back-cover {
|
|
2365
|
+
min-height: 100vh;
|
|
2366
|
+
box-sizing: border-box;
|
|
2367
|
+
display: flex;
|
|
2368
|
+
flex-direction: column;
|
|
2369
|
+
justify-content: space-between;
|
|
2370
|
+
padding: 4rem 3.5rem;
|
|
2371
|
+
page-break-before: always;
|
|
2372
|
+
break-before: page;
|
|
2373
|
+
position: relative;
|
|
2374
|
+
z-index: 2;
|
|
2375
|
+
background: ${backCover.bgGradient || (isDark ? "#0F172A" : "#FFFFFF")};
|
|
2376
|
+
color: ${backCover.textColor || (isDark ? "#F8FAFC" : "#0F172A")};
|
|
2377
|
+
}
|
|
2378
|
+
.markforge-back-cover.back-modern {
|
|
2379
|
+
border-bottom: 8px solid #0D998D;
|
|
2380
|
+
}
|
|
2381
|
+
.markforge-back-cover.back-corporate {
|
|
2382
|
+
border-left: 12px solid #33CDCF;
|
|
2383
|
+
}
|
|
2384
|
+
.markforge-back-cover.back-card {
|
|
2385
|
+
background: #F8FAFC;
|
|
2386
|
+
}
|
|
2387
|
+
.back-top {
|
|
2388
|
+
display: flex;
|
|
2389
|
+
justify-content: space-between;
|
|
2390
|
+
align-items: flex-start;
|
|
2391
|
+
width: 100%;
|
|
2392
|
+
}
|
|
2393
|
+
.back-badge {
|
|
2394
|
+
display: inline-block;
|
|
2395
|
+
padding: 0.35rem 0.85rem;
|
|
2396
|
+
font-size: 0.78rem;
|
|
2397
|
+
font-weight: 700;
|
|
2398
|
+
letter-spacing: 0.08em;
|
|
2399
|
+
text-transform: uppercase;
|
|
2400
|
+
background-color: #ECFDFD;
|
|
2401
|
+
color: #0D998D;
|
|
2402
|
+
border-radius: 4px;
|
|
2403
|
+
border: 1px solid #33CDCF;
|
|
2404
|
+
}
|
|
2405
|
+
.back-body {
|
|
2406
|
+
margin: auto 0;
|
|
2407
|
+
}
|
|
2408
|
+
.back-title {
|
|
2409
|
+
font-size: 2.6rem;
|
|
2410
|
+
font-weight: 800;
|
|
2411
|
+
line-height: 1.15;
|
|
2412
|
+
margin: 0 0 0.75rem 0;
|
|
2413
|
+
color: inherit;
|
|
2414
|
+
}
|
|
2415
|
+
.back-subtitle {
|
|
2416
|
+
font-size: 1.25rem;
|
|
2417
|
+
font-weight: 400;
|
|
2418
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2419
|
+
margin: 0 0 2rem 0;
|
|
2420
|
+
line-height: 1.4;
|
|
2421
|
+
}
|
|
2422
|
+
.back-contact-grid {
|
|
2423
|
+
display: flex;
|
|
2424
|
+
flex-direction: column;
|
|
2425
|
+
gap: 0.6rem;
|
|
2426
|
+
border-top: 1.5px solid ${isDark ? "#334155" : "#E2E8F0"};
|
|
2427
|
+
padding-top: 1.5rem;
|
|
2428
|
+
max-width: 540px;
|
|
2429
|
+
}
|
|
2430
|
+
.back-contact-item {
|
|
2431
|
+
font-size: 0.92rem;
|
|
2432
|
+
display: flex;
|
|
2433
|
+
gap: 0.75rem;
|
|
2434
|
+
}
|
|
2435
|
+
.back-contact-label {
|
|
2436
|
+
font-weight: 600;
|
|
2437
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2438
|
+
min-width: 110px;
|
|
2439
|
+
}
|
|
2440
|
+
.back-contact-link {
|
|
2441
|
+
color: #0D998D;
|
|
2442
|
+
text-decoration: none;
|
|
2443
|
+
font-weight: 600;
|
|
2444
|
+
}
|
|
2445
|
+
.back-contact-link:hover {
|
|
2446
|
+
text-decoration: underline;
|
|
2447
|
+
}
|
|
2448
|
+
.back-copyright {
|
|
2449
|
+
font-size: 0.82rem;
|
|
2450
|
+
color: ${isDark ? "#64748B" : "#94A3B8"};
|
|
2451
|
+
border-top: 1px solid ${isDark ? "#1E293B" : "#F1F5F9"};
|
|
2452
|
+
padding-top: 1rem;
|
|
2453
|
+
margin-top: 2rem;
|
|
2454
|
+
}
|
|
2455
|
+
@media print {
|
|
2456
|
+
.markforge-back-cover {
|
|
2457
|
+
page: back-cover-page;
|
|
2458
|
+
page-break-before: always;
|
|
2459
|
+
break-before: page;
|
|
2460
|
+
page-break-after: avoid;
|
|
2461
|
+
break-after: avoid;
|
|
2462
|
+
min-height: 100vh;
|
|
2463
|
+
height: 100vh;
|
|
2464
|
+
max-height: 100vh;
|
|
2465
|
+
margin: 0;
|
|
2466
|
+
box-sizing: border-box;
|
|
2467
|
+
overflow: hidden;
|
|
2468
|
+
-webkit-print-color-adjust: exact;
|
|
2469
|
+
print-color-adjust: exact;
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
2472
|
+
`;
|
|
2473
|
+
const html = ` <section class="markforge-back-cover back-${backCover.preset}">
|
|
2474
|
+
<div class="back-top">
|
|
2475
|
+
${logoHtml}
|
|
2476
|
+
${badgeHtml}
|
|
2477
|
+
</div>
|
|
2478
|
+
<div class="back-body">
|
|
2479
|
+
${titleHtml}
|
|
2480
|
+
${subtitleHtml}
|
|
2481
|
+
${contactHtml}
|
|
2482
|
+
</div>
|
|
2483
|
+
${copyrightHtml}
|
|
2484
|
+
</section>
|
|
2485
|
+
`;
|
|
2486
|
+
return { html, css };
|
|
2487
|
+
}
|
|
2488
|
+
async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
|
|
2489
|
+
var _a, _b;
|
|
2490
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2491
|
+
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
2492
|
+
let customCss = "";
|
|
2493
|
+
for (const cssPath of resolved.css) {
|
|
2494
|
+
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
2495
|
+
if (fs3.existsSync(fullCssPath)) {
|
|
2496
|
+
customCss += `
|
|
2497
|
+
/* Custom CSS: ${cssPath} */
|
|
2498
|
+
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
2502
|
+
const extraCss = `
|
|
2503
|
+
.markforge-columns {
|
|
2504
|
+
display: grid;
|
|
2505
|
+
grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
|
|
2506
|
+
gap: var(--col-gap, 1.5rem);
|
|
2507
|
+
margin: 1.5rem 0;
|
|
2508
|
+
}
|
|
2509
|
+
.markforge-col {
|
|
2510
|
+
min-width: 0;
|
|
2511
|
+
}
|
|
2512
|
+
.markforge-fnref {
|
|
2513
|
+
text-decoration: none;
|
|
2514
|
+
font-size: 0.8em;
|
|
2515
|
+
vertical-align: super;
|
|
2516
|
+
color: #0D998D;
|
|
2517
|
+
font-weight: 700;
|
|
2518
|
+
}
|
|
2519
|
+
.markforge-footnotes {
|
|
2520
|
+
margin-top: 3rem;
|
|
2521
|
+
padding-top: 1rem;
|
|
2522
|
+
font-size: 0.88rem;
|
|
2523
|
+
color: #64748B;
|
|
2524
|
+
}
|
|
2525
|
+
.markforge-footnotes hr {
|
|
2526
|
+
border: 0;
|
|
2527
|
+
border-top: 1px solid #E2E8F0;
|
|
2528
|
+
margin-bottom: 1rem;
|
|
2529
|
+
}
|
|
2530
|
+
.markforge-fn-return {
|
|
2531
|
+
text-decoration: none;
|
|
2532
|
+
color: #0D998D;
|
|
2533
|
+
}
|
|
2534
|
+
.math-block {
|
|
2535
|
+
margin: 1.5rem 0;
|
|
2536
|
+
text-align: center;
|
|
2537
|
+
overflow-x: auto;
|
|
2538
|
+
}
|
|
2539
|
+
`;
|
|
2540
|
+
let coverHtml = "";
|
|
2541
|
+
let coverCss = "";
|
|
2542
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
2543
|
+
const coverRes = await renderCoverPageHtml(resolved.coverPage, baseDir);
|
|
2544
|
+
coverHtml = coverRes.html;
|
|
2545
|
+
coverCss = coverRes.css;
|
|
2546
|
+
}
|
|
2547
|
+
let backHtml = "";
|
|
2548
|
+
let backCss = "";
|
|
2549
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
2550
|
+
const backRes = await renderBackCoverHtml(resolved.backCover, baseDir);
|
|
2551
|
+
backHtml = backRes.html;
|
|
2552
|
+
backCss = backRes.css;
|
|
2553
|
+
}
|
|
2554
|
+
let bodyHtml = "";
|
|
2555
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2556
|
+
bodyHtml += ` <header class="document-header">
|
|
2557
|
+
`;
|
|
2558
|
+
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
2559
|
+
`;
|
|
2560
|
+
if (resolved.subtitle) {
|
|
2561
|
+
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
2562
|
+
`;
|
|
2563
|
+
}
|
|
2564
|
+
if (resolved.author || resolved.date || resolved.version) {
|
|
2565
|
+
bodyHtml += ` <div class="document-meta">
|
|
2566
|
+
`;
|
|
2567
|
+
if (resolved.author) {
|
|
2568
|
+
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
2569
|
+
`;
|
|
2570
|
+
}
|
|
2571
|
+
if (resolved.version) {
|
|
2572
|
+
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
2573
|
+
`;
|
|
2574
|
+
}
|
|
2575
|
+
if (resolved.date) {
|
|
2576
|
+
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
2577
|
+
`;
|
|
2578
|
+
}
|
|
2579
|
+
bodyHtml += ` </div>
|
|
2580
|
+
`;
|
|
2581
|
+
}
|
|
2582
|
+
bodyHtml += ` </header>
|
|
2583
|
+
`;
|
|
2584
|
+
}
|
|
2585
|
+
if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
|
|
2586
|
+
applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
|
|
2587
|
+
}
|
|
2588
|
+
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
2589
|
+
bodyHtml += ` <nav class="table-of-contents">
|
|
2590
|
+
`;
|
|
2591
|
+
bodyHtml += ` <h2>Table of Contents</h2>
|
|
2592
|
+
<ul>
|
|
2593
|
+
`;
|
|
2594
|
+
for (const entry of doc.tocEntries) {
|
|
2595
|
+
const indent = " ".repeat(entry.level);
|
|
2596
|
+
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
2597
|
+
`;
|
|
2598
|
+
}
|
|
2599
|
+
bodyHtml += ` </ul>
|
|
2600
|
+
</nav>
|
|
2601
|
+
`;
|
|
2602
|
+
}
|
|
2603
|
+
const mergedTokens = {
|
|
2604
|
+
...config.metadata,
|
|
2605
|
+
...doc.metadata,
|
|
2606
|
+
...resolved,
|
|
2607
|
+
title: resolved.title,
|
|
2608
|
+
subtitle: resolved.subtitle,
|
|
2609
|
+
author: resolved.author,
|
|
2610
|
+
version: resolved.version,
|
|
2611
|
+
date: resolved.date,
|
|
2612
|
+
company: resolved.company
|
|
2613
|
+
};
|
|
2614
|
+
const nodesHtml = await renderNodesToHtml(doc.nodes, resolved, baseDir, mergedTokens);
|
|
2615
|
+
bodyHtml += ` <main class="markforge-content-body">
|
|
2616
|
+
${nodesHtml} </main>
|
|
2617
|
+
`;
|
|
2618
|
+
let footnotesHtml = "";
|
|
2619
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
2620
|
+
let fnListHtml = "";
|
|
2621
|
+
for (const def of doc.footnoteDefs) {
|
|
2622
|
+
const defInner = await renderInlinesToHtml(def.inlines, baseDir, mergedTokens);
|
|
2623
|
+
fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">↩</a></li>
|
|
2624
|
+
`;
|
|
2625
|
+
}
|
|
2626
|
+
footnotesHtml = `
|
|
2627
|
+
<footer class="markforge-footnotes">
|
|
2628
|
+
<hr />
|
|
2629
|
+
<ol>
|
|
2630
|
+
${fnListHtml} </ol>
|
|
2631
|
+
</footer>
|
|
2632
|
+
`;
|
|
2633
|
+
}
|
|
2634
|
+
let watermarkCss = "";
|
|
2635
|
+
let watermarkHtml = "";
|
|
2636
|
+
if (resolved.watermark) {
|
|
2637
|
+
const wm = resolved.watermark;
|
|
2638
|
+
watermarkCss = `
|
|
2639
|
+
.document-watermark {
|
|
2640
|
+
position: fixed;
|
|
2641
|
+
top: 0;
|
|
2642
|
+
left: 0;
|
|
2643
|
+
right: 0;
|
|
2644
|
+
bottom: 0;
|
|
2645
|
+
width: 100%;
|
|
2646
|
+
height: 100%;
|
|
2647
|
+
pointer-events: none;
|
|
2648
|
+
z-index: 0;
|
|
2649
|
+
user-select: none;
|
|
2650
|
+
-webkit-user-select: none;
|
|
2651
|
+
-webkit-print-color-adjust: exact;
|
|
2652
|
+
print-color-adjust: exact;
|
|
2653
|
+
}
|
|
2654
|
+
.document-container {
|
|
2655
|
+
position: relative;
|
|
2656
|
+
z-index: 1;
|
|
2657
|
+
}
|
|
2658
|
+
@media print {
|
|
2659
|
+
.document-watermark {
|
|
2660
|
+
display: none !important;
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
`;
|
|
2664
|
+
watermarkHtml = ` <div id="markforge-watermark" class="document-watermark" aria-hidden="true"></div>
|
|
2665
|
+
<script>
|
|
2666
|
+
(function() {
|
|
2667
|
+
try {
|
|
2668
|
+
var canvas = document.createElement('canvas');
|
|
2669
|
+
var dpr = 2;
|
|
2670
|
+
var width = 1200;
|
|
2671
|
+
var height = 1600;
|
|
2672
|
+
canvas.width = width * dpr;
|
|
2673
|
+
canvas.height = height * dpr;
|
|
2674
|
+
var ctx = canvas.getContext('2d');
|
|
2675
|
+
if (ctx) {
|
|
2676
|
+
ctx.scale(dpr, dpr);
|
|
2677
|
+
ctx.translate(width / 2, height / 2);
|
|
2678
|
+
ctx.rotate((-Math.abs(${wm.rotate || 45}) * Math.PI) / 180);
|
|
2679
|
+
ctx.textAlign = 'center';
|
|
2680
|
+
ctx.textBaseline = 'middle';
|
|
2681
|
+
ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
|
|
2682
|
+
ctx.fillStyle = '${wm.color}';
|
|
2683
|
+
ctx.globalAlpha = ${wm.opacity};
|
|
2684
|
+
try { ctx.letterSpacing = '0.15em'; } catch(e) {}
|
|
2685
|
+
ctx.fillText(${JSON.stringify(wm.text.toUpperCase())}, 0, 0);
|
|
2686
|
+
var dataUrl = canvas.toDataURL('image/png');
|
|
2687
|
+
var wmEl = document.getElementById('markforge-watermark');
|
|
2688
|
+
if (wmEl) {
|
|
2689
|
+
wmEl.style.backgroundImage = 'url("' + dataUrl + '")';
|
|
2690
|
+
wmEl.style.backgroundRepeat = 'no-repeat';
|
|
2691
|
+
wmEl.style.backgroundPosition = 'center center';
|
|
2692
|
+
wmEl.style.backgroundSize = 'contain';
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
} catch(err) {}
|
|
1809
2696
|
})();
|
|
1810
2697
|
</script>
|
|
1811
2698
|
`;
|
|
@@ -1815,10 +2702,6 @@ ${escapeHtml(node.text || "")}
|
|
|
1815
2702
|
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
1816
2703
|
const sig = resolved.signatures;
|
|
1817
2704
|
const numItems = sig.items.length;
|
|
1818
|
-
let justifyCss = "flex-end";
|
|
1819
|
-
if (sig.align === "left") justifyCss = "flex-start";
|
|
1820
|
-
else if (sig.align === "center") justifyCss = "center";
|
|
1821
|
-
else if (sig.align === "space-between") justifyCss = "space-between";
|
|
1822
2705
|
signaturesCss = `
|
|
1823
2706
|
.markforge-signatures {
|
|
1824
2707
|
margin-top: ${sig.spacingBefore};
|
|
@@ -1930,6 +2813,10 @@ ${itemCards}
|
|
|
1930
2813
|
<style>
|
|
1931
2814
|
${THEME_COMPONENTS}
|
|
1932
2815
|
${baseThemeCss}
|
|
2816
|
+
${KATEX_INLINE_CSS}
|
|
2817
|
+
${extraCss}
|
|
2818
|
+
${coverCss}
|
|
2819
|
+
${backCss}
|
|
1933
2820
|
${customCss}
|
|
1934
2821
|
${inlinedCss}
|
|
1935
2822
|
${watermarkCss}
|
|
@@ -1937,14 +2824,95 @@ ${signaturesCss}
|
|
|
1937
2824
|
</style>
|
|
1938
2825
|
</head>
|
|
1939
2826
|
<body>
|
|
1940
|
-
${watermarkHtml} <div class="document-container">
|
|
1941
|
-
${bodyHtml}${signaturesHtml} </div>
|
|
2827
|
+
${watermarkHtml}${coverHtml} <div class="document-container">
|
|
2828
|
+
${bodyHtml}${footnotesHtml}${signaturesHtml} </div>
|
|
1942
2829
|
${mermaidScript}
|
|
1943
|
-
</body>
|
|
2830
|
+
${backHtml}</body>
|
|
1944
2831
|
</html>`;
|
|
1945
2832
|
}
|
|
1946
2833
|
|
|
1947
2834
|
// src/core/pdf/pdfBuilder.ts
|
|
2835
|
+
function escapeXml(str) {
|
|
2836
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2837
|
+
}
|
|
2838
|
+
function generateWatermarkPngBuffer(chromePath, wm) {
|
|
2839
|
+
const tmpHtml = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.html`);
|
|
2840
|
+
const tmpPng = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
|
|
2841
|
+
try {
|
|
2842
|
+
const text = escapeXml(wm.text.toUpperCase());
|
|
2843
|
+
const fontSize = (wm.fontSize || 52) * 1.5;
|
|
2844
|
+
const color = wm.color || "#E11D48";
|
|
2845
|
+
const opacity = wm.opacity !== void 0 ? wm.opacity : 0.12;
|
|
2846
|
+
const rotate = wm.rotate !== void 0 ? wm.rotate : -45;
|
|
2847
|
+
const html = `<!DOCTYPE html>
|
|
2848
|
+
<html>
|
|
2849
|
+
<head>
|
|
2850
|
+
<meta charset="utf-8">
|
|
2851
|
+
<style>
|
|
2852
|
+
html, body {
|
|
2853
|
+
margin: 0;
|
|
2854
|
+
padding: 0;
|
|
2855
|
+
width: 1200px;
|
|
2856
|
+
height: 1600px;
|
|
2857
|
+
background: transparent;
|
|
2858
|
+
overflow: hidden;
|
|
2859
|
+
}
|
|
2860
|
+
.wm-box {
|
|
2861
|
+
width: 1200px;
|
|
2862
|
+
height: 1600px;
|
|
2863
|
+
display: flex;
|
|
2864
|
+
align-items: center;
|
|
2865
|
+
justify-content: center;
|
|
2866
|
+
transform: rotate(${rotate}deg);
|
|
2867
|
+
}
|
|
2868
|
+
.wm-text {
|
|
2869
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
2870
|
+
font-weight: 900;
|
|
2871
|
+
font-size: ${fontSize}px;
|
|
2872
|
+
color: ${color};
|
|
2873
|
+
opacity: ${opacity};
|
|
2874
|
+
letter-spacing: 0.15em;
|
|
2875
|
+
text-transform: uppercase;
|
|
2876
|
+
white-space: nowrap;
|
|
2877
|
+
}
|
|
2878
|
+
</style>
|
|
2879
|
+
</head>
|
|
2880
|
+
<body>
|
|
2881
|
+
<div class="wm-box"><span class="wm-text">${text}</span></div>
|
|
2882
|
+
</body>
|
|
2883
|
+
</html>`;
|
|
2884
|
+
fs4.writeFileSync(tmpHtml, html, "utf8");
|
|
2885
|
+
const fileUrl = (0, import_node_url2.pathToFileURL)(tmpHtml).href;
|
|
2886
|
+
const isWin = process.platform === "win32";
|
|
2887
|
+
(0, import_node_child_process.spawnSync)(
|
|
2888
|
+
chromePath,
|
|
2889
|
+
[
|
|
2890
|
+
"--headless=new",
|
|
2891
|
+
"--disable-gpu",
|
|
2892
|
+
"--disable-sync",
|
|
2893
|
+
"--disable-extensions",
|
|
2894
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
2895
|
+
`--screenshot=${tmpPng}`,
|
|
2896
|
+
"--window-size=1200,1600",
|
|
2897
|
+
"--default-background-color=00000000",
|
|
2898
|
+
fileUrl
|
|
2899
|
+
],
|
|
2900
|
+
{ timeout: 15e3, windowsHide: true }
|
|
2901
|
+
);
|
|
2902
|
+
if (fs4.existsSync(tmpPng) && fs4.statSync(tmpPng).size > 0) {
|
|
2903
|
+
return fs4.readFileSync(tmpPng);
|
|
2904
|
+
}
|
|
2905
|
+
return null;
|
|
2906
|
+
} catch {
|
|
2907
|
+
return null;
|
|
2908
|
+
} finally {
|
|
2909
|
+
try {
|
|
2910
|
+
if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
|
|
2911
|
+
if (fs4.existsSync(tmpPng)) fs4.unlinkSync(tmpPng);
|
|
2912
|
+
} catch {
|
|
2913
|
+
}
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
1948
2916
|
function findChromeExecutable() {
|
|
1949
2917
|
if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
|
|
1950
2918
|
return process.env.CHROME_PATH;
|
|
@@ -2009,7 +2977,7 @@ function findChromeExecutable() {
|
|
|
2009
2977
|
return null;
|
|
2010
2978
|
}
|
|
2011
2979
|
function injectPagedMediaStyles(html, config, metadata) {
|
|
2012
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2980
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
2013
2981
|
const resolved = resolveDocumentConfig(metadata || {}, config);
|
|
2014
2982
|
const size = resolved.paperSize;
|
|
2015
2983
|
const orientation = resolved.orientation;
|
|
@@ -2053,6 +3021,66 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
2053
3021
|
${fontStyle}
|
|
2054
3022
|
}`;
|
|
2055
3023
|
};
|
|
3024
|
+
const coverPageCss = ((_a = resolved.coverPage) == null ? void 0 : _a.enabled) ? `
|
|
3025
|
+
@page :first {
|
|
3026
|
+
margin-top: 0;
|
|
3027
|
+
margin-bottom: 0;
|
|
3028
|
+
margin-left: 0;
|
|
3029
|
+
margin-right: 0;
|
|
3030
|
+
background-image: none !important;
|
|
3031
|
+
@top-left { content: none; }
|
|
3032
|
+
@top-center { content: none; }
|
|
3033
|
+
@top-right { content: none; }
|
|
3034
|
+
@bottom-left { content: none; }
|
|
3035
|
+
@bottom-center { content: none; }
|
|
3036
|
+
@bottom-right { content: none; }
|
|
3037
|
+
}` : "";
|
|
3038
|
+
const backCoverCss = ((_b = resolved.backCover) == null ? void 0 : _b.enabled) ? `
|
|
3039
|
+
@page back-cover-page {
|
|
3040
|
+
size: ${size} ${orientation};
|
|
3041
|
+
margin: 0;
|
|
3042
|
+
background-image: none !important;
|
|
3043
|
+
@top-left { content: none; }
|
|
3044
|
+
@top-center { content: none; }
|
|
3045
|
+
@top-right { content: none; }
|
|
3046
|
+
@bottom-left { content: none; }
|
|
3047
|
+
@bottom-center { content: none; }
|
|
3048
|
+
@bottom-right { content: none; }
|
|
3049
|
+
}
|
|
3050
|
+
.markforge-back-cover {
|
|
3051
|
+
page: back-cover-page;
|
|
3052
|
+
min-height: 100vh;
|
|
3053
|
+
height: 100vh;
|
|
3054
|
+
box-sizing: border-box;
|
|
3055
|
+
break-before: page;
|
|
3056
|
+
break-after: avoid;
|
|
3057
|
+
}` : "";
|
|
3058
|
+
const tocPageCss = resolved.toc ? `
|
|
3059
|
+
@page toc-page {
|
|
3060
|
+
size: ${size} ${orientation};
|
|
3061
|
+
margin-top: ${top};
|
|
3062
|
+
margin-bottom: ${bottom};
|
|
3063
|
+
margin-left: ${left};
|
|
3064
|
+
margin-right: ${right};
|
|
3065
|
+
${buildZoneCss("top-left", (_c = resolved.header) == null ? void 0 : _c.left)}
|
|
3066
|
+
${buildZoneCss("top-center", (_d = resolved.header) == null ? void 0 : _d.center)}
|
|
3067
|
+
${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
|
|
3068
|
+
${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
|
|
3069
|
+
${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
|
|
3070
|
+
@bottom-right {
|
|
3071
|
+
content: counter(page, lower-roman);
|
|
3072
|
+
font-size: 9pt;
|
|
3073
|
+
color: #94a3b8;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
.table-of-contents {
|
|
3077
|
+
page: toc-page;
|
|
3078
|
+
page-break-after: always;
|
|
3079
|
+
break-after: page;
|
|
3080
|
+
}
|
|
3081
|
+
.markforge-content-body {
|
|
3082
|
+
counter-reset: page 1;
|
|
3083
|
+
}` : "";
|
|
2056
3084
|
const pagedCss = `
|
|
2057
3085
|
@page {
|
|
2058
3086
|
size: ${size} ${orientation};
|
|
@@ -2060,15 +3088,19 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
2060
3088
|
margin-bottom: ${bottom};
|
|
2061
3089
|
margin-left: ${left};
|
|
2062
3090
|
margin-right: ${right};
|
|
2063
|
-
${buildZoneCss("top-left", (
|
|
2064
|
-
${buildZoneCss("top-center", (
|
|
2065
|
-
${buildZoneCss("top-right", (
|
|
2066
|
-
${buildZoneCss("bottom-left", (
|
|
2067
|
-
${buildZoneCss("bottom-center", (
|
|
2068
|
-
${buildZoneCss("bottom-right", (
|
|
3091
|
+
${buildZoneCss("top-left", (_h = resolved.header) == null ? void 0 : _h.left)}
|
|
3092
|
+
${buildZoneCss("top-center", (_i = resolved.header) == null ? void 0 : _i.center)}
|
|
3093
|
+
${buildZoneCss("top-right", (_j = resolved.header) == null ? void 0 : _j.right)}
|
|
3094
|
+
${buildZoneCss("bottom-left", (_k = resolved.footer) == null ? void 0 : _k.left)}
|
|
3095
|
+
${buildZoneCss("bottom-center", (_l = resolved.footer) == null ? void 0 : _l.center)}
|
|
3096
|
+
${buildZoneCss("bottom-right", (_m = resolved.footer) == null ? void 0 : _m.right, true)}
|
|
2069
3097
|
}
|
|
3098
|
+
${coverPageCss}
|
|
3099
|
+
${tocPageCss}
|
|
3100
|
+
${backCoverCss}
|
|
2070
3101
|
@media print {
|
|
2071
3102
|
body { padding: 0; }
|
|
3103
|
+
.document-watermark { display: none !important; }
|
|
2072
3104
|
h1, h2, h3, pre, table, blockquote, .callout {
|
|
2073
3105
|
break-inside: avoid;
|
|
2074
3106
|
}
|
|
@@ -2116,6 +3148,7 @@ startxref
|
|
|
2116
3148
|
return Buffer.from(pdfBody, "utf-8");
|
|
2117
3149
|
}
|
|
2118
3150
|
async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
3151
|
+
var _a, _b, _c;
|
|
2119
3152
|
const baseHtml = await buildHtmlDocument(doc, config, baseDir);
|
|
2120
3153
|
const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
|
|
2121
3154
|
const chromePath = findChromeExecutable();
|
|
@@ -2125,6 +3158,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2125
3158
|
const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
|
|
2126
3159
|
const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
|
|
2127
3160
|
const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
|
|
3161
|
+
const isWin = process.platform === "win32";
|
|
2128
3162
|
const isolatedFlags = [
|
|
2129
3163
|
`--user-data-dir=${tmpProfile}`,
|
|
2130
3164
|
"--no-first-run",
|
|
@@ -2135,7 +3169,6 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2135
3169
|
"--disable-default-apps",
|
|
2136
3170
|
"--disable-extensions",
|
|
2137
3171
|
"--disable-domain-reliability",
|
|
2138
|
-
"--disable-client-side-phishing-detection",
|
|
2139
3172
|
"--disable-breakpad",
|
|
2140
3173
|
"--disable-component-extensions-with-background-pages",
|
|
2141
3174
|
"--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
|
|
@@ -2144,12 +3177,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2144
3177
|
"--mute-audio",
|
|
2145
3178
|
"--no-service-autorun",
|
|
2146
3179
|
"--disable-gpu",
|
|
2147
|
-
"--no-sandbox",
|
|
2148
|
-
"--disable-setuid-sandbox",
|
|
2149
|
-
"--allow-file-access-from-files",
|
|
2150
|
-
"--disable-web-security",
|
|
3180
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
2151
3181
|
"--force-color-profile=srgb",
|
|
2152
|
-
"--no-pdf-header-footer"
|
|
3182
|
+
"--no-pdf-header-footer",
|
|
3183
|
+
"--window-size=1200,1600"
|
|
2153
3184
|
];
|
|
2154
3185
|
try {
|
|
2155
3186
|
fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
|
|
@@ -2164,7 +3195,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2164
3195
|
`--print-to-pdf=${tmpPdf}`,
|
|
2165
3196
|
fileUrl
|
|
2166
3197
|
],
|
|
2167
|
-
{ timeout: 3e4 }
|
|
3198
|
+
{ timeout: 3e4, windowsHide: true }
|
|
2168
3199
|
);
|
|
2169
3200
|
if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
|
|
2170
3201
|
res = (0, import_node_child_process.spawnSync)(
|
|
@@ -2175,12 +3206,79 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2175
3206
|
`--print-to-pdf=${tmpPdf}`,
|
|
2176
3207
|
fileUrl
|
|
2177
3208
|
],
|
|
2178
|
-
{ timeout: 3e4 }
|
|
3209
|
+
{ timeout: 3e4, windowsHide: true }
|
|
2179
3210
|
);
|
|
2180
3211
|
}
|
|
2181
3212
|
if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
|
|
2182
3213
|
const pdfBuffer = fs4.readFileSync(tmpPdf);
|
|
2183
|
-
|
|
3214
|
+
try {
|
|
3215
|
+
const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBuffer);
|
|
3216
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
3217
|
+
if (resolved.title) pdfDoc.setTitle(resolved.title);
|
|
3218
|
+
if (resolved.author) pdfDoc.setAuthor(resolved.author);
|
|
3219
|
+
if (resolved.subtitle) pdfDoc.setSubject(resolved.subtitle);
|
|
3220
|
+
pdfDoc.setCreator("MarkForge Enterprise Document Generator");
|
|
3221
|
+
pdfDoc.setProducer("MarkForge (by Ma'sum)");
|
|
3222
|
+
pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
|
|
3223
|
+
if (((_a = resolved.backCover) == null ? void 0 : _a.enabled) && pdfDoc.getPageCount() > 2) {
|
|
3224
|
+
pdfDoc.removePage(pdfDoc.getPageCount() - 1);
|
|
3225
|
+
}
|
|
3226
|
+
if (resolved.watermark) {
|
|
3227
|
+
const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
|
|
3228
|
+
if (wmPng) {
|
|
3229
|
+
const embeddedPng = await pdfDoc.embedPng(wmPng);
|
|
3230
|
+
const totalPages = pdfDoc.getPageCount();
|
|
3231
|
+
const pages = pdfDoc.getPages();
|
|
3232
|
+
const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
|
|
3233
|
+
const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? totalPages - 1 : totalPages;
|
|
3234
|
+
for (let i = startPageIndex; i < endPageIndex; i++) {
|
|
3235
|
+
const page = pages[i];
|
|
3236
|
+
const { width, height } = page.getSize();
|
|
3237
|
+
page.drawImage(embeddedPng, {
|
|
3238
|
+
x: 0,
|
|
3239
|
+
y: 0,
|
|
3240
|
+
width,
|
|
3241
|
+
height
|
|
3242
|
+
});
|
|
3243
|
+
}
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
const savedBytes = await pdfDoc.save();
|
|
3247
|
+
let finalBuffer = Buffer.from(savedBytes);
|
|
3248
|
+
if (resolved.security) {
|
|
3249
|
+
const sec = resolved.security;
|
|
3250
|
+
const hasUserPassword = typeof sec.userPassword === "string" && sec.userPassword.length > 0;
|
|
3251
|
+
const hasOwnerPassword = typeof sec.ownerPassword === "string" && sec.ownerPassword.length > 0;
|
|
3252
|
+
if (hasUserPassword || hasOwnerPassword) {
|
|
3253
|
+
try {
|
|
3254
|
+
const userPass = sec.userPassword ?? "";
|
|
3255
|
+
const ownerPass = sec.ownerPassword ?? userPass;
|
|
3256
|
+
const perms = sec.permissions;
|
|
3257
|
+
const encryptedBytes = await (0, import_pdf_encrypt.encryptPDF)(
|
|
3258
|
+
new Uint8Array(finalBuffer),
|
|
3259
|
+
userPass,
|
|
3260
|
+
{
|
|
3261
|
+
ownerPassword: ownerPass,
|
|
3262
|
+
algorithm: "AES-256",
|
|
3263
|
+
allowPrinting: (perms == null ? void 0 : perms.printing) !== "none",
|
|
3264
|
+
allowHighQualityPrint: (perms == null ? void 0 : perms.printing) === "highResolution",
|
|
3265
|
+
allowModifying: (perms == null ? void 0 : perms.modifying) ?? true,
|
|
3266
|
+
allowCopying: (perms == null ? void 0 : perms.copying) ?? true,
|
|
3267
|
+
allowAnnotating: (perms == null ? void 0 : perms.annotating) ?? true,
|
|
3268
|
+
allowFillingForms: (perms == null ? void 0 : perms.fillingForms) ?? true,
|
|
3269
|
+
allowExtraction: (perms == null ? void 0 : perms.contentAccessibility) ?? true,
|
|
3270
|
+
allowAssembly: (perms == null ? void 0 : perms.documentAssembly) ?? true
|
|
3271
|
+
}
|
|
3272
|
+
);
|
|
3273
|
+
finalBuffer = Buffer.from(encryptedBytes);
|
|
3274
|
+
} catch {
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
return finalBuffer;
|
|
3279
|
+
} catch {
|
|
3280
|
+
return pdfBuffer;
|
|
3281
|
+
}
|
|
2184
3282
|
}
|
|
2185
3283
|
} catch {
|
|
2186
3284
|
} finally {
|
|
@@ -2435,6 +3533,29 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2435
3533
|
);
|
|
2436
3534
|
continue;
|
|
2437
3535
|
}
|
|
3536
|
+
if (span.type === "mathInline") {
|
|
3537
|
+
runs.push(
|
|
3538
|
+
new import_docx.TextRun({
|
|
3539
|
+
text: span.content,
|
|
3540
|
+
font: "Cambria Math",
|
|
3541
|
+
italics: true,
|
|
3542
|
+
size: options.size,
|
|
3543
|
+
color: options.color || "0F172A"
|
|
3544
|
+
})
|
|
3545
|
+
);
|
|
3546
|
+
continue;
|
|
3547
|
+
}
|
|
3548
|
+
if (span.type === "footnoteRef") {
|
|
3549
|
+
runs.push(
|
|
3550
|
+
new import_docx.TextRun({
|
|
3551
|
+
text: `[${span.content}]`,
|
|
3552
|
+
superScript: true,
|
|
3553
|
+
color: "009DA0",
|
|
3554
|
+
bold: true
|
|
3555
|
+
})
|
|
3556
|
+
);
|
|
3557
|
+
continue;
|
|
3558
|
+
}
|
|
2438
3559
|
runs.push(
|
|
2439
3560
|
new import_docx.TextRun({
|
|
2440
3561
|
text: span.content,
|
|
@@ -2449,7 +3570,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2449
3570
|
return runs;
|
|
2450
3571
|
}
|
|
2451
3572
|
async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
2452
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
3573
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2453
3574
|
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2454
3575
|
const docElements = [];
|
|
2455
3576
|
const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
|
|
@@ -2460,7 +3581,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2460
3581
|
const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
|
|
2461
3582
|
const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
|
|
2462
3583
|
const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
|
|
2463
|
-
if (resolved.title) {
|
|
3584
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2464
3585
|
docElements.push(
|
|
2465
3586
|
new import_docx.Paragraph({
|
|
2466
3587
|
children: [
|
|
@@ -2521,6 +3642,9 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2521
3642
|
);
|
|
2522
3643
|
}
|
|
2523
3644
|
}
|
|
3645
|
+
if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
|
|
3646
|
+
applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
|
|
3647
|
+
}
|
|
2524
3648
|
if (resolved.toc) {
|
|
2525
3649
|
const headingNodes = doc.nodes.filter(
|
|
2526
3650
|
(n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
|
|
@@ -2590,7 +3714,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2590
3714
|
]
|
|
2591
3715
|
});
|
|
2592
3716
|
docElements.push(tocCard);
|
|
2593
|
-
docElements.push(
|
|
3717
|
+
docElements.push(
|
|
3718
|
+
new import_docx.Paragraph({
|
|
3719
|
+
children: [new import_docx.PageBreak()]
|
|
3720
|
+
})
|
|
3721
|
+
);
|
|
2594
3722
|
}
|
|
2595
3723
|
}
|
|
2596
3724
|
for (const node of doc.nodes) {
|
|
@@ -2600,7 +3728,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2600
3728
|
font: defaultFont,
|
|
2601
3729
|
size: 34,
|
|
2602
3730
|
// 17pt
|
|
2603
|
-
color:
|
|
3731
|
+
color: primaryDarkHex,
|
|
2604
3732
|
bold: true
|
|
2605
3733
|
});
|
|
2606
3734
|
docElements.push(
|
|
@@ -2646,7 +3774,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2646
3774
|
font: defaultFont,
|
|
2647
3775
|
size: 22,
|
|
2648
3776
|
// 11pt
|
|
2649
|
-
color:
|
|
3777
|
+
color: primaryDarkHex,
|
|
2650
3778
|
bold: true
|
|
2651
3779
|
});
|
|
2652
3780
|
docElements.push(
|
|
@@ -2876,7 +4004,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2876
4004
|
}
|
|
2877
4005
|
if (node.type === "table" && node.children) {
|
|
2878
4006
|
const tableRows = [];
|
|
2879
|
-
const numCols = ((
|
|
4007
|
+
const numCols = ((_d = (_c = node.children[0]) == null ? void 0 : _c.children) == null ? void 0 : _d.length) || 1;
|
|
2880
4008
|
const colWidth = Math.floor(9e3 / numCols);
|
|
2881
4009
|
for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
|
|
2882
4010
|
const rowNode = node.children[rowIdx];
|
|
@@ -2886,7 +4014,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2886
4014
|
if (rowNode.children) {
|
|
2887
4015
|
for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
|
|
2888
4016
|
const cellNode = rowNode.children[colIdx];
|
|
2889
|
-
const align = (
|
|
4017
|
+
const align = (_e = node.align) == null ? void 0 : _e[colIdx];
|
|
2890
4018
|
let alignment = import_docx.AlignmentType.LEFT;
|
|
2891
4019
|
if (align === "center") alignment = import_docx.AlignmentType.CENTER;
|
|
2892
4020
|
if (align === "right") alignment = import_docx.AlignmentType.RIGHT;
|
|
@@ -3025,16 +4153,123 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3025
4153
|
}
|
|
3026
4154
|
continue;
|
|
3027
4155
|
}
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
4156
|
+
if (node.type === "mathBlock") {
|
|
4157
|
+
docElements.push(
|
|
4158
|
+
new import_docx.Paragraph({
|
|
4159
|
+
alignment: import_docx.AlignmentType.CENTER,
|
|
4160
|
+
children: [
|
|
4161
|
+
new import_docx.TextRun({
|
|
4162
|
+
text: node.text || "",
|
|
4163
|
+
font: "Cambria Math",
|
|
4164
|
+
italics: true,
|
|
4165
|
+
size: 24,
|
|
4166
|
+
// 12pt
|
|
4167
|
+
color: textHex
|
|
4168
|
+
})
|
|
4169
|
+
],
|
|
4170
|
+
spacing: { before: 180, after: 180 },
|
|
4171
|
+
shading: { fill: cardBgHex, type: import_docx.ShadingType.CLEAR },
|
|
4172
|
+
border: {
|
|
4173
|
+
top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4174
|
+
bottom: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4175
|
+
left: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4176
|
+
right: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex }
|
|
4177
|
+
}
|
|
4178
|
+
})
|
|
4179
|
+
);
|
|
4180
|
+
continue;
|
|
4181
|
+
}
|
|
4182
|
+
if (node.type === "columns") {
|
|
4183
|
+
const cols = node.columnsCount || 2;
|
|
4184
|
+
const contentWidth = Math.max(
|
|
4185
|
+
1e3,
|
|
4186
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4187
|
+
);
|
|
4188
|
+
const cellWidthDxa = Math.floor(contentWidth / cols);
|
|
4189
|
+
const cells = [];
|
|
4190
|
+
for (const col of node.children || []) {
|
|
4191
|
+
const colParagraphs = [];
|
|
4192
|
+
for (const childNode of col.children || []) {
|
|
4193
|
+
if (childNode.type === "heading") {
|
|
4194
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, bold: true, size: 24, color: primaryDarkHex });
|
|
4195
|
+
colParagraphs.push(new import_docx.Paragraph({ children: runs, spacing: { before: 120, after: 60 } }));
|
|
4196
|
+
} else if (childNode.type === "paragraph") {
|
|
4197
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
4198
|
+
colParagraphs.push(new import_docx.Paragraph({ children: runs, spacing: { after: 100 } }));
|
|
4199
|
+
} else if (childNode.type === "list" && childNode.children) {
|
|
4200
|
+
for (const item of childNode.children) {
|
|
4201
|
+
const runs = await convertInlinesToTextRuns(item.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
4202
|
+
colParagraphs.push(new import_docx.Paragraph({ children: [new import_docx.TextRun({ text: "\u2022 ", font: defaultFont, color: primaryHex }), ...runs], spacing: { after: 40 } }));
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
if (colParagraphs.length === 0) colParagraphs.push(new import_docx.Paragraph({}));
|
|
4207
|
+
cells.push(
|
|
4208
|
+
new import_docx.TableCell({
|
|
4209
|
+
width: { size: cellWidthDxa, type: import_docx.WidthType.DXA },
|
|
4210
|
+
borders: {
|
|
4211
|
+
top: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4212
|
+
bottom: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4213
|
+
left: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" },
|
|
4214
|
+
right: { style: import_docx.BorderStyle.NONE, size: 0, color: "auto" }
|
|
4215
|
+
},
|
|
4216
|
+
margins: { top: 60, bottom: 60, left: 100, right: 100 },
|
|
4217
|
+
children: colParagraphs
|
|
4218
|
+
})
|
|
4219
|
+
);
|
|
4220
|
+
}
|
|
4221
|
+
docElements.push(
|
|
4222
|
+
new import_docx.Table({
|
|
4223
|
+
width: { size: 100, type: import_docx.WidthType.PERCENTAGE },
|
|
4224
|
+
rows: [new import_docx.TableRow({ children: cells })]
|
|
4225
|
+
})
|
|
4226
|
+
);
|
|
4227
|
+
docElements.push(new import_docx.Paragraph({ spacing: { after: 120 } }));
|
|
4228
|
+
continue;
|
|
4229
|
+
}
|
|
4230
|
+
}
|
|
4231
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
4232
|
+
docElements.push(
|
|
4233
|
+
new import_docx.Paragraph({
|
|
4234
|
+
border: {
|
|
4235
|
+
top: { style: import_docx.BorderStyle.SINGLE, size: 4, color: borderHex, space: 8 }
|
|
4236
|
+
},
|
|
4237
|
+
spacing: { before: 360, after: 120 }
|
|
4238
|
+
})
|
|
4239
|
+
);
|
|
4240
|
+
for (const def of doc.footnoteDefs) {
|
|
4241
|
+
const defRuns = await convertInlinesToTextRuns(def.inlines, baseDir, {
|
|
4242
|
+
font: defaultFont,
|
|
4243
|
+
size: 18,
|
|
4244
|
+
// 9pt
|
|
4245
|
+
color: textMutedHex
|
|
4246
|
+
});
|
|
4247
|
+
docElements.push(
|
|
4248
|
+
new import_docx.Paragraph({
|
|
4249
|
+
children: [
|
|
4250
|
+
new import_docx.TextRun({
|
|
4251
|
+
text: `[${def.id}] `,
|
|
4252
|
+
bold: true,
|
|
4253
|
+
color: primaryDarkHex,
|
|
4254
|
+
font: defaultFont,
|
|
4255
|
+
size: 18
|
|
4256
|
+
}),
|
|
4257
|
+
...defRuns
|
|
4258
|
+
],
|
|
4259
|
+
spacing: { after: 60 }
|
|
4260
|
+
})
|
|
4261
|
+
);
|
|
4262
|
+
}
|
|
4263
|
+
}
|
|
4264
|
+
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
4265
|
+
const sig = resolved.signatures;
|
|
4266
|
+
const numItems = sig.items.length;
|
|
4267
|
+
const contentWidth = Math.max(
|
|
4268
|
+
1e3,
|
|
4269
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4270
|
+
);
|
|
4271
|
+
docElements.push(new import_docx.Paragraph({ spacing: { before: sig.spacingBeforeTwip } }));
|
|
4272
|
+
const sigCells = [];
|
|
3038
4273
|
const colWidths = [];
|
|
3039
4274
|
if (numItems === 1) {
|
|
3040
4275
|
const cardWidth = Math.min(3400, Math.floor(contentWidth * 0.42));
|
|
@@ -3086,7 +4321,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3086
4321
|
const centerPos = Math.round(contentWidthTwip / 2);
|
|
3087
4322
|
const rightPos = contentWidthTwip;
|
|
3088
4323
|
const headerRuns = [];
|
|
3089
|
-
if ((
|
|
4324
|
+
if ((_f = resolved.header) == null ? void 0 : _f.left) {
|
|
3090
4325
|
headerRuns.push(
|
|
3091
4326
|
new import_docx.TextRun({
|
|
3092
4327
|
text: resolved.header.left.text,
|
|
@@ -3099,7 +4334,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3099
4334
|
);
|
|
3100
4335
|
}
|
|
3101
4336
|
headerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3102
|
-
if ((
|
|
4337
|
+
if ((_g = resolved.header) == null ? void 0 : _g.center) {
|
|
3103
4338
|
headerRuns.push(
|
|
3104
4339
|
new import_docx.TextRun({
|
|
3105
4340
|
text: resolved.header.center.text,
|
|
@@ -3112,7 +4347,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3112
4347
|
);
|
|
3113
4348
|
}
|
|
3114
4349
|
headerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3115
|
-
if ((
|
|
4350
|
+
if ((_h = resolved.header) == null ? void 0 : _h.right) {
|
|
3116
4351
|
headerRuns.push(
|
|
3117
4352
|
new import_docx.TextRun({
|
|
3118
4353
|
text: resolved.header.right.text,
|
|
@@ -3151,7 +4386,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3151
4386
|
]
|
|
3152
4387
|
}) : void 0;
|
|
3153
4388
|
const footerRuns = [];
|
|
3154
|
-
if ((
|
|
4389
|
+
if ((_i = resolved.footer) == null ? void 0 : _i.left) {
|
|
3155
4390
|
footerRuns.push(
|
|
3156
4391
|
new import_docx.TextRun({
|
|
3157
4392
|
text: resolved.footer.left.text,
|
|
@@ -3164,7 +4399,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3164
4399
|
);
|
|
3165
4400
|
}
|
|
3166
4401
|
footerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3167
|
-
if ((
|
|
4402
|
+
if ((_j = resolved.footer) == null ? void 0 : _j.center) {
|
|
3168
4403
|
footerRuns.push(
|
|
3169
4404
|
new import_docx.TextRun({
|
|
3170
4405
|
text: resolved.footer.center.text,
|
|
@@ -3177,7 +4412,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3177
4412
|
);
|
|
3178
4413
|
}
|
|
3179
4414
|
footerRuns.push(new import_docx.TextRun({ text: " " }));
|
|
3180
|
-
if ((
|
|
4415
|
+
if ((_k = resolved.footer) == null ? void 0 : _k.right) {
|
|
3181
4416
|
const rZone = resolved.footer.right;
|
|
3182
4417
|
const rColor = rZone.color.replace("#", "");
|
|
3183
4418
|
const rSize = (rZone.fontSize || 9) * 2;
|
|
@@ -3262,6 +4497,95 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3262
4497
|
]
|
|
3263
4498
|
}) : void 0;
|
|
3264
4499
|
const isLandscape = resolved.orientation === "landscape";
|
|
4500
|
+
const docSections = [];
|
|
4501
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
4502
|
+
const coverElements = await buildDocxCoverPageElements(
|
|
4503
|
+
resolved.coverPage,
|
|
4504
|
+
defaultFont,
|
|
4505
|
+
textHex,
|
|
4506
|
+
primaryHex,
|
|
4507
|
+
primaryDarkHex,
|
|
4508
|
+
textMutedHex,
|
|
4509
|
+
baseDir
|
|
4510
|
+
);
|
|
4511
|
+
docSections.push({
|
|
4512
|
+
properties: {
|
|
4513
|
+
page: {
|
|
4514
|
+
size: {
|
|
4515
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4516
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4517
|
+
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
4518
|
+
},
|
|
4519
|
+
margin: {
|
|
4520
|
+
top: resolved.margins.topTwip,
|
|
4521
|
+
bottom: resolved.margins.bottomTwip,
|
|
4522
|
+
left: resolved.margins.leftTwip,
|
|
4523
|
+
right: resolved.margins.rightTwip
|
|
4524
|
+
}
|
|
4525
|
+
}
|
|
4526
|
+
},
|
|
4527
|
+
headers: void 0,
|
|
4528
|
+
footers: void 0,
|
|
4529
|
+
children: coverElements
|
|
4530
|
+
});
|
|
4531
|
+
}
|
|
4532
|
+
docSections.push({
|
|
4533
|
+
properties: {
|
|
4534
|
+
page: {
|
|
4535
|
+
pageNumbers: {
|
|
4536
|
+
start: 1,
|
|
4537
|
+
formatType: import_docx.NumberFormat.DECIMAL
|
|
4538
|
+
},
|
|
4539
|
+
size: {
|
|
4540
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4541
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4542
|
+
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
4543
|
+
},
|
|
4544
|
+
margin: {
|
|
4545
|
+
top: resolved.margins.topTwip,
|
|
4546
|
+
bottom: resolved.margins.bottomTwip,
|
|
4547
|
+
left: resolved.margins.leftTwip,
|
|
4548
|
+
right: resolved.margins.rightTwip,
|
|
4549
|
+
header: 720,
|
|
4550
|
+
footer: 720
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
},
|
|
4554
|
+
headers: docHeader ? { default: docHeader } : void 0,
|
|
4555
|
+
footers: docFooter ? { default: docFooter } : void 0,
|
|
4556
|
+
children: docElements
|
|
4557
|
+
});
|
|
4558
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
4559
|
+
const backElements = await buildDocxBackCoverElements(
|
|
4560
|
+
resolved.backCover,
|
|
4561
|
+
defaultFont,
|
|
4562
|
+
textHex,
|
|
4563
|
+
primaryHex,
|
|
4564
|
+
primaryDarkHex,
|
|
4565
|
+
textMutedHex,
|
|
4566
|
+
baseDir
|
|
4567
|
+
);
|
|
4568
|
+
docSections.push({
|
|
4569
|
+
properties: {
|
|
4570
|
+
page: {
|
|
4571
|
+
size: {
|
|
4572
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4573
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4574
|
+
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
4575
|
+
},
|
|
4576
|
+
margin: {
|
|
4577
|
+
top: resolved.margins.topTwip,
|
|
4578
|
+
bottom: resolved.margins.bottomTwip,
|
|
4579
|
+
left: resolved.margins.leftTwip,
|
|
4580
|
+
right: resolved.margins.rightTwip
|
|
4581
|
+
}
|
|
4582
|
+
}
|
|
4583
|
+
},
|
|
4584
|
+
headers: void 0,
|
|
4585
|
+
footers: void 0,
|
|
4586
|
+
children: backElements
|
|
4587
|
+
});
|
|
4588
|
+
}
|
|
3265
4589
|
const document = new import_docx.Document({
|
|
3266
4590
|
styles: {
|
|
3267
4591
|
default: {
|
|
@@ -3282,33 +4606,250 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3282
4606
|
}
|
|
3283
4607
|
}
|
|
3284
4608
|
},
|
|
3285
|
-
sections:
|
|
3286
|
-
{
|
|
3287
|
-
properties: {
|
|
3288
|
-
page: {
|
|
3289
|
-
size: {
|
|
3290
|
-
width: resolved.paperDimensions.widthTwip,
|
|
3291
|
-
height: resolved.paperDimensions.heightTwip,
|
|
3292
|
-
orientation: isLandscape ? import_docx.PageOrientation.LANDSCAPE : import_docx.PageOrientation.PORTRAIT
|
|
3293
|
-
},
|
|
3294
|
-
margin: {
|
|
3295
|
-
top: resolved.margins.topTwip,
|
|
3296
|
-
bottom: resolved.margins.bottomTwip,
|
|
3297
|
-
left: resolved.margins.leftTwip,
|
|
3298
|
-
right: resolved.margins.rightTwip,
|
|
3299
|
-
header: 720,
|
|
3300
|
-
footer: 720
|
|
3301
|
-
}
|
|
3302
|
-
}
|
|
3303
|
-
},
|
|
3304
|
-
headers: docHeader ? { default: docHeader } : void 0,
|
|
3305
|
-
footers: docFooter ? { default: docFooter } : void 0,
|
|
3306
|
-
children: docElements
|
|
3307
|
-
}
|
|
3308
|
-
]
|
|
4609
|
+
sections: docSections
|
|
3309
4610
|
});
|
|
3310
4611
|
return await import_docx.Packer.toBuffer(document);
|
|
3311
4612
|
}
|
|
4613
|
+
async function buildDocxBackCoverElements(backCover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4614
|
+
var _a;
|
|
4615
|
+
const elements = [];
|
|
4616
|
+
elements.push(new import_docx.Paragraph({ spacing: { before: 1800 } }));
|
|
4617
|
+
if (backCover.logo) {
|
|
4618
|
+
const resolvedLogo = await resolveImage(backCover.logo, baseDir);
|
|
4619
|
+
if (resolvedLogo) {
|
|
4620
|
+
const logoW = typeof backCover.logoWidth === "number" ? backCover.logoWidth : 140;
|
|
4621
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4622
|
+
elements.push(
|
|
4623
|
+
new import_docx.Paragraph({
|
|
4624
|
+
children: [
|
|
4625
|
+
new import_docx.ImageRun({
|
|
4626
|
+
data: resolvedLogo.buffer,
|
|
4627
|
+
transformation: {
|
|
4628
|
+
width: logoW,
|
|
4629
|
+
height: Math.round(logoW * 0.75)
|
|
4630
|
+
},
|
|
4631
|
+
type: logoType
|
|
4632
|
+
})
|
|
4633
|
+
],
|
|
4634
|
+
spacing: { after: 240 }
|
|
4635
|
+
})
|
|
4636
|
+
);
|
|
4637
|
+
}
|
|
4638
|
+
}
|
|
4639
|
+
if (backCover.badge) {
|
|
4640
|
+
elements.push(
|
|
4641
|
+
new import_docx.Paragraph({
|
|
4642
|
+
children: [
|
|
4643
|
+
new import_docx.TextRun({
|
|
4644
|
+
text: `[ ${backCover.badge.toUpperCase()} ]`,
|
|
4645
|
+
font: defaultFont,
|
|
4646
|
+
size: 20,
|
|
4647
|
+
bold: true,
|
|
4648
|
+
color: primaryDarkHex
|
|
4649
|
+
})
|
|
4650
|
+
],
|
|
4651
|
+
spacing: { after: 240 }
|
|
4652
|
+
})
|
|
4653
|
+
);
|
|
4654
|
+
}
|
|
4655
|
+
elements.push(
|
|
4656
|
+
new import_docx.Paragraph({
|
|
4657
|
+
children: [
|
|
4658
|
+
new import_docx.TextRun({
|
|
4659
|
+
text: backCover.title,
|
|
4660
|
+
font: defaultFont,
|
|
4661
|
+
size: 52,
|
|
4662
|
+
// 26pt
|
|
4663
|
+
bold: true,
|
|
4664
|
+
color: textHex
|
|
4665
|
+
})
|
|
4666
|
+
],
|
|
4667
|
+
spacing: { after: 140 }
|
|
4668
|
+
})
|
|
4669
|
+
);
|
|
4670
|
+
if (backCover.subtitle) {
|
|
4671
|
+
elements.push(
|
|
4672
|
+
new import_docx.Paragraph({
|
|
4673
|
+
children: [
|
|
4674
|
+
new import_docx.TextRun({
|
|
4675
|
+
text: backCover.subtitle,
|
|
4676
|
+
font: defaultFont,
|
|
4677
|
+
size: 24,
|
|
4678
|
+
// 12pt
|
|
4679
|
+
color: textMutedHex
|
|
4680
|
+
})
|
|
4681
|
+
],
|
|
4682
|
+
spacing: { after: 480 }
|
|
4683
|
+
})
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
elements.push(
|
|
4687
|
+
new import_docx.Paragraph({
|
|
4688
|
+
border: {
|
|
4689
|
+
bottom: { style: import_docx.BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4690
|
+
},
|
|
4691
|
+
spacing: { after: 480 }
|
|
4692
|
+
})
|
|
4693
|
+
);
|
|
4694
|
+
const contactRuns = [];
|
|
4695
|
+
if (backCover.company) contactRuns.push(new import_docx.TextRun({ text: `Organization: ${backCover.company}
|
|
4696
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4697
|
+
if (backCover.address) contactRuns.push(new import_docx.TextRun({ text: `Address: ${backCover.address}
|
|
4698
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4699
|
+
if (backCover.email) contactRuns.push(new import_docx.TextRun({ text: `Email: ${backCover.email}
|
|
4700
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4701
|
+
if (backCover.phone) contactRuns.push(new import_docx.TextRun({ text: `Phone: ${backCover.phone}
|
|
4702
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4703
|
+
if (backCover.website) contactRuns.push(new import_docx.TextRun({ text: `Website: ${backCover.website}
|
|
4704
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4705
|
+
if (backCover.social) {
|
|
4706
|
+
for (const [net, url] of Object.entries(backCover.social)) {
|
|
4707
|
+
if (url) {
|
|
4708
|
+
contactRuns.push(new import_docx.TextRun({ text: `${net.toUpperCase()}: ${url}
|
|
4709
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4710
|
+
}
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
if (contactRuns.length > 0) {
|
|
4714
|
+
elements.push(
|
|
4715
|
+
new import_docx.Paragraph({
|
|
4716
|
+
children: contactRuns,
|
|
4717
|
+
spacing: { before: 360, after: 360 }
|
|
4718
|
+
})
|
|
4719
|
+
);
|
|
4720
|
+
}
|
|
4721
|
+
if (backCover.copyright) {
|
|
4722
|
+
elements.push(
|
|
4723
|
+
new import_docx.Paragraph({
|
|
4724
|
+
children: [
|
|
4725
|
+
new import_docx.TextRun({
|
|
4726
|
+
text: backCover.copyright,
|
|
4727
|
+
font: defaultFont,
|
|
4728
|
+
size: 18,
|
|
4729
|
+
color: "94A3B8"
|
|
4730
|
+
})
|
|
4731
|
+
],
|
|
4732
|
+
spacing: { before: 720 }
|
|
4733
|
+
})
|
|
4734
|
+
);
|
|
4735
|
+
}
|
|
4736
|
+
return elements;
|
|
4737
|
+
}
|
|
4738
|
+
async function buildDocxCoverPageElements(cover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4739
|
+
var _a;
|
|
4740
|
+
const elements = [];
|
|
4741
|
+
elements.push(new import_docx.Paragraph({ spacing: { before: 1800 } }));
|
|
4742
|
+
if (cover.logo) {
|
|
4743
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
4744
|
+
if (resolvedLogo) {
|
|
4745
|
+
const logoW = typeof cover.logoWidth === "number" ? cover.logoWidth : 140;
|
|
4746
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4747
|
+
elements.push(
|
|
4748
|
+
new import_docx.Paragraph({
|
|
4749
|
+
children: [
|
|
4750
|
+
new import_docx.ImageRun({
|
|
4751
|
+
data: resolvedLogo.buffer,
|
|
4752
|
+
transformation: {
|
|
4753
|
+
width: logoW,
|
|
4754
|
+
height: Math.round(logoW * 0.75)
|
|
4755
|
+
},
|
|
4756
|
+
type: logoType
|
|
4757
|
+
})
|
|
4758
|
+
],
|
|
4759
|
+
spacing: { after: 240 }
|
|
4760
|
+
})
|
|
4761
|
+
);
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
if (cover.badge) {
|
|
4765
|
+
elements.push(
|
|
4766
|
+
new import_docx.Paragraph({
|
|
4767
|
+
children: [
|
|
4768
|
+
new import_docx.TextRun({
|
|
4769
|
+
text: `[ ${cover.badge.toUpperCase()} ]`,
|
|
4770
|
+
font: defaultFont,
|
|
4771
|
+
size: 20,
|
|
4772
|
+
bold: true,
|
|
4773
|
+
color: primaryDarkHex
|
|
4774
|
+
})
|
|
4775
|
+
],
|
|
4776
|
+
spacing: { after: 240 }
|
|
4777
|
+
})
|
|
4778
|
+
);
|
|
4779
|
+
}
|
|
4780
|
+
elements.push(
|
|
4781
|
+
new import_docx.Paragraph({
|
|
4782
|
+
children: [
|
|
4783
|
+
new import_docx.TextRun({
|
|
4784
|
+
text: cover.title,
|
|
4785
|
+
font: defaultFont,
|
|
4786
|
+
size: 56,
|
|
4787
|
+
// 28pt
|
|
4788
|
+
bold: true,
|
|
4789
|
+
color: textHex
|
|
4790
|
+
})
|
|
4791
|
+
],
|
|
4792
|
+
spacing: { after: 140 }
|
|
4793
|
+
})
|
|
4794
|
+
);
|
|
4795
|
+
if (cover.subtitle) {
|
|
4796
|
+
elements.push(
|
|
4797
|
+
new import_docx.Paragraph({
|
|
4798
|
+
children: [
|
|
4799
|
+
new import_docx.TextRun({
|
|
4800
|
+
text: cover.subtitle,
|
|
4801
|
+
font: defaultFont,
|
|
4802
|
+
size: 26,
|
|
4803
|
+
// 13pt
|
|
4804
|
+
color: textMutedHex
|
|
4805
|
+
})
|
|
4806
|
+
],
|
|
4807
|
+
spacing: { after: 480 }
|
|
4808
|
+
})
|
|
4809
|
+
);
|
|
4810
|
+
}
|
|
4811
|
+
elements.push(
|
|
4812
|
+
new import_docx.Paragraph({
|
|
4813
|
+
border: {
|
|
4814
|
+
bottom: { style: import_docx.BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4815
|
+
},
|
|
4816
|
+
spacing: { after: 480 }
|
|
4817
|
+
})
|
|
4818
|
+
);
|
|
4819
|
+
if (cover.company || cover.author || cover.version || cover.date) {
|
|
4820
|
+
const metaRuns = [];
|
|
4821
|
+
if (cover.company) metaRuns.push(new import_docx.TextRun({ text: `Organization: ${cover.company}
|
|
4822
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4823
|
+
if (cover.author) metaRuns.push(new import_docx.TextRun({ text: `Author: ${cover.author}
|
|
4824
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4825
|
+
if (cover.version) metaRuns.push(new import_docx.TextRun({ text: `Version: ${cover.version}
|
|
4826
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4827
|
+
if (cover.date) metaRuns.push(new import_docx.TextRun({ text: `Date: ${cover.date}
|
|
4828
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4829
|
+
elements.push(
|
|
4830
|
+
new import_docx.Paragraph({
|
|
4831
|
+
children: metaRuns,
|
|
4832
|
+
spacing: { before: 360, after: 360 }
|
|
4833
|
+
})
|
|
4834
|
+
);
|
|
4835
|
+
}
|
|
4836
|
+
if (cover.footerText) {
|
|
4837
|
+
elements.push(
|
|
4838
|
+
new import_docx.Paragraph({
|
|
4839
|
+
children: [
|
|
4840
|
+
new import_docx.TextRun({
|
|
4841
|
+
text: cover.footerText,
|
|
4842
|
+
font: defaultFont,
|
|
4843
|
+
size: 18,
|
|
4844
|
+
color: "94A3B8"
|
|
4845
|
+
})
|
|
4846
|
+
],
|
|
4847
|
+
spacing: { before: 720 }
|
|
4848
|
+
})
|
|
4849
|
+
);
|
|
4850
|
+
}
|
|
4851
|
+
return elements;
|
|
4852
|
+
}
|
|
3312
4853
|
async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
|
|
3313
4854
|
const cellParagraphs = [];
|
|
3314
4855
|
if (item.title) {
|
|
@@ -3536,14 +5077,885 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
|
|
|
3536
5077
|
};
|
|
3537
5078
|
}
|
|
3538
5079
|
|
|
5080
|
+
// src/server/previewServer.ts
|
|
5081
|
+
var http = __toESM(require("http"));
|
|
5082
|
+
var fs7 = __toESM(require("fs"));
|
|
5083
|
+
var path7 = __toESM(require("path"));
|
|
5084
|
+
async function startPreviewServer(options) {
|
|
5085
|
+
const absoluteFilePath = path7.resolve(process.cwd(), options.filePath);
|
|
5086
|
+
if (!fs7.existsSync(absoluteFilePath)) {
|
|
5087
|
+
throw new Error(`MarkForge preview error: File not found at "${absoluteFilePath}"`);
|
|
5088
|
+
}
|
|
5089
|
+
const baseDir = path7.dirname(absoluteFilePath);
|
|
5090
|
+
const { config: fileConfig } = await loadConfig(void 0, baseDir);
|
|
5091
|
+
const baseConfig = options.config || fileConfig;
|
|
5092
|
+
const port = options.port || 3e3;
|
|
5093
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
5094
|
+
const broadcastReload = () => {
|
|
5095
|
+
sseClients.forEach((client) => {
|
|
5096
|
+
try {
|
|
5097
|
+
client.write(`event: reload
|
|
5098
|
+
data: ${Date.now()}
|
|
5099
|
+
|
|
5100
|
+
`);
|
|
5101
|
+
} catch {
|
|
5102
|
+
sseClients.delete(client);
|
|
5103
|
+
}
|
|
5104
|
+
});
|
|
5105
|
+
};
|
|
5106
|
+
let debounceTimer = null;
|
|
5107
|
+
const watcher = fs7.watch(baseDir, { recursive: false }, (_event, filename) => {
|
|
5108
|
+
if (!filename) return;
|
|
5109
|
+
const changedPath = path7.resolve(baseDir, filename);
|
|
5110
|
+
if (changedPath === absoluteFilePath || filename.includes("markforge") || filename.endsWith(".css")) {
|
|
5111
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
5112
|
+
debounceTimer = setTimeout(() => {
|
|
5113
|
+
broadcastReload();
|
|
5114
|
+
}, 150);
|
|
5115
|
+
}
|
|
5116
|
+
});
|
|
5117
|
+
const server = http.createServer(async (req, res) => {
|
|
5118
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
5119
|
+
if (url.pathname === "/events") {
|
|
5120
|
+
res.writeHead(200, {
|
|
5121
|
+
"Content-Type": "text/event-stream",
|
|
5122
|
+
"Cache-Control": "no-cache, no-transform",
|
|
5123
|
+
Connection: "keep-alive"
|
|
5124
|
+
});
|
|
5125
|
+
res.write(`data: connected
|
|
5126
|
+
|
|
5127
|
+
`);
|
|
5128
|
+
sseClients.add(res);
|
|
5129
|
+
req.on("close", () => {
|
|
5130
|
+
sseClients.delete(res);
|
|
5131
|
+
});
|
|
5132
|
+
return;
|
|
5133
|
+
}
|
|
5134
|
+
if (url.pathname === "/api/file-content" && req.method === "GET") {
|
|
5135
|
+
try {
|
|
5136
|
+
const content = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5137
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5138
|
+
res.end(
|
|
5139
|
+
JSON.stringify({
|
|
5140
|
+
content,
|
|
5141
|
+
fileName: path7.basename(absoluteFilePath),
|
|
5142
|
+
filePath: absoluteFilePath
|
|
5143
|
+
})
|
|
5144
|
+
);
|
|
5145
|
+
} catch (err) {
|
|
5146
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5147
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
5148
|
+
res.end(JSON.stringify({ error: msg }));
|
|
5149
|
+
}
|
|
5150
|
+
return;
|
|
5151
|
+
}
|
|
5152
|
+
if (url.pathname === "/api/save-content" && req.method === "POST") {
|
|
5153
|
+
let body = "";
|
|
5154
|
+
req.on("data", (chunk) => {
|
|
5155
|
+
body += chunk;
|
|
5156
|
+
});
|
|
5157
|
+
req.on("end", () => {
|
|
5158
|
+
try {
|
|
5159
|
+
const parsed = JSON.parse(body);
|
|
5160
|
+
if (typeof parsed.content === "string") {
|
|
5161
|
+
fs7.writeFileSync(absoluteFilePath, parsed.content, "utf-8");
|
|
5162
|
+
broadcastReload();
|
|
5163
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5164
|
+
res.end(JSON.stringify({ success: true, savedAt: Date.now() }));
|
|
5165
|
+
} else {
|
|
5166
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
5167
|
+
res.end(JSON.stringify({ error: "Missing content field in request body" }));
|
|
5168
|
+
}
|
|
5169
|
+
} catch (err) {
|
|
5170
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5171
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
5172
|
+
res.end(JSON.stringify({ error: msg }));
|
|
5173
|
+
}
|
|
5174
|
+
});
|
|
5175
|
+
return;
|
|
5176
|
+
}
|
|
5177
|
+
if (url.pathname === "/api/export" && (req.method === "GET" || req.method === "POST")) {
|
|
5178
|
+
const format = url.searchParams.get("format") || "docx";
|
|
5179
|
+
try {
|
|
5180
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5181
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
5182
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
5183
|
+
const mergedConfig = { ...baseConfig, ...resolvedConfig };
|
|
5184
|
+
const fileBase = path7.basename(absoluteFilePath, path7.extname(absoluteFilePath));
|
|
5185
|
+
if (format === "docx") {
|
|
5186
|
+
const buffer = await buildDocxDocument(doc, mergedConfig, baseDir);
|
|
5187
|
+
res.writeHead(200, {
|
|
5188
|
+
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
5189
|
+
"Content-Disposition": `attachment; filename="${fileBase}.docx"`
|
|
5190
|
+
});
|
|
5191
|
+
res.end(buffer);
|
|
5192
|
+
return;
|
|
5193
|
+
} else if (format === "pdf") {
|
|
5194
|
+
const buffer = await buildPdfDocument(doc, mergedConfig, baseDir);
|
|
5195
|
+
res.writeHead(200, {
|
|
5196
|
+
"Content-Type": "application/pdf",
|
|
5197
|
+
"Content-Disposition": `attachment; filename="${fileBase}.pdf"`
|
|
5198
|
+
});
|
|
5199
|
+
res.end(buffer);
|
|
5200
|
+
return;
|
|
5201
|
+
} else {
|
|
5202
|
+
const html = await buildHtmlDocument(doc, mergedConfig, baseDir);
|
|
5203
|
+
res.writeHead(200, {
|
|
5204
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
5205
|
+
"Content-Disposition": `attachment; filename="${fileBase}.html"`
|
|
5206
|
+
});
|
|
5207
|
+
res.end(html);
|
|
5208
|
+
return;
|
|
5209
|
+
}
|
|
5210
|
+
} catch (err) {
|
|
5211
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5212
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
5213
|
+
res.end(`Export failed: ${msg}`);
|
|
5214
|
+
return;
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
if (url.pathname === "/document-content") {
|
|
5218
|
+
try {
|
|
5219
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5220
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
5221
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
5222
|
+
const html = await buildHtmlDocument(doc, { ...baseConfig, ...resolvedConfig }, baseDir);
|
|
5223
|
+
const injectedScript = `
|
|
5224
|
+
<script>
|
|
5225
|
+
(function() {
|
|
5226
|
+
var evtSource = new EventSource('/events');
|
|
5227
|
+
evtSource.addEventListener('reload', function() {
|
|
5228
|
+
var scrollPos = window.scrollY;
|
|
5229
|
+
sessionStorage.setItem('markforge_scroll', scrollPos);
|
|
5230
|
+
window.location.reload();
|
|
5231
|
+
});
|
|
5232
|
+
window.addEventListener('load', function() {
|
|
5233
|
+
var saved = sessionStorage.getItem('markforge_scroll');
|
|
5234
|
+
if (saved) {
|
|
5235
|
+
window.scrollTo(0, parseInt(saved, 10));
|
|
5236
|
+
}
|
|
5237
|
+
});
|
|
5238
|
+
})();
|
|
5239
|
+
</script>
|
|
5240
|
+
`;
|
|
5241
|
+
const finalHtml = html.replace("</body>", `${injectedScript}</body>`);
|
|
5242
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5243
|
+
res.end(finalHtml);
|
|
5244
|
+
} catch (err) {
|
|
5245
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5246
|
+
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
5247
|
+
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>`);
|
|
5248
|
+
}
|
|
5249
|
+
return;
|
|
5250
|
+
}
|
|
5251
|
+
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
5252
|
+
const fileName = path7.basename(absoluteFilePath);
|
|
5253
|
+
const initialContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5254
|
+
const appHtml = `<!DOCTYPE html>
|
|
5255
|
+
<html lang="en">
|
|
5256
|
+
<head>
|
|
5257
|
+
<meta charset="UTF-8">
|
|
5258
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5259
|
+
<title>MarkForge Live Studio - ${escapeHtml2(fileName)}</title>
|
|
5260
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
5261
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
5262
|
+
<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">
|
|
5263
|
+
<style>
|
|
5264
|
+
:root {
|
|
5265
|
+
--mf-primary: #0D998D;
|
|
5266
|
+
--mf-primary-dark: #008277;
|
|
5267
|
+
--mf-primary-light: #ECFDFD;
|
|
5268
|
+
--mf-primary-border: #33CDCF;
|
|
5269
|
+
--mf-dark: #0F172A;
|
|
5270
|
+
--mf-slate: #1E293B;
|
|
5271
|
+
--mf-editor-bg: #0F172A;
|
|
5272
|
+
--mf-editor-gutter: #1E293B;
|
|
5273
|
+
--mf-editor-text: #F8FAFC;
|
|
5274
|
+
--mf-muted: #64748B;
|
|
5275
|
+
--mf-light-border: #E2E8F0;
|
|
5276
|
+
--mf-bg: #F1F5F9;
|
|
5277
|
+
}
|
|
5278
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5279
|
+
body {
|
|
5280
|
+
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
5281
|
+
background: var(--mf-bg);
|
|
5282
|
+
color: var(--mf-dark);
|
|
5283
|
+
display: flex;
|
|
5284
|
+
flex-direction: column;
|
|
5285
|
+
height: 100vh;
|
|
5286
|
+
overflow: hidden;
|
|
5287
|
+
}
|
|
5288
|
+
header {
|
|
5289
|
+
background: #FFFFFF;
|
|
5290
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5291
|
+
min-height: 56px;
|
|
5292
|
+
display: flex;
|
|
5293
|
+
flex-wrap: wrap;
|
|
5294
|
+
align-items: center;
|
|
5295
|
+
justify-content: space-between;
|
|
5296
|
+
padding: 0.4rem 1.2rem;
|
|
5297
|
+
z-index: 10;
|
|
5298
|
+
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
|
5299
|
+
gap: 0.75rem;
|
|
5300
|
+
}
|
|
5301
|
+
.brand-section {
|
|
5302
|
+
display: flex;
|
|
5303
|
+
align-items: center;
|
|
5304
|
+
gap: 0.75rem;
|
|
5305
|
+
}
|
|
5306
|
+
.brand-badge {
|
|
5307
|
+
font-size: 0.72rem;
|
|
5308
|
+
font-weight: 800;
|
|
5309
|
+
letter-spacing: 0.08em;
|
|
5310
|
+
background: var(--mf-dark);
|
|
5311
|
+
color: #FFFFFF;
|
|
5312
|
+
padding: 0.25rem 0.55rem;
|
|
5313
|
+
border-radius: 4px;
|
|
5314
|
+
text-transform: uppercase;
|
|
5315
|
+
}
|
|
5316
|
+
.file-name {
|
|
5317
|
+
font-size: 0.9rem;
|
|
5318
|
+
font-weight: 700;
|
|
5319
|
+
color: var(--mf-dark);
|
|
5320
|
+
}
|
|
5321
|
+
.sync-status {
|
|
5322
|
+
display: flex;
|
|
5323
|
+
align-items: center;
|
|
5324
|
+
gap: 0.35rem;
|
|
5325
|
+
font-size: 0.75rem;
|
|
5326
|
+
font-weight: 600;
|
|
5327
|
+
color: var(--mf-primary-dark);
|
|
5328
|
+
background: var(--mf-primary-light);
|
|
5329
|
+
padding: 0.2rem 0.55rem;
|
|
5330
|
+
border-radius: 9999px;
|
|
5331
|
+
border: 1px solid var(--mf-primary-border);
|
|
5332
|
+
}
|
|
5333
|
+
.sync-dot {
|
|
5334
|
+
width: 7px;
|
|
5335
|
+
height: 7px;
|
|
5336
|
+
background-color: var(--mf-primary);
|
|
5337
|
+
border-radius: 50%;
|
|
5338
|
+
box-shadow: 0 0 0 2px rgba(13, 153, 141, 0.2);
|
|
5339
|
+
}
|
|
5340
|
+
.toolbar-section {
|
|
5341
|
+
display: flex;
|
|
5342
|
+
align-items: center;
|
|
5343
|
+
gap: 0.3rem;
|
|
5344
|
+
background: #F8FAFC;
|
|
5345
|
+
padding: 0.25rem 0.4rem;
|
|
5346
|
+
border-radius: 6px;
|
|
5347
|
+
border: 1px solid var(--mf-light-border);
|
|
5348
|
+
}
|
|
5349
|
+
.tool-btn {
|
|
5350
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5351
|
+
font-size: 0.75rem;
|
|
5352
|
+
font-weight: 600;
|
|
5353
|
+
padding: 0.25rem 0.45rem;
|
|
5354
|
+
background: transparent;
|
|
5355
|
+
border: 1px solid transparent;
|
|
5356
|
+
border-radius: 4px;
|
|
5357
|
+
cursor: pointer;
|
|
5358
|
+
color: var(--mf-slate);
|
|
5359
|
+
transition: all 0.1s ease;
|
|
5360
|
+
}
|
|
5361
|
+
.tool-btn:hover {
|
|
5362
|
+
background: #FFFFFF;
|
|
5363
|
+
border-color: var(--mf-light-border);
|
|
5364
|
+
color: var(--mf-primary-dark);
|
|
5365
|
+
}
|
|
5366
|
+
.tool-divider {
|
|
5367
|
+
width: 1px;
|
|
5368
|
+
height: 16px;
|
|
5369
|
+
background: var(--mf-light-border);
|
|
5370
|
+
margin: 0 0.15rem;
|
|
5371
|
+
}
|
|
5372
|
+
.controls {
|
|
5373
|
+
display: flex;
|
|
5374
|
+
align-items: center;
|
|
5375
|
+
gap: 0.5rem;
|
|
5376
|
+
}
|
|
5377
|
+
.view-toggles {
|
|
5378
|
+
display: flex;
|
|
5379
|
+
background: #F1F5F9;
|
|
5380
|
+
padding: 2px;
|
|
5381
|
+
border-radius: 6px;
|
|
5382
|
+
border: 1px solid var(--mf-light-border);
|
|
5383
|
+
}
|
|
5384
|
+
.toggle-btn {
|
|
5385
|
+
font-family: inherit;
|
|
5386
|
+
font-size: 0.74rem;
|
|
5387
|
+
font-weight: 600;
|
|
5388
|
+
padding: 0.25rem 0.55rem;
|
|
5389
|
+
border: none;
|
|
5390
|
+
background: transparent;
|
|
5391
|
+
border-radius: 4px;
|
|
5392
|
+
cursor: pointer;
|
|
5393
|
+
color: var(--mf-muted);
|
|
5394
|
+
transition: all 0.15s ease;
|
|
5395
|
+
}
|
|
5396
|
+
.toggle-btn.active {
|
|
5397
|
+
background: #FFFFFF;
|
|
5398
|
+
color: var(--mf-dark);
|
|
5399
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
|
5400
|
+
}
|
|
5401
|
+
.btn {
|
|
5402
|
+
font-family: inherit;
|
|
5403
|
+
font-size: 0.78rem;
|
|
5404
|
+
font-weight: 600;
|
|
5405
|
+
padding: 0.35rem 0.75rem;
|
|
5406
|
+
border-radius: 6px;
|
|
5407
|
+
cursor: pointer;
|
|
5408
|
+
text-decoration: none;
|
|
5409
|
+
transition: all 0.15s ease;
|
|
5410
|
+
display: inline-flex;
|
|
5411
|
+
align-items: center;
|
|
5412
|
+
gap: 0.3rem;
|
|
5413
|
+
border: 1px solid var(--mf-light-border);
|
|
5414
|
+
background: #FFFFFF;
|
|
5415
|
+
color: var(--mf-dark);
|
|
5416
|
+
}
|
|
5417
|
+
.btn:hover {
|
|
5418
|
+
background: #F8FAFC;
|
|
5419
|
+
border-color: #CBD5E1;
|
|
5420
|
+
}
|
|
5421
|
+
.btn-primary {
|
|
5422
|
+
background: var(--mf-primary);
|
|
5423
|
+
color: #FFFFFF;
|
|
5424
|
+
border-color: var(--mf-primary);
|
|
5425
|
+
}
|
|
5426
|
+
.btn-primary:hover {
|
|
5427
|
+
background: var(--mf-primary-dark);
|
|
5428
|
+
border-color: var(--mf-primary-dark);
|
|
5429
|
+
}
|
|
5430
|
+
.save-indicator {
|
|
5431
|
+
font-size: 0.75rem;
|
|
5432
|
+
font-weight: 600;
|
|
5433
|
+
color: var(--mf-muted);
|
|
5434
|
+
min-width: 65px;
|
|
5435
|
+
text-align: right;
|
|
5436
|
+
}
|
|
5437
|
+
.save-indicator.saved {
|
|
5438
|
+
color: var(--mf-primary-dark);
|
|
5439
|
+
}
|
|
5440
|
+
.save-indicator.saving {
|
|
5441
|
+
color: #D97706;
|
|
5442
|
+
}
|
|
5443
|
+
.save-indicator.unsaved {
|
|
5444
|
+
color: #E11D48;
|
|
5445
|
+
}
|
|
5446
|
+
|
|
5447
|
+
/* Main Workspace Splitter Layout */
|
|
5448
|
+
main.workspace {
|
|
5449
|
+
flex: 1;
|
|
5450
|
+
display: flex;
|
|
5451
|
+
height: calc(100vh - 56px);
|
|
5452
|
+
overflow: hidden;
|
|
5453
|
+
background: var(--mf-bg);
|
|
5454
|
+
position: relative;
|
|
5455
|
+
}
|
|
5456
|
+
.editor-pane {
|
|
5457
|
+
width: 50%;
|
|
5458
|
+
height: 100%;
|
|
5459
|
+
display: flex;
|
|
5460
|
+
flex-direction: column;
|
|
5461
|
+
background: var(--mf-editor-bg);
|
|
5462
|
+
border-right: 1px solid #334155;
|
|
5463
|
+
overflow: hidden;
|
|
5464
|
+
}
|
|
5465
|
+
.editor-header {
|
|
5466
|
+
background: #090D16;
|
|
5467
|
+
border-bottom: 1px solid #1E293B;
|
|
5468
|
+
padding: 0.4rem 0.8rem;
|
|
5469
|
+
display: flex;
|
|
5470
|
+
align-items: center;
|
|
5471
|
+
justify-content: space-between;
|
|
5472
|
+
color: #94A3B8;
|
|
5473
|
+
font-size: 0.74rem;
|
|
5474
|
+
font-weight: 500;
|
|
5475
|
+
}
|
|
5476
|
+
.editor-container {
|
|
5477
|
+
flex: 1;
|
|
5478
|
+
display: flex;
|
|
5479
|
+
position: relative;
|
|
5480
|
+
overflow: hidden;
|
|
5481
|
+
background: var(--mf-editor-bg);
|
|
5482
|
+
}
|
|
5483
|
+
.line-numbers {
|
|
5484
|
+
width: 44px;
|
|
5485
|
+
padding: 0.8rem 0.4rem;
|
|
5486
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5487
|
+
font-size: 13px;
|
|
5488
|
+
line-height: 1.55;
|
|
5489
|
+
color: #475569;
|
|
5490
|
+
text-align: right;
|
|
5491
|
+
user-select: none;
|
|
5492
|
+
background: var(--mf-editor-gutter);
|
|
5493
|
+
overflow: hidden;
|
|
5494
|
+
border-right: 1px solid #1E293B;
|
|
5495
|
+
}
|
|
5496
|
+
.code-editor {
|
|
5497
|
+
flex: 1;
|
|
5498
|
+
padding: 0.8rem 1rem;
|
|
5499
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5500
|
+
font-size: 13px;
|
|
5501
|
+
line-height: 1.55;
|
|
5502
|
+
color: var(--mf-editor-text);
|
|
5503
|
+
background: transparent;
|
|
5504
|
+
border: none;
|
|
5505
|
+
outline: none;
|
|
5506
|
+
resize: none;
|
|
5507
|
+
white-space: pre;
|
|
5508
|
+
overflow-wrap: normal;
|
|
5509
|
+
overflow: auto;
|
|
5510
|
+
tab-size: 2;
|
|
5511
|
+
}
|
|
5512
|
+
|
|
5513
|
+
/* Draggable Splitter Handle */
|
|
5514
|
+
.splitter {
|
|
5515
|
+
width: 8px;
|
|
5516
|
+
cursor: col-resize;
|
|
5517
|
+
background: #E2E8F0;
|
|
5518
|
+
transition: background 0.15s ease;
|
|
5519
|
+
position: relative;
|
|
5520
|
+
z-index: 5;
|
|
5521
|
+
}
|
|
5522
|
+
.splitter:hover, .splitter.active {
|
|
5523
|
+
background: var(--mf-primary);
|
|
5524
|
+
}
|
|
5525
|
+
|
|
5526
|
+
/* Right Preview Pane */
|
|
5527
|
+
.preview-pane {
|
|
5528
|
+
width: 50%;
|
|
5529
|
+
height: 100%;
|
|
5530
|
+
display: flex;
|
|
5531
|
+
flex-direction: column;
|
|
5532
|
+
background: #FFFFFF;
|
|
5533
|
+
overflow: hidden;
|
|
5534
|
+
}
|
|
5535
|
+
.preview-header {
|
|
5536
|
+
background: #FFFFFF;
|
|
5537
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5538
|
+
padding: 0.35rem 0.8rem;
|
|
5539
|
+
display: flex;
|
|
5540
|
+
align-items: center;
|
|
5541
|
+
justify-content: space-between;
|
|
5542
|
+
color: var(--mf-muted);
|
|
5543
|
+
font-size: 0.74rem;
|
|
5544
|
+
font-weight: 600;
|
|
5545
|
+
}
|
|
5546
|
+
.viewport-selector {
|
|
5547
|
+
display: flex;
|
|
5548
|
+
gap: 0.25rem;
|
|
5549
|
+
}
|
|
5550
|
+
.vp-btn {
|
|
5551
|
+
font-size: 0.72rem;
|
|
5552
|
+
padding: 0.15rem 0.4rem;
|
|
5553
|
+
border: 1px solid var(--mf-light-border);
|
|
5554
|
+
background: #F8FAFC;
|
|
5555
|
+
border-radius: 4px;
|
|
5556
|
+
cursor: pointer;
|
|
5557
|
+
color: var(--mf-muted);
|
|
5558
|
+
}
|
|
5559
|
+
.vp-btn.active {
|
|
5560
|
+
background: var(--mf-primary-light);
|
|
5561
|
+
color: var(--mf-primary-dark);
|
|
5562
|
+
border-color: var(--mf-primary-border);
|
|
5563
|
+
}
|
|
5564
|
+
.preview-wrapper {
|
|
5565
|
+
flex: 1;
|
|
5566
|
+
display: flex;
|
|
5567
|
+
justify-content: center;
|
|
5568
|
+
align-items: stretch;
|
|
5569
|
+
background: #F1F5F9;
|
|
5570
|
+
overflow: hidden;
|
|
5571
|
+
}
|
|
5572
|
+
iframe {
|
|
5573
|
+
width: 100%;
|
|
5574
|
+
height: 100%;
|
|
5575
|
+
border: none;
|
|
5576
|
+
background: #FFFFFF;
|
|
5577
|
+
transition: max-width 0.2s ease;
|
|
5578
|
+
}
|
|
5579
|
+
.author-footer {
|
|
5580
|
+
font-size: 0.72rem;
|
|
5581
|
+
color: var(--mf-muted);
|
|
5582
|
+
padding-right: 0.5rem;
|
|
5583
|
+
}
|
|
5584
|
+
.author-footer a {
|
|
5585
|
+
color: var(--mf-primary-dark);
|
|
5586
|
+
text-decoration: none;
|
|
5587
|
+
font-weight: 600;
|
|
5588
|
+
}
|
|
5589
|
+
</style>
|
|
5590
|
+
</head>
|
|
5591
|
+
<body>
|
|
5592
|
+
<header>
|
|
5593
|
+
<div class="brand-section">
|
|
5594
|
+
<span class="brand-badge">MARKFORGE STUDIO</span>
|
|
5595
|
+
<span class="file-name" title="${escapeHtml2(absoluteFilePath)}">${escapeHtml2(fileName)}</span>
|
|
5596
|
+
<div class="sync-status">
|
|
5597
|
+
<div class="sync-dot"></div>
|
|
5598
|
+
<span>Live Sync Active</span>
|
|
5599
|
+
</div>
|
|
5600
|
+
</div>
|
|
5601
|
+
|
|
5602
|
+
<!-- Quick Formatting Toolbar -->
|
|
5603
|
+
<div class="toolbar-section">
|
|
5604
|
+
<button class="tool-btn" onclick="insertFormat('h1')" title="Heading 1">H1</button>
|
|
5605
|
+
<button class="tool-btn" onclick="insertFormat('h2')" title="Heading 2">H2</button>
|
|
5606
|
+
<button class="tool-btn" onclick="insertFormat('h3')" title="Heading 3">H3</button>
|
|
5607
|
+
<div class="tool-divider"></div>
|
|
5608
|
+
<button class="tool-btn" onclick="insertFormat('bold')" title="Bold">B</button>
|
|
5609
|
+
<button class="tool-btn" onclick="insertFormat('italic')" title="Italic">I</button>
|
|
5610
|
+
<button class="tool-btn" onclick="insertFormat('code')" title="Inline Code"><></button>
|
|
5611
|
+
<button class="tool-btn" onclick="insertFormat('quote')" title="Blockquote">></button>
|
|
5612
|
+
<div class="tool-divider"></div>
|
|
5613
|
+
<button class="tool-btn" onclick="insertFormat('table')" title="GFM Table">Table</button>
|
|
5614
|
+
<button class="tool-btn" onclick="insertFormat('list')" title="List">List</button>
|
|
5615
|
+
<button class="tool-btn" onclick="insertFormat('task')" title="Task Checklist">Task</button>
|
|
5616
|
+
<div class="tool-divider"></div>
|
|
5617
|
+
<button class="tool-btn" onclick="insertFormat('callout')" title="Callout Box">Callout</button>
|
|
5618
|
+
<button class="tool-btn" onclick="insertFormat('math')" title="LaTeX Math">Math</button>
|
|
5619
|
+
<button class="tool-btn" onclick="insertFormat('columns')" title="Multi-Columns">Columns</button>
|
|
5620
|
+
<button class="tool-btn" onclick="insertFormat('footnote')" title="Footnote">Footnote</button>
|
|
5621
|
+
<button class="tool-btn" onclick="insertFormat('mermaid')" title="Mermaid Diagram">Mermaid</button>
|
|
5622
|
+
</div>
|
|
5623
|
+
|
|
5624
|
+
<!-- Controls & View Mode -->
|
|
5625
|
+
<div class="controls">
|
|
5626
|
+
<div class="view-toggles">
|
|
5627
|
+
<button class="toggle-btn active" id="btn-split" onclick="setViewMode('split')">Split</button>
|
|
5628
|
+
<button class="toggle-btn" id="btn-edit" onclick="setViewMode('edit')">Editor</button>
|
|
5629
|
+
<button class="toggle-btn" id="btn-prev" onclick="setViewMode('prev')">Preview</button>
|
|
5630
|
+
</div>
|
|
5631
|
+
<span class="save-indicator saved" id="save-status">Saved</span>
|
|
5632
|
+
<button class="btn btn-primary" onclick="saveContentManual()" title="Save (Ctrl+S)">Save</button>
|
|
5633
|
+
<button class="btn" onclick="exportDoc('docx')" title="Download Word Document">DOCX</button>
|
|
5634
|
+
<button class="btn" onclick="exportDoc('pdf')" title="Download PDF Document">PDF</button>
|
|
5635
|
+
<button class="btn" onclick="printDoc()" title="Print / PDF dialog">Print</button>
|
|
5636
|
+
</div>
|
|
5637
|
+
</header>
|
|
5638
|
+
|
|
5639
|
+
<main class="workspace" id="workspace">
|
|
5640
|
+
<!-- Left: Code Editor Pane -->
|
|
5641
|
+
<div class="editor-pane" id="editor-pane">
|
|
5642
|
+
<div class="editor-header">
|
|
5643
|
+
<span>MARKDOWN SOURCE</span>
|
|
5644
|
+
<span id="editor-stats">Lines: 1 | Words: 0 | UTF-8</span>
|
|
5645
|
+
</div>
|
|
5646
|
+
<div class="editor-container">
|
|
5647
|
+
<div class="line-numbers" id="line-numbers">1</div>
|
|
5648
|
+
<textarea class="code-editor" id="code-editor" spellcheck="false" placeholder="Write markdown here...">${escapeHtml2(initialContent)}</textarea>
|
|
5649
|
+
</div>
|
|
5650
|
+
</div>
|
|
5651
|
+
|
|
5652
|
+
<!-- Middle: Draggable Splitter Handle -->
|
|
5653
|
+
<div class="splitter" id="splitter"></div>
|
|
5654
|
+
|
|
5655
|
+
<!-- Right: Rendered Preview Pane -->
|
|
5656
|
+
<div class="preview-pane" id="preview-pane">
|
|
5657
|
+
<div class="preview-header">
|
|
5658
|
+
<span>RENDERED PREVIEW</span>
|
|
5659
|
+
<div class="viewport-selector">
|
|
5660
|
+
<button class="vp-btn active" onclick="setViewport('100%')" id="vp-full">100% Full</button>
|
|
5661
|
+
<button class="vp-btn" onclick="setViewport('820px')" id="vp-a4">A4 (820px)</button>
|
|
5662
|
+
<button class="vp-btn" onclick="setViewport('440px')" id="vp-mob">Mobile</button>
|
|
5663
|
+
</div>
|
|
5664
|
+
<span class="author-footer">Created by <a href="https://github.com/masumrpg" target="_blank">Ma'sum (@masumrpg)</a></span>
|
|
5665
|
+
</div>
|
|
5666
|
+
<div class="preview-wrapper">
|
|
5667
|
+
<iframe id="preview-frame" src="/document-content"></iframe>
|
|
5668
|
+
</div>
|
|
5669
|
+
</div>
|
|
5670
|
+
</main>
|
|
5671
|
+
|
|
5672
|
+
<script>
|
|
5673
|
+
var editor = document.getElementById('code-editor');
|
|
5674
|
+
var lineNumbers = document.getElementById('line-numbers');
|
|
5675
|
+
var stats = document.getElementById('editor-stats');
|
|
5676
|
+
var saveStatus = document.getElementById('save-status');
|
|
5677
|
+
var previewFrame = document.getElementById('preview-frame');
|
|
5678
|
+
var editorPane = document.getElementById('editor-pane');
|
|
5679
|
+
var previewPane = document.getElementById('preview-pane');
|
|
5680
|
+
var splitter = document.getElementById('splitter');
|
|
5681
|
+
var isDirty = false;
|
|
5682
|
+
var autoSaveTimeout = null;
|
|
5683
|
+
|
|
5684
|
+
// Update Line Numbers & Stats
|
|
5685
|
+
function updateStatsAndLines() {
|
|
5686
|
+
var lines = editor.value.split('\\n');
|
|
5687
|
+
var lineCount = lines.length;
|
|
5688
|
+
var numHtml = '';
|
|
5689
|
+
for (var i = 1; i <= lineCount; i++) {
|
|
5690
|
+
numHtml += i + '<br>';
|
|
5691
|
+
}
|
|
5692
|
+
lineNumbers.innerHTML = numHtml;
|
|
5693
|
+
|
|
5694
|
+
var words = editor.value.trim().length > 0 ? editor.value.trim().split(/\\s+/).length : 0;
|
|
5695
|
+
var chars = editor.value.length;
|
|
5696
|
+
stats.textContent = 'Lines: ' + lineCount + ' | Words: ' + words + ' | Chars: ' + chars + ' | UTF-8';
|
|
5697
|
+
}
|
|
5698
|
+
|
|
5699
|
+
// Synchronize vertical scroll between Line Numbers and Textarea
|
|
5700
|
+
editor.addEventListener('scroll', function() {
|
|
5701
|
+
lineNumbers.scrollTop = editor.scrollTop;
|
|
5702
|
+
});
|
|
5703
|
+
|
|
5704
|
+
// Handle Input & Debounced Auto-Save
|
|
5705
|
+
editor.addEventListener('input', function() {
|
|
5706
|
+
updateStatsAndLines();
|
|
5707
|
+
setSaveState('unsaved');
|
|
5708
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5709
|
+
autoSaveTimeout = setTimeout(function() {
|
|
5710
|
+
saveContent();
|
|
5711
|
+
}, 600);
|
|
5712
|
+
});
|
|
5713
|
+
|
|
5714
|
+
function setSaveState(state) {
|
|
5715
|
+
if (state === 'saved') {
|
|
5716
|
+
saveStatus.textContent = 'Saved';
|
|
5717
|
+
saveStatus.className = 'save-indicator saved';
|
|
5718
|
+
isDirty = false;
|
|
5719
|
+
} else if (state === 'saving') {
|
|
5720
|
+
saveStatus.textContent = 'Saving...';
|
|
5721
|
+
saveStatus.className = 'save-indicator saving';
|
|
5722
|
+
} else {
|
|
5723
|
+
saveStatus.textContent = 'Changes...';
|
|
5724
|
+
saveStatus.className = 'save-indicator unsaved';
|
|
5725
|
+
isDirty = true;
|
|
5726
|
+
}
|
|
5727
|
+
}
|
|
5728
|
+
|
|
5729
|
+
// Save Content via API
|
|
5730
|
+
function saveContent(callback) {
|
|
5731
|
+
setSaveState('saving');
|
|
5732
|
+
fetch('/api/save-content', {
|
|
5733
|
+
method: 'POST',
|
|
5734
|
+
headers: { 'Content-Type': 'application/json' },
|
|
5735
|
+
body: JSON.stringify({ content: editor.value }),
|
|
5736
|
+
})
|
|
5737
|
+
.then(function(res) { return res.json(); })
|
|
5738
|
+
.then(function(data) {
|
|
5739
|
+
if (data.success) {
|
|
5740
|
+
setSaveState('saved');
|
|
5741
|
+
if (callback) callback();
|
|
5742
|
+
} else {
|
|
5743
|
+
saveStatus.textContent = 'Save Error';
|
|
5744
|
+
}
|
|
5745
|
+
})
|
|
5746
|
+
.catch(function() {
|
|
5747
|
+
saveStatus.textContent = 'Save Error';
|
|
5748
|
+
});
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5751
|
+
function saveContentManual() {
|
|
5752
|
+
saveContent();
|
|
5753
|
+
}
|
|
5754
|
+
|
|
5755
|
+
// Keyboard Shortcuts: Tab (2 spaces), Shift+Tab, Ctrl+S
|
|
5756
|
+
editor.addEventListener('keydown', function(e) {
|
|
5757
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
5758
|
+
e.preventDefault();
|
|
5759
|
+
saveContent();
|
|
5760
|
+
return;
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5763
|
+
if (e.key === 'Tab') {
|
|
5764
|
+
e.preventDefault();
|
|
5765
|
+
var start = this.selectionStart;
|
|
5766
|
+
var end = this.selectionEnd;
|
|
5767
|
+
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
|
|
5768
|
+
this.selectionStart = this.selectionEnd = start + 2;
|
|
5769
|
+
updateStatsAndLines();
|
|
5770
|
+
setSaveState('unsaved');
|
|
5771
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5772
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5773
|
+
}
|
|
5774
|
+
});
|
|
5775
|
+
|
|
5776
|
+
// Formatting Snippet Injector
|
|
5777
|
+
function insertFormat(type) {
|
|
5778
|
+
var start = editor.selectionStart;
|
|
5779
|
+
var end = editor.selectionEnd;
|
|
5780
|
+
var selected = editor.value.substring(start, end);
|
|
5781
|
+
var replacement = '';
|
|
5782
|
+
|
|
5783
|
+
switch (type) {
|
|
5784
|
+
case 'h1': replacement = '# ' + (selected || 'Heading 1'); break;
|
|
5785
|
+
case 'h2': replacement = '## ' + (selected || 'Heading 2'); break;
|
|
5786
|
+
case 'h3': replacement = '### ' + (selected || 'Heading 3'); break;
|
|
5787
|
+
case 'bold': replacement = '**' + (selected || 'bold text') + '**'; break;
|
|
5788
|
+
case 'italic': replacement = '*' + (selected || 'italic text') + '*'; break;
|
|
5789
|
+
case 'code': replacement = '\`' + (selected || 'inline code') + '\`'; break;
|
|
5790
|
+
case 'quote': replacement = '> ' + (selected || 'Quote text'); break;
|
|
5791
|
+
case 'table':
|
|
5792
|
+
replacement = '\\n| Column 1 | Column 2 | Column 3 |\\n| :--- | :---: | ---: |\\n| Data A | Data B | Data C |\\n| Data D | Data E | Data F |\\n';
|
|
5793
|
+
break;
|
|
5794
|
+
case 'list': replacement = '- ' + (selected || 'List item'); break;
|
|
5795
|
+
case 'task': replacement = '- [ ] ' + (selected || 'Task item'); break;
|
|
5796
|
+
case 'callout':
|
|
5797
|
+
replacement = '> [!NOTE]\\n> ' + (selected || 'This is an important callout note.');
|
|
5798
|
+
break;
|
|
5799
|
+
case 'math':
|
|
5800
|
+
replacement = '$$\\n' + (selected || '\\\\int_{-\\\\infty}^{\\\\infty} e^{-x^2} dx = \\\\sqrt{\\\\pi}') + '\\n$$';
|
|
5801
|
+
break;
|
|
5802
|
+
case 'columns':
|
|
5803
|
+
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:::';
|
|
5804
|
+
break;
|
|
5805
|
+
case 'footnote':
|
|
5806
|
+
replacement = (selected || 'Statement with footnote') + '[^1]\\n\\n[^1]: Note description text.';
|
|
5807
|
+
break;
|
|
5808
|
+
case 'mermaid':
|
|
5809
|
+
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';
|
|
5810
|
+
break;
|
|
5811
|
+
}
|
|
5812
|
+
|
|
5813
|
+
editor.value = editor.value.substring(0, start) + replacement + editor.value.substring(end);
|
|
5814
|
+
editor.selectionStart = editor.selectionEnd = start + replacement.length;
|
|
5815
|
+
editor.focus();
|
|
5816
|
+
updateStatsAndLines();
|
|
5817
|
+
setSaveState('unsaved');
|
|
5818
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5819
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5820
|
+
}
|
|
5821
|
+
|
|
5822
|
+
// View Mode Toggle (Split / Editor Only / Preview Only)
|
|
5823
|
+
function setViewMode(mode) {
|
|
5824
|
+
document.getElementById('btn-split').classList.remove('active');
|
|
5825
|
+
document.getElementById('btn-edit').classList.remove('active');
|
|
5826
|
+
document.getElementById('btn-prev').classList.remove('active');
|
|
5827
|
+
|
|
5828
|
+
if (mode === 'split') {
|
|
5829
|
+
document.getElementById('btn-split').classList.add('active');
|
|
5830
|
+
editorPane.style.display = 'flex';
|
|
5831
|
+
editorPane.style.width = '50%';
|
|
5832
|
+
previewPane.style.display = 'flex';
|
|
5833
|
+
previewPane.style.width = '50%';
|
|
5834
|
+
splitter.style.display = 'block';
|
|
5835
|
+
} else if (mode === 'edit') {
|
|
5836
|
+
document.getElementById('btn-edit').classList.add('active');
|
|
5837
|
+
editorPane.style.display = 'flex';
|
|
5838
|
+
editorPane.style.width = '100%';
|
|
5839
|
+
previewPane.style.display = 'none';
|
|
5840
|
+
splitter.style.display = 'none';
|
|
5841
|
+
} else if (mode === 'prev') {
|
|
5842
|
+
document.getElementById('btn-prev').classList.add('active');
|
|
5843
|
+
editorPane.style.display = 'none';
|
|
5844
|
+
previewPane.style.display = 'flex';
|
|
5845
|
+
previewPane.style.width = '100%';
|
|
5846
|
+
splitter.style.display = 'none';
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
|
|
5850
|
+
// Viewport Width Resizer
|
|
5851
|
+
function setViewport(width) {
|
|
5852
|
+
document.getElementById('vp-full').classList.remove('active');
|
|
5853
|
+
document.getElementById('vp-a4').classList.remove('active');
|
|
5854
|
+
document.getElementById('vp-mob').classList.remove('active');
|
|
5855
|
+
|
|
5856
|
+
if (width === '100%') {
|
|
5857
|
+
document.getElementById('vp-full').classList.add('active');
|
|
5858
|
+
previewFrame.style.maxWidth = '100%';
|
|
5859
|
+
} else if (width === '820px') {
|
|
5860
|
+
document.getElementById('vp-a4').classList.add('active');
|
|
5861
|
+
previewFrame.style.maxWidth = '820px';
|
|
5862
|
+
} else if (width === '440px') {
|
|
5863
|
+
document.getElementById('vp-mob').classList.add('active');
|
|
5864
|
+
previewFrame.style.maxWidth = '440px';
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5867
|
+
|
|
5868
|
+
// Draggable Splitter Handle Logic
|
|
5869
|
+
var isDragging = false;
|
|
5870
|
+
splitter.addEventListener('mousedown', function(e) {
|
|
5871
|
+
isDragging = true;
|
|
5872
|
+
splitter.classList.add('active');
|
|
5873
|
+
document.body.style.cursor = 'col-resize';
|
|
5874
|
+
document.body.style.userSelect = 'none';
|
|
5875
|
+
});
|
|
5876
|
+
|
|
5877
|
+
window.addEventListener('mousemove', function(e) {
|
|
5878
|
+
if (!isDragging) return;
|
|
5879
|
+
var totalWidth = document.getElementById('workspace').clientWidth;
|
|
5880
|
+
var newEditorWidth = (e.clientX / totalWidth) * 100;
|
|
5881
|
+
if (newEditorWidth > 15 && newEditorWidth < 85) {
|
|
5882
|
+
editorPane.style.width = newEditorWidth + '%';
|
|
5883
|
+
previewPane.style.width = (100 - newEditorWidth) + '%';
|
|
5884
|
+
}
|
|
5885
|
+
});
|
|
5886
|
+
|
|
5887
|
+
window.addEventListener('mouseup', function() {
|
|
5888
|
+
if (isDragging) {
|
|
5889
|
+
isDragging = false;
|
|
5890
|
+
splitter.classList.remove('active');
|
|
5891
|
+
document.body.style.cursor = '';
|
|
5892
|
+
document.body.style.userSelect = '';
|
|
5893
|
+
}
|
|
5894
|
+
});
|
|
5895
|
+
|
|
5896
|
+
// Document Print Action
|
|
5897
|
+
function printDoc() {
|
|
5898
|
+
previewFrame.contentWindow.print();
|
|
5899
|
+
}
|
|
5900
|
+
|
|
5901
|
+
// Document Export Action
|
|
5902
|
+
function exportDoc(format) {
|
|
5903
|
+
saveContent(function() {
|
|
5904
|
+
window.location.href = '/api/export?format=' + format;
|
|
5905
|
+
});
|
|
5906
|
+
}
|
|
5907
|
+
|
|
5908
|
+
// Initialize line stats
|
|
5909
|
+
updateStatsAndLines();
|
|
5910
|
+
</script>
|
|
5911
|
+
</body>
|
|
5912
|
+
</html>`;
|
|
5913
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5914
|
+
res.end(appHtml);
|
|
5915
|
+
return;
|
|
5916
|
+
}
|
|
5917
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
5918
|
+
res.end("Not Found");
|
|
5919
|
+
});
|
|
5920
|
+
return new Promise((resolve6, reject) => {
|
|
5921
|
+
server.listen(port, () => {
|
|
5922
|
+
const url = `http://localhost:${port}`;
|
|
5923
|
+
resolve6({
|
|
5924
|
+
server,
|
|
5925
|
+
port,
|
|
5926
|
+
url,
|
|
5927
|
+
close: async () => {
|
|
5928
|
+
watcher.close();
|
|
5929
|
+
sseClients.forEach((client) => {
|
|
5930
|
+
try {
|
|
5931
|
+
client.end();
|
|
5932
|
+
} catch {
|
|
5933
|
+
}
|
|
5934
|
+
});
|
|
5935
|
+
sseClients.clear();
|
|
5936
|
+
return new Promise((res) => {
|
|
5937
|
+
server.close(() => res());
|
|
5938
|
+
});
|
|
5939
|
+
}
|
|
5940
|
+
});
|
|
5941
|
+
});
|
|
5942
|
+
server.on("error", (err) => {
|
|
5943
|
+
reject(err);
|
|
5944
|
+
});
|
|
5945
|
+
});
|
|
5946
|
+
}
|
|
5947
|
+
function escapeHtml2(str) {
|
|
5948
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5949
|
+
}
|
|
5950
|
+
|
|
3539
5951
|
// src/config/defineConfig.ts
|
|
3540
5952
|
function defineConfig(config) {
|
|
3541
5953
|
return config;
|
|
3542
5954
|
}
|
|
3543
5955
|
|
|
3544
5956
|
// src/version.ts
|
|
3545
|
-
var
|
|
3546
|
-
var
|
|
5957
|
+
var fs8 = __toESM(require("fs"));
|
|
5958
|
+
var path8 = __toESM(require("path"));
|
|
3547
5959
|
var import_node_url4 = require("url");
|
|
3548
5960
|
var import_meta = {};
|
|
3549
5961
|
try {
|
|
@@ -3566,32 +5978,33 @@ try {
|
|
|
3566
5978
|
}
|
|
3567
5979
|
} catch {
|
|
3568
5980
|
}
|
|
3569
|
-
var FALLBACK_VERSION = "0.3.0";
|
|
3570
5981
|
function readVersionFromPackageJson(fromDir) {
|
|
3571
5982
|
let currentDir = fromDir;
|
|
3572
|
-
for (let i = 0; i <
|
|
5983
|
+
for (let i = 0; i < 10; i++) {
|
|
3573
5984
|
try {
|
|
3574
|
-
const pkgJsonPath =
|
|
3575
|
-
if (
|
|
3576
|
-
const pkg = JSON.parse(
|
|
5985
|
+
const pkgJsonPath = path8.join(currentDir, "package.json");
|
|
5986
|
+
if (fs8.existsSync(pkgJsonPath)) {
|
|
5987
|
+
const pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
|
|
3577
5988
|
if (pkg.name === "@masumdev/markforge" && pkg.version) {
|
|
3578
5989
|
return pkg.version;
|
|
3579
5990
|
}
|
|
3580
5991
|
}
|
|
3581
5992
|
} catch {
|
|
3582
5993
|
}
|
|
3583
|
-
const parentDir =
|
|
5994
|
+
const parentDir = path8.dirname(currentDir);
|
|
3584
5995
|
if (parentDir === currentDir) break;
|
|
3585
5996
|
currentDir = parentDir;
|
|
3586
5997
|
}
|
|
3587
|
-
|
|
5998
|
+
throw new Error(
|
|
5999
|
+
"Failed to resolve '@masumdev/markforge' package version: package.json was not found or is missing a valid 'version' field."
|
|
6000
|
+
);
|
|
3588
6001
|
}
|
|
3589
6002
|
function getPackageDir() {
|
|
3590
6003
|
if (typeof __dirname !== "undefined") {
|
|
3591
6004
|
return __dirname;
|
|
3592
6005
|
}
|
|
3593
6006
|
try {
|
|
3594
|
-
return
|
|
6007
|
+
return path8.dirname((0, import_node_url4.fileURLToPath)(import_meta.url));
|
|
3595
6008
|
} catch {
|
|
3596
6009
|
return process.cwd();
|
|
3597
6010
|
}
|
|
@@ -3603,6 +6016,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3603
6016
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3604
6017
|
0 && (module.exports = {
|
|
3605
6018
|
DEFAULT_CONFIG,
|
|
6019
|
+
KATEX_INLINE_CSS,
|
|
3606
6020
|
MARKFORGE_VERSION,
|
|
3607
6021
|
Orientation,
|
|
3608
6022
|
OutputFormat,
|
|
@@ -3615,6 +6029,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3615
6029
|
THEME_DEFAULT,
|
|
3616
6030
|
Theme,
|
|
3617
6031
|
WatermarkPosition,
|
|
6032
|
+
applyHeadingNumbering,
|
|
3618
6033
|
buildDocxDocument,
|
|
3619
6034
|
buildHtmlDocument,
|
|
3620
6035
|
buildPdfDocument,
|
|
@@ -3631,18 +6046,28 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3631
6046
|
inlineHtmlImages,
|
|
3632
6047
|
loadConfig,
|
|
3633
6048
|
markforge,
|
|
6049
|
+
normalizeBackCover,
|
|
6050
|
+
normalizeCoverPage,
|
|
3634
6051
|
normalizeHeaderFooter,
|
|
3635
6052
|
normalizeHeaderFooterSlot,
|
|
6053
|
+
normalizeNumberHeadings,
|
|
6054
|
+
normalizeSecurity,
|
|
3636
6055
|
normalizeSignatures,
|
|
3637
6056
|
normalizeWatermark,
|
|
3638
6057
|
parseInlineSpans,
|
|
3639
6058
|
parseMarginToTwip,
|
|
6059
|
+
parseMarkdown,
|
|
3640
6060
|
parseMarkdownDocument,
|
|
6061
|
+
renderBackCoverHtml,
|
|
6062
|
+
renderCoverPageHtml,
|
|
3641
6063
|
renderInlinesToHtml,
|
|
6064
|
+
renderMathToHtml,
|
|
3642
6065
|
renderMermaidToPng,
|
|
6066
|
+
renderNodesToHtml,
|
|
3643
6067
|
replaceDocumentTokens,
|
|
3644
6068
|
resolveDocumentConfig,
|
|
3645
6069
|
resolveImage,
|
|
3646
6070
|
slugify,
|
|
6071
|
+
startPreviewServer,
|
|
3647
6072
|
tokenizeCodeLine
|
|
3648
6073
|
});
|