@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.mjs
CHANGED
|
@@ -49,6 +49,26 @@ function parseInlineSpans(text) {
|
|
|
49
49
|
remaining = remaining.slice(imgMatch[0].length);
|
|
50
50
|
continue;
|
|
51
51
|
}
|
|
52
|
+
const fnMatch = remaining.match(/^\[\^([\w-]+)\]/);
|
|
53
|
+
if (fnMatch) {
|
|
54
|
+
const fnId = fnMatch[1];
|
|
55
|
+
spans.push({
|
|
56
|
+
type: "footnoteRef",
|
|
57
|
+
content: fnId,
|
|
58
|
+
footnoteId: fnId
|
|
59
|
+
});
|
|
60
|
+
remaining = remaining.slice(fnMatch[0].length);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const mathMatch = remaining.match(/^\$([^$\n]+?)\$/);
|
|
64
|
+
if (mathMatch && !mathMatch[1].startsWith("$")) {
|
|
65
|
+
spans.push({
|
|
66
|
+
type: "mathInline",
|
|
67
|
+
content: mathMatch[1]
|
|
68
|
+
});
|
|
69
|
+
remaining = remaining.slice(mathMatch[0].length);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
52
72
|
const linkMatch = remaining.match(/^\[([^\]]+)\]\(([^)\s]+)(?:\s+"([^"]+)")?\)/);
|
|
53
73
|
if (linkMatch) {
|
|
54
74
|
spans.push({
|
|
@@ -148,7 +168,7 @@ function parseInlineSpans(text) {
|
|
|
148
168
|
remaining = remaining.slice(fullTag.length);
|
|
149
169
|
continue;
|
|
150
170
|
}
|
|
151
|
-
const nextSpecial = remaining.search(/[\*\_\[
|
|
171
|
+
const nextSpecial = remaining.search(/[\*\_\[\!`~<\$]/);
|
|
152
172
|
if (nextSpecial === -1) {
|
|
153
173
|
spans.push({
|
|
154
174
|
type: "text",
|
|
@@ -169,11 +189,51 @@ function parseInlineSpans(text) {
|
|
|
169
189
|
remaining = remaining.slice(nextSpecial);
|
|
170
190
|
}
|
|
171
191
|
}
|
|
172
|
-
|
|
192
|
+
const merged = [];
|
|
193
|
+
for (const s of spans) {
|
|
194
|
+
const prev = merged[merged.length - 1];
|
|
195
|
+
if (prev && prev.type === "text" && s.type === "text") {
|
|
196
|
+
prev.content += s.content;
|
|
197
|
+
} else {
|
|
198
|
+
merged.push(s);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return merged;
|
|
173
202
|
}
|
|
174
203
|
function slugify(text) {
|
|
175
204
|
return text.toLowerCase().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
176
205
|
}
|
|
206
|
+
function applyHeadingNumbering(nodes, tocEntries, options) {
|
|
207
|
+
const depth = options.depth ?? 3;
|
|
208
|
+
const skipH1 = options.skipH1 ?? false;
|
|
209
|
+
const prefix = options.prefix ?? "";
|
|
210
|
+
const counters = [0, 0, 0, 0, 0, 0];
|
|
211
|
+
for (const node of nodes) {
|
|
212
|
+
if (node.type === "heading" && node.level) {
|
|
213
|
+
if (node._numbered) continue;
|
|
214
|
+
const lvl = node.level;
|
|
215
|
+
if (lvl > depth) continue;
|
|
216
|
+
if (lvl === 1 && skipH1) continue;
|
|
217
|
+
const idx = lvl - 1;
|
|
218
|
+
counters[idx]++;
|
|
219
|
+
for (let c = idx + 1; c < counters.length; c++) {
|
|
220
|
+
counters[c] = 0;
|
|
221
|
+
}
|
|
222
|
+
const startIdx = skipH1 ? 1 : 0;
|
|
223
|
+
const parts = counters.slice(startIdx, idx + 1).filter((n) => n > 0);
|
|
224
|
+
const numberStr = parts.join(".") + ".";
|
|
225
|
+
const fullPrefix = prefix ? `${prefix} ${numberStr} ` : `${numberStr} `;
|
|
226
|
+
const originalText = node.text || "";
|
|
227
|
+
node.text = fullPrefix + originalText;
|
|
228
|
+
node.inlines = parseInlineSpans(node.text);
|
|
229
|
+
node._numbered = true;
|
|
230
|
+
const toc = tocEntries.find((t) => t.id === node.id);
|
|
231
|
+
if (toc) {
|
|
232
|
+
toc.text = node.text;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
177
237
|
function parseMarkdownDocument(rawMarkdown) {
|
|
178
238
|
const { data: frontmatter, content } = matter(rawMarkdown);
|
|
179
239
|
const metadata = frontmatter || {};
|
|
@@ -185,6 +245,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
185
245
|
const lines = cleanContent.split(/\r?\n/);
|
|
186
246
|
const nodes = [];
|
|
187
247
|
const tocEntries = [];
|
|
248
|
+
const footnoteDefs = [];
|
|
188
249
|
let i = 0;
|
|
189
250
|
while (i < lines.length) {
|
|
190
251
|
const line = lines[i];
|
|
@@ -213,6 +274,29 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
213
274
|
i++;
|
|
214
275
|
continue;
|
|
215
276
|
}
|
|
277
|
+
if (line.trim().startsWith("$$")) {
|
|
278
|
+
const mathLines = [];
|
|
279
|
+
const singleLine = line.trim().match(/^\$\$(.+)\$\$$/);
|
|
280
|
+
if (singleLine) {
|
|
281
|
+
nodes.push({
|
|
282
|
+
type: "mathBlock",
|
|
283
|
+
text: singleLine[1].trim()
|
|
284
|
+
});
|
|
285
|
+
i++;
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
i++;
|
|
289
|
+
while (i < lines.length && !lines[i].trim().startsWith("$$")) {
|
|
290
|
+
mathLines.push(lines[i]);
|
|
291
|
+
i++;
|
|
292
|
+
}
|
|
293
|
+
if (i < lines.length) i++;
|
|
294
|
+
nodes.push({
|
|
295
|
+
type: "mathBlock",
|
|
296
|
+
text: mathLines.join("\n").trim()
|
|
297
|
+
});
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
216
300
|
const codeBlockMatch = line.match(/^```(\w+)?/);
|
|
217
301
|
if (codeBlockMatch) {
|
|
218
302
|
const language = (codeBlockMatch[1] || "text").trim().toLowerCase();
|
|
@@ -239,6 +323,100 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
239
323
|
}
|
|
240
324
|
continue;
|
|
241
325
|
}
|
|
326
|
+
const colsMatch = line.trim().match(/^:::columns(?:\s+\[?([\w\s=.-]+)\]?)?$/i);
|
|
327
|
+
if (colsMatch) {
|
|
328
|
+
const attrStr = colsMatch[1] || "";
|
|
329
|
+
let colsCount = 2;
|
|
330
|
+
let colGap = "1.5rem";
|
|
331
|
+
if (attrStr) {
|
|
332
|
+
const numMatch = attrStr.trim().match(/^(\d+)$/);
|
|
333
|
+
const cMatch = attrStr.match(/cols=(\d+)/i) || attrStr.match(/columns=(\d+)/i);
|
|
334
|
+
const gMatch = attrStr.match(/gap=([^\s]+)/i);
|
|
335
|
+
if (numMatch) colsCount = parseInt(numMatch[1], 10);
|
|
336
|
+
else if (cMatch) colsCount = parseInt(cMatch[1], 10);
|
|
337
|
+
if (gMatch) colGap = gMatch[1];
|
|
338
|
+
}
|
|
339
|
+
const columnNodes = [];
|
|
340
|
+
let currentColumnLines = [];
|
|
341
|
+
let inColBlock = false;
|
|
342
|
+
i++;
|
|
343
|
+
while (i < lines.length) {
|
|
344
|
+
const curLine = lines[i];
|
|
345
|
+
const trimmed = curLine.trim();
|
|
346
|
+
if (/^:::col(?:umn)?$/i.test(trimmed)) {
|
|
347
|
+
if (currentColumnLines.length > 0) {
|
|
348
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
349
|
+
columnNodes.push({
|
|
350
|
+
type: "column",
|
|
351
|
+
children: subDoc.nodes
|
|
352
|
+
});
|
|
353
|
+
currentColumnLines = [];
|
|
354
|
+
}
|
|
355
|
+
inColBlock = true;
|
|
356
|
+
i++;
|
|
357
|
+
} else if (trimmed === ":::") {
|
|
358
|
+
if (inColBlock) {
|
|
359
|
+
if (currentColumnLines.length > 0) {
|
|
360
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
361
|
+
columnNodes.push({
|
|
362
|
+
type: "column",
|
|
363
|
+
children: subDoc.nodes
|
|
364
|
+
});
|
|
365
|
+
currentColumnLines = [];
|
|
366
|
+
}
|
|
367
|
+
inColBlock = false;
|
|
368
|
+
i++;
|
|
369
|
+
} else {
|
|
370
|
+
if (currentColumnLines.length > 0) {
|
|
371
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
372
|
+
columnNodes.push({
|
|
373
|
+
type: "column",
|
|
374
|
+
children: subDoc.nodes
|
|
375
|
+
});
|
|
376
|
+
currentColumnLines = [];
|
|
377
|
+
}
|
|
378
|
+
i++;
|
|
379
|
+
break;
|
|
380
|
+
}
|
|
381
|
+
} else {
|
|
382
|
+
currentColumnLines.push(curLine);
|
|
383
|
+
i++;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
if (currentColumnLines.length > 0) {
|
|
387
|
+
const subDoc = parseMarkdownDocument(currentColumnLines.join("\n"));
|
|
388
|
+
columnNodes.push({
|
|
389
|
+
type: "column",
|
|
390
|
+
children: subDoc.nodes
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
nodes.push({
|
|
394
|
+
type: "columns",
|
|
395
|
+
columnsCount: columnNodes.length > 0 ? columnNodes.length : colsCount,
|
|
396
|
+
columnGap: colGap,
|
|
397
|
+
children: columnNodes
|
|
398
|
+
});
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
const fnDefMatch = line.match(/^\[\^([\w-]+)\]:\s+(.+)$/);
|
|
402
|
+
if (fnDefMatch) {
|
|
403
|
+
const fnId = fnDefMatch[1];
|
|
404
|
+
const fnText = fnDefMatch[2].trim();
|
|
405
|
+
const inlines = parseInlineSpans(fnText);
|
|
406
|
+
footnoteDefs.push({
|
|
407
|
+
id: fnId,
|
|
408
|
+
text: fnText,
|
|
409
|
+
inlines
|
|
410
|
+
});
|
|
411
|
+
nodes.push({
|
|
412
|
+
type: "footnoteDef",
|
|
413
|
+
footnoteId: fnId,
|
|
414
|
+
text: fnText,
|
|
415
|
+
inlines
|
|
416
|
+
});
|
|
417
|
+
i++;
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
242
420
|
const calloutMatch = line.match(/^>\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]\s*$/i);
|
|
243
421
|
if (calloutMatch) {
|
|
244
422
|
const calloutType = calloutMatch[1].toUpperCase();
|
|
@@ -362,7 +540,7 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
362
540
|
continue;
|
|
363
541
|
}
|
|
364
542
|
const paraLines = [];
|
|
365
|
-
while (i < lines.length && lines[i].trim() && !lines[i].match(/^#{1,6}\s+/) && !lines[i].startsWith("```") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !lines[i].match(/^(\s*)([-*+]|\d+\.)\s+/)) {
|
|
543
|
+
while (i < lines.length && lines[i].trim() && !lines[i].match(/^#{1,6}\s+/) && !lines[i].startsWith("```") && !lines[i].startsWith("$$") && !lines[i].startsWith(">") && !lines[i].trim().startsWith("|") && !lines[i].match(/^:::columns/) && !lines[i].match(/^\[\^[\w-]+\]:\s+/) && !lines[i].match(/^(\s*)([-*+]|\d+\.)\s+/)) {
|
|
366
544
|
paraLines.push(lines[i]);
|
|
367
545
|
i++;
|
|
368
546
|
}
|
|
@@ -373,12 +551,19 @@ function parseMarkdownDocument(rawMarkdown) {
|
|
|
373
551
|
inlines: parseInlineSpans(paraText)
|
|
374
552
|
});
|
|
375
553
|
}
|
|
554
|
+
if (metadata.numberHeadings) {
|
|
555
|
+
const opts = typeof metadata.numberHeadings === "boolean" ? { enabled: metadata.numberHeadings } : metadata.numberHeadings;
|
|
556
|
+
if (opts.enabled !== false) {
|
|
557
|
+
applyHeadingNumbering(nodes, tocEntries, opts);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
376
560
|
return {
|
|
377
561
|
metadata,
|
|
378
562
|
content: cleanContent,
|
|
379
563
|
nodes,
|
|
380
564
|
tocEntries,
|
|
381
|
-
inlinedStyles
|
|
565
|
+
inlinedStyles,
|
|
566
|
+
footnoteDefs
|
|
382
567
|
};
|
|
383
568
|
}
|
|
384
569
|
|
|
@@ -404,7 +589,9 @@ import {
|
|
|
404
589
|
convertMillimetersToTwip,
|
|
405
590
|
ShadingType,
|
|
406
591
|
ExternalHyperlink,
|
|
407
|
-
TabStopType
|
|
592
|
+
TabStopType,
|
|
593
|
+
PageBreak,
|
|
594
|
+
NumberFormat
|
|
408
595
|
} from "docx";
|
|
409
596
|
|
|
410
597
|
// src/core/imageResolver.ts
|
|
@@ -466,9 +653,14 @@ async function resolveImage(src, baseDir = process.cwd()) {
|
|
|
466
653
|
memoryImageCache.set(cacheKey, resolved2);
|
|
467
654
|
return resolved2;
|
|
468
655
|
}
|
|
469
|
-
|
|
656
|
+
let localPath = path.isAbsolute(src) ? src : path.resolve(baseDir, src);
|
|
470
657
|
if (!fs.existsSync(localPath)) {
|
|
471
|
-
|
|
658
|
+
const cwdPath = path.resolve(process.cwd(), src);
|
|
659
|
+
if (fs.existsSync(cwdPath)) {
|
|
660
|
+
localPath = cwdPath;
|
|
661
|
+
} else {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
472
664
|
}
|
|
473
665
|
const buffer = fs.readFileSync(localPath);
|
|
474
666
|
const mimeType = getMimeType(localPath);
|
|
@@ -873,6 +1065,8 @@ import * as path4 from "path";
|
|
|
873
1065
|
import * as os from "os";
|
|
874
1066
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
875
1067
|
import { spawnSync } from "child_process";
|
|
1068
|
+
import { PDFDocument } from "pdf-lib";
|
|
1069
|
+
import { encryptPDF } from "@pdfsmaller/pdf-encrypt";
|
|
876
1070
|
|
|
877
1071
|
// src/core/html/htmlBuilder.ts
|
|
878
1072
|
import * as fs3 from "fs";
|
|
@@ -944,7 +1138,7 @@ var THEME_COMPONENTS = `
|
|
|
944
1138
|
.document-meta { font-size: 0.9rem; color: var(--mf-text-muted); display: flex; gap: 1.5rem; flex-wrap: wrap; }
|
|
945
1139
|
|
|
946
1140
|
/* Table of Contents */
|
|
947
|
-
.table-of-contents { background: var(--mf-card-bg); border: 1px solid var(--mf-border); border-radius: 8px; padding: 1.5rem 2rem; margin: 2rem 0; }
|
|
1141
|
+
.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; }
|
|
948
1142
|
.table-of-contents h2 { font-size: 1rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--mf-text-muted); margin: 0 0 1rem 0; }
|
|
949
1143
|
.table-of-contents ul { list-style: none; padding: 0; margin: 0; }
|
|
950
1144
|
.table-of-contents li { padding: 0.25rem 0; }
|
|
@@ -1017,11 +1211,13 @@ var THEME_CORPORATE = `
|
|
|
1017
1211
|
}
|
|
1018
1212
|
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; }
|
|
1019
1213
|
.document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
|
|
1020
|
-
h1, h2, h3, h4, h5, h6 { color: var(--mf-
|
|
1021
|
-
h1 { font-size: 2.2rem; border-bottom:
|
|
1214
|
+
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; }
|
|
1215
|
+
h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
|
|
1022
1216
|
h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid #CCFBF1; padding-bottom: 0.4rem; }
|
|
1023
|
-
h3 { font-size: 1.3rem; }
|
|
1024
|
-
h4 { font-size: 1.1rem; }
|
|
1217
|
+
h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
|
|
1218
|
+
h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
|
|
1219
|
+
h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
|
|
1220
|
+
h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
|
|
1025
1221
|
p { margin: 0.8rem 0; }
|
|
1026
1222
|
`;
|
|
1027
1223
|
var THEME_DEFAULT = THEME_CORPORATE;
|
|
@@ -1063,11 +1259,13 @@ function generateThemeCss(theme) {
|
|
|
1063
1259
|
}
|
|
1064
1260
|
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; }
|
|
1065
1261
|
.document-container { max-width: 860px; margin: 0 auto; position: relative; z-index: 1; }
|
|
1066
|
-
h1, h2, h3, h4, h5, h6 { color: var(--mf-
|
|
1067
|
-
h1 { font-size: 2.2rem; border-bottom:
|
|
1262
|
+
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; }
|
|
1263
|
+
h1 { font-size: 2.2rem; color: var(--mf-primary-dark); border-bottom: 2.5px solid var(--mf-primary); padding-bottom: 0.5rem; }
|
|
1068
1264
|
h2 { font-size: 1.6rem; color: var(--mf-primary-dark); border-bottom: 1px solid var(--mf-border); padding-bottom: 0.4rem; }
|
|
1069
|
-
h3 { font-size: 1.3rem; }
|
|
1070
|
-
h4 { font-size: 1.1rem; }
|
|
1265
|
+
h3 { font-size: 1.3rem; color: var(--mf-primary-dark); }
|
|
1266
|
+
h4 { font-size: 1.1rem; color: var(--mf-primary-dark); }
|
|
1267
|
+
h5 { font-size: 1.0rem; color: var(--mf-primary-dark); }
|
|
1268
|
+
h6 { font-size: 0.9rem; color: var(--mf-primary-dark); }
|
|
1071
1269
|
p { margin: 0.8rem 0; }
|
|
1072
1270
|
${theme.customCss || ""}
|
|
1073
1271
|
`;
|
|
@@ -1115,6 +1313,12 @@ var DEFAULT_CONFIG = {
|
|
|
1115
1313
|
},
|
|
1116
1314
|
toc: false,
|
|
1117
1315
|
watermark: void 0,
|
|
1316
|
+
signatures: void 0,
|
|
1317
|
+
coverPage: void 0,
|
|
1318
|
+
backCover: void 0,
|
|
1319
|
+
numberHeadings: void 0,
|
|
1320
|
+
security: void 0,
|
|
1321
|
+
math: true,
|
|
1118
1322
|
embedImages: true,
|
|
1119
1323
|
metadata: void 0,
|
|
1120
1324
|
watch: false,
|
|
@@ -1270,15 +1474,45 @@ function formatMarginCss(margin, defaultCss = "2.5cm") {
|
|
|
1270
1474
|
if (/^[0-9.]+$/.test(str)) return `${str}pt`;
|
|
1271
1475
|
return str;
|
|
1272
1476
|
}
|
|
1273
|
-
function replaceDocumentTokens(template = "", meta) {
|
|
1274
|
-
|
|
1477
|
+
function replaceDocumentTokens(template = "", meta = {}) {
|
|
1478
|
+
if (!template) return "";
|
|
1479
|
+
const currentYear = meta.year ? String(meta.year) : (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1480
|
+
const tokenMap = {
|
|
1481
|
+
title: meta.title ? String(meta.title) : "",
|
|
1482
|
+
subtitle: meta.subtitle ? String(meta.subtitle) : "",
|
|
1483
|
+
author: meta.author ? Array.isArray(meta.author) ? meta.author.join(", ") : String(meta.author) : "",
|
|
1484
|
+
version: meta.version ? String(meta.version) : "",
|
|
1485
|
+
date: meta.date ? String(meta.date) : "",
|
|
1486
|
+
company: meta.company ? String(meta.company) : "",
|
|
1487
|
+
year: currentYear
|
|
1488
|
+
};
|
|
1489
|
+
if (meta.metadata && typeof meta.metadata === "object") {
|
|
1490
|
+
for (const [key, val] of Object.entries(meta.metadata)) {
|
|
1491
|
+
if (val !== void 0 && val !== null) {
|
|
1492
|
+
tokenMap[key.toLowerCase()] = String(val);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
for (const [key, val] of Object.entries(meta)) {
|
|
1497
|
+
if (val !== void 0 && val !== null && typeof val !== "object") {
|
|
1498
|
+
tokenMap[key.toLowerCase()] = String(val);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
return template.replace(/\{([a-zA-Z0-9_\-]+)\}/gi, (match, tokenKey) => {
|
|
1502
|
+
const lowerKey = tokenKey.toLowerCase();
|
|
1503
|
+
if (lowerKey in tokenMap) {
|
|
1504
|
+
return tokenMap[lowerKey];
|
|
1505
|
+
}
|
|
1506
|
+
return match;
|
|
1507
|
+
});
|
|
1275
1508
|
}
|
|
1276
|
-
function normalizeWatermark(rawWatermark) {
|
|
1509
|
+
function normalizeWatermark(rawWatermark, tokens) {
|
|
1277
1510
|
if (!rawWatermark) {
|
|
1278
1511
|
return void 0;
|
|
1279
1512
|
}
|
|
1280
1513
|
if (typeof rawWatermark === "string") {
|
|
1281
|
-
|
|
1514
|
+
let text = rawWatermark.trim();
|
|
1515
|
+
if (tokens) text = replaceDocumentTokens(text, tokens);
|
|
1282
1516
|
if (!text) return void 0;
|
|
1283
1517
|
return {
|
|
1284
1518
|
text,
|
|
@@ -1291,8 +1525,10 @@ function normalizeWatermark(rawWatermark) {
|
|
|
1291
1525
|
}
|
|
1292
1526
|
if (typeof rawWatermark === "object") {
|
|
1293
1527
|
if (!rawWatermark.text || !rawWatermark.text.trim()) return void 0;
|
|
1528
|
+
let text = rawWatermark.text.trim();
|
|
1529
|
+
if (tokens) text = replaceDocumentTokens(text, tokens);
|
|
1294
1530
|
return {
|
|
1295
|
-
text
|
|
1531
|
+
text,
|
|
1296
1532
|
color: rawWatermark.color || "#94a3b8",
|
|
1297
1533
|
opacity: typeof rawWatermark.opacity === "number" ? rawWatermark.opacity : 0.08,
|
|
1298
1534
|
fontSize: rawWatermark.fontSize || 54,
|
|
@@ -1408,6 +1644,174 @@ function normalizeSignatures(raw, meta = {}) {
|
|
|
1408
1644
|
spacingBeforeTwip
|
|
1409
1645
|
};
|
|
1410
1646
|
}
|
|
1647
|
+
function normalizeCoverPage(rawCover, tokenCtx = {}) {
|
|
1648
|
+
if (!rawCover) return void 0;
|
|
1649
|
+
const cfg = typeof rawCover === "object" ? rawCover : {};
|
|
1650
|
+
if (cfg.enabled === false) return void 0;
|
|
1651
|
+
const preset = cfg.preset || "modern";
|
|
1652
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : tokenCtx.title ? String(tokenCtx.title) : "Document Title";
|
|
1653
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : tokenCtx.subtitle ? String(tokenCtx.subtitle) : void 0;
|
|
1654
|
+
const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
|
|
1655
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
|
|
1656
|
+
const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
|
|
1657
|
+
let dateStr;
|
|
1658
|
+
if (typeof cfg.date === "string") {
|
|
1659
|
+
dateStr = replaceDocumentTokens(cfg.date, tokenCtx);
|
|
1660
|
+
} else if (cfg.date === true) {
|
|
1661
|
+
dateStr = tokenCtx.date ? String(tokenCtx.date) : (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1662
|
+
} else {
|
|
1663
|
+
dateStr = tokenCtx.date ? String(tokenCtx.date) : void 0;
|
|
1664
|
+
}
|
|
1665
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1666
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1667
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1668
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1669
|
+
const logoWidth = cfg.logoWidth;
|
|
1670
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1671
|
+
const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1672
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1673
|
+
const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
|
|
1674
|
+
const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
|
|
1675
|
+
const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
|
|
1676
|
+
const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
|
|
1677
|
+
const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
|
|
1678
|
+
const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
|
|
1679
|
+
const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
|
|
1680
|
+
const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
|
|
1681
|
+
let socialMap;
|
|
1682
|
+
if (cfg.social && typeof cfg.social === "object") {
|
|
1683
|
+
socialMap = {};
|
|
1684
|
+
for (const [k, v] of Object.entries(cfg.social)) {
|
|
1685
|
+
if (typeof v === "string") {
|
|
1686
|
+
socialMap[k] = replaceDocumentTokens(v, tokenCtx);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1691
|
+
const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : void 0;
|
|
1692
|
+
return {
|
|
1693
|
+
enabled: true,
|
|
1694
|
+
preset,
|
|
1695
|
+
title,
|
|
1696
|
+
subtitle,
|
|
1697
|
+
author,
|
|
1698
|
+
company,
|
|
1699
|
+
version,
|
|
1700
|
+
date: dateStr,
|
|
1701
|
+
badge,
|
|
1702
|
+
badgeColor,
|
|
1703
|
+
badgeTextColor,
|
|
1704
|
+
logo,
|
|
1705
|
+
logoWidth,
|
|
1706
|
+
bgGradient,
|
|
1707
|
+
backgroundColor,
|
|
1708
|
+
textColor,
|
|
1709
|
+
titleColor,
|
|
1710
|
+
subtitleColor,
|
|
1711
|
+
accentColor,
|
|
1712
|
+
footerText,
|
|
1713
|
+
address,
|
|
1714
|
+
email,
|
|
1715
|
+
phone,
|
|
1716
|
+
website,
|
|
1717
|
+
social: socialMap,
|
|
1718
|
+
copyright
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function normalizeBackCover(rawBack, tokenCtx = {}) {
|
|
1722
|
+
if (!rawBack) return void 0;
|
|
1723
|
+
const cfg = typeof rawBack === "object" ? rawBack : {};
|
|
1724
|
+
if (cfg.enabled === false) return void 0;
|
|
1725
|
+
const preset = cfg.preset || "modern";
|
|
1726
|
+
const title = cfg.title ? replaceDocumentTokens(String(cfg.title), tokenCtx) : "Thank You";
|
|
1727
|
+
const subtitle = cfg.subtitle ? replaceDocumentTokens(String(cfg.subtitle), tokenCtx) : void 0;
|
|
1728
|
+
const author = Array.isArray(cfg.author) ? cfg.author.join(", ") : cfg.author ? replaceDocumentTokens(String(cfg.author), tokenCtx) : tokenCtx.author ? String(tokenCtx.author) : void 0;
|
|
1729
|
+
const company = cfg.company ? replaceDocumentTokens(String(cfg.company), tokenCtx) : tokenCtx.company ? String(tokenCtx.company) : void 0;
|
|
1730
|
+
const version = cfg.version ? replaceDocumentTokens(String(cfg.version), tokenCtx) : tokenCtx.version ? String(tokenCtx.version) : void 0;
|
|
1731
|
+
const date = typeof cfg.date === "string" ? replaceDocumentTokens(cfg.date, tokenCtx) : tokenCtx.date ? String(tokenCtx.date) : void 0;
|
|
1732
|
+
const address = cfg.address ? replaceDocumentTokens(String(cfg.address), tokenCtx) : void 0;
|
|
1733
|
+
const email = cfg.email ? replaceDocumentTokens(String(cfg.email), tokenCtx) : void 0;
|
|
1734
|
+
const phone = cfg.phone ? replaceDocumentTokens(String(cfg.phone), tokenCtx) : void 0;
|
|
1735
|
+
const website = cfg.website ? replaceDocumentTokens(String(cfg.website), tokenCtx) : void 0;
|
|
1736
|
+
let socialMap;
|
|
1737
|
+
if (cfg.social && typeof cfg.social === "object") {
|
|
1738
|
+
socialMap = {};
|
|
1739
|
+
for (const [k, v] of Object.entries(cfg.social)) {
|
|
1740
|
+
if (typeof v === "string") {
|
|
1741
|
+
socialMap[k] = replaceDocumentTokens(v, tokenCtx);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
const currentYear = (/* @__PURE__ */ new Date()).getFullYear().toString();
|
|
1746
|
+
const copyright = cfg.copyright ? replaceDocumentTokens(String(cfg.copyright), { ...tokenCtx, year: currentYear }) : company ? `Copyright (c) ${currentYear} ${company}. All Rights Reserved.` : void 0;
|
|
1747
|
+
const footerText = cfg.footerText ? replaceDocumentTokens(String(cfg.footerText), tokenCtx) : void 0;
|
|
1748
|
+
const badge = cfg.badge ? replaceDocumentTokens(String(cfg.badge), tokenCtx) : void 0;
|
|
1749
|
+
const badgeColor = typeof cfg.badgeColor === "string" ? cfg.badgeColor : void 0;
|
|
1750
|
+
const badgeTextColor = typeof cfg.badgeTextColor === "string" ? cfg.badgeTextColor : void 0;
|
|
1751
|
+
const logo = typeof cfg.logo === "string" ? cfg.logo : void 0;
|
|
1752
|
+
const logoWidth = cfg.logoWidth;
|
|
1753
|
+
const bgGradient = typeof cfg.bgGradient === "string" ? cfg.bgGradient : typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1754
|
+
const backgroundColor = typeof cfg.backgroundColor === "string" ? cfg.backgroundColor : void 0;
|
|
1755
|
+
const textColor = typeof cfg.textColor === "string" ? cfg.textColor : void 0;
|
|
1756
|
+
const titleColor = typeof cfg.titleColor === "string" ? cfg.titleColor : void 0;
|
|
1757
|
+
const subtitleColor = typeof cfg.subtitleColor === "string" ? cfg.subtitleColor : void 0;
|
|
1758
|
+
const accentColor = typeof cfg.accentColor === "string" ? cfg.accentColor : void 0;
|
|
1759
|
+
return {
|
|
1760
|
+
enabled: true,
|
|
1761
|
+
preset,
|
|
1762
|
+
title,
|
|
1763
|
+
subtitle,
|
|
1764
|
+
author,
|
|
1765
|
+
company,
|
|
1766
|
+
version,
|
|
1767
|
+
date,
|
|
1768
|
+
address,
|
|
1769
|
+
email,
|
|
1770
|
+
phone,
|
|
1771
|
+
website,
|
|
1772
|
+
social: socialMap,
|
|
1773
|
+
copyright,
|
|
1774
|
+
footerText,
|
|
1775
|
+
badge,
|
|
1776
|
+
badgeColor,
|
|
1777
|
+
badgeTextColor,
|
|
1778
|
+
logo,
|
|
1779
|
+
logoWidth,
|
|
1780
|
+
bgGradient,
|
|
1781
|
+
backgroundColor,
|
|
1782
|
+
textColor,
|
|
1783
|
+
titleColor,
|
|
1784
|
+
subtitleColor,
|
|
1785
|
+
accentColor
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
function normalizeNumberHeadings(raw) {
|
|
1789
|
+
if (raw === void 0 || raw === false) return void 0;
|
|
1790
|
+
if (raw === true) {
|
|
1791
|
+
return { enabled: true, depth: 3, skipH1: false, prefix: "" };
|
|
1792
|
+
}
|
|
1793
|
+
if (typeof raw === "object") {
|
|
1794
|
+
const obj = raw;
|
|
1795
|
+
if (obj.enabled === false) return void 0;
|
|
1796
|
+
return {
|
|
1797
|
+
enabled: true,
|
|
1798
|
+
depth: obj.depth ?? 3,
|
|
1799
|
+
skipH1: obj.skipH1 ?? false,
|
|
1800
|
+
prefix: obj.prefix ?? ""
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
return void 0;
|
|
1804
|
+
}
|
|
1805
|
+
function normalizeSecurity(raw) {
|
|
1806
|
+
if (!raw) return void 0;
|
|
1807
|
+
const sec = raw;
|
|
1808
|
+
if (!sec.userPassword && !sec.ownerPassword && !sec.permissions) return void 0;
|
|
1809
|
+
return {
|
|
1810
|
+
userPassword: sec.userPassword,
|
|
1811
|
+
ownerPassword: sec.ownerPassword,
|
|
1812
|
+
permissions: sec.permissions
|
|
1813
|
+
};
|
|
1814
|
+
}
|
|
1411
1815
|
function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
1412
1816
|
const configMeta = userConfig.metadata || {};
|
|
1413
1817
|
const mergedMeta = { ...configMeta, ...frontmatter };
|
|
@@ -1418,7 +1822,15 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1418
1822
|
const version = mergedMeta.version || void 0;
|
|
1419
1823
|
const company = mergedMeta.company || void 0;
|
|
1420
1824
|
const lang = mergedMeta.lang || "en";
|
|
1421
|
-
const tokenContext = {
|
|
1825
|
+
const tokenContext = {
|
|
1826
|
+
...mergedMeta,
|
|
1827
|
+
title,
|
|
1828
|
+
subtitle,
|
|
1829
|
+
author,
|
|
1830
|
+
version,
|
|
1831
|
+
date,
|
|
1832
|
+
company
|
|
1833
|
+
};
|
|
1422
1834
|
const theme = mergedMeta.theme || userConfig.theme || DEFAULT_CONFIG.theme;
|
|
1423
1835
|
const orientation = mergedMeta.orientation || userConfig.orientation || DEFAULT_CONFIG.orientation;
|
|
1424
1836
|
const paperSize = mergedMeta.paperSize || userConfig.paperSize || DEFAULT_CONFIG.paperSize;
|
|
@@ -1447,9 +1859,18 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1447
1859
|
const footer = normalizeHeaderFooter(rawFooter, tokenContext);
|
|
1448
1860
|
const toc = typeof mergedMeta.toc === "boolean" ? mergedMeta.toc : typeof userConfig.toc === "boolean" ? userConfig.toc : DEFAULT_CONFIG.toc;
|
|
1449
1861
|
const rawWatermark = mergedMeta.watermark !== void 0 ? mergedMeta.watermark : userConfig.watermark !== void 0 ? userConfig.watermark : DEFAULT_CONFIG.watermark;
|
|
1450
|
-
const watermark = normalizeWatermark(rawWatermark);
|
|
1862
|
+
const watermark = normalizeWatermark(rawWatermark, tokenContext);
|
|
1451
1863
|
const rawSignatures = mergedMeta.signatures || userConfig.signatures;
|
|
1452
1864
|
const signatures = normalizeSignatures(rawSignatures, tokenContext);
|
|
1865
|
+
const rawCover = mergedMeta.coverPage !== void 0 ? mergedMeta.coverPage : userConfig.coverPage;
|
|
1866
|
+
const coverPage = normalizeCoverPage(rawCover, tokenContext);
|
|
1867
|
+
const rawBack = mergedMeta.backCover !== void 0 ? mergedMeta.backCover : userConfig.backCover;
|
|
1868
|
+
const backCover = normalizeBackCover(rawBack, tokenContext);
|
|
1869
|
+
const rawNumberHeadings = mergedMeta.numberHeadings !== void 0 ? mergedMeta.numberHeadings : userConfig.numberHeadings;
|
|
1870
|
+
const numberHeadings = normalizeNumberHeadings(rawNumberHeadings);
|
|
1871
|
+
const rawSecurity = mergedMeta.security || userConfig.security;
|
|
1872
|
+
const security = normalizeSecurity(rawSecurity);
|
|
1873
|
+
const math = mergedMeta.math !== false && userConfig.math !== false;
|
|
1453
1874
|
const cssList = [];
|
|
1454
1875
|
const addCss = (item) => {
|
|
1455
1876
|
if (!item) return;
|
|
@@ -1479,6 +1900,11 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1479
1900
|
toc,
|
|
1480
1901
|
signatures,
|
|
1481
1902
|
watermark,
|
|
1903
|
+
coverPage,
|
|
1904
|
+
backCover,
|
|
1905
|
+
numberHeadings,
|
|
1906
|
+
security,
|
|
1907
|
+
math,
|
|
1482
1908
|
css: cssList,
|
|
1483
1909
|
embedImages,
|
|
1484
1910
|
bundleHtml,
|
|
@@ -1486,11 +1912,50 @@ function resolveDocumentConfig(frontmatter = {}, userConfig = {}) {
|
|
|
1486
1912
|
};
|
|
1487
1913
|
}
|
|
1488
1914
|
|
|
1915
|
+
// src/core/math/mathRenderer.ts
|
|
1916
|
+
import katex from "katex";
|
|
1917
|
+
function renderMathToHtml(latex, displayMode = false) {
|
|
1918
|
+
try {
|
|
1919
|
+
return katex.renderToString(latex.trim(), {
|
|
1920
|
+
displayMode,
|
|
1921
|
+
throwOnError: false,
|
|
1922
|
+
output: "htmlAndMathml",
|
|
1923
|
+
strict: false
|
|
1924
|
+
});
|
|
1925
|
+
} catch {
|
|
1926
|
+
return `<span class="katex-fallback">${latex}</span>`;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
var KATEX_INLINE_CSS = `
|
|
1930
|
+
.katex { font: normal 1.21em KaTeX_Main, Times New Roman, serif; line-height: 1.2; text-indent: 0; text-rendering: auto; border-color: currentColor; }
|
|
1931
|
+
.katex * { -ms-high-contrast-adjust: none !important; }
|
|
1932
|
+
.katex .katex-html { display: inline-block; }
|
|
1933
|
+
.katex .katex-mathml { clip: rect(1px, 1px, 1px, 1px); border: 0; height: 1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }
|
|
1934
|
+
.katex-display { display: block; margin: 1em 0; text-align: center; }
|
|
1935
|
+
.katex-display > .katex { display: inline-block; text-align: initial; }
|
|
1936
|
+
.katex .base { position: relative; white-space: nowrap; width: min-content; }
|
|
1937
|
+
.katex .strut { display: inline-block; }
|
|
1938
|
+
.katex .mord { display: inline-block; }
|
|
1939
|
+
.katex .mbin { display: inline-block; }
|
|
1940
|
+
.katex .mrel { display: inline-block; }
|
|
1941
|
+
.katex .mopen { display: inline-block; }
|
|
1942
|
+
.katex .mclose { display: inline-block; }
|
|
1943
|
+
.katex .mpunct { display: inline-block; }
|
|
1944
|
+
.katex .minner { display: inline-block; }
|
|
1945
|
+
.katex .mop { display: inline-block; }
|
|
1946
|
+
.katex .frac-line { width: 100%; border-bottom-style: solid; }
|
|
1947
|
+
.katex .vlist-t { display: inline-table; table-layout: fixed; }
|
|
1948
|
+
.katex .vlist-r { display: table-row; }
|
|
1949
|
+
.katex .vlist { display: table-cell; vertical-align: bottom; position: relative; }
|
|
1950
|
+
.katex .msupsub { text-align: left; }
|
|
1951
|
+
.katex .sqrt > .root { margin-left: 0.27777778em; margin-right: -0.55555556em; }
|
|
1952
|
+
`;
|
|
1953
|
+
|
|
1489
1954
|
// src/core/html/htmlBuilder.ts
|
|
1490
1955
|
function escapeHtml(str) {
|
|
1491
1956
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1492
1957
|
}
|
|
1493
|
-
async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
1958
|
+
async function renderInlinesToHtml(spans = [], baseDir = process.cwd(), tokens) {
|
|
1494
1959
|
let result = "";
|
|
1495
1960
|
for (const span of spans) {
|
|
1496
1961
|
if (span.type === "image" && span.url) {
|
|
@@ -1504,23 +1969,23 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1504
1969
|
continue;
|
|
1505
1970
|
}
|
|
1506
1971
|
if (span.type === "link" && span.url) {
|
|
1507
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
1972
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1508
1973
|
const title = span.title ? ` title="${escapeHtml(span.title)}"` : "";
|
|
1509
1974
|
result += `<a href="${escapeHtml(span.url)}"${title}>${inner}</a>`;
|
|
1510
1975
|
continue;
|
|
1511
1976
|
}
|
|
1512
1977
|
if (span.type === "bold") {
|
|
1513
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
1978
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1514
1979
|
result += `<strong>${inner}</strong>`;
|
|
1515
1980
|
continue;
|
|
1516
1981
|
}
|
|
1517
1982
|
if (span.type === "italic") {
|
|
1518
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
1983
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1519
1984
|
result += `<em>${inner}</em>`;
|
|
1520
1985
|
continue;
|
|
1521
1986
|
}
|
|
1522
1987
|
if (span.type === "strikethrough") {
|
|
1523
|
-
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir) : escapeHtml(span.content);
|
|
1988
|
+
const inner = span.children ? await renderInlinesToHtml(span.children, baseDir, tokens) : escapeHtml(tokens ? replaceDocumentTokens(span.content, tokens) : span.content);
|
|
1524
1989
|
result += `<del>${inner}</del>`;
|
|
1525
1990
|
continue;
|
|
1526
1991
|
}
|
|
@@ -1528,83 +1993,57 @@ async function renderInlinesToHtml(spans = [], baseDir = process.cwd()) {
|
|
|
1528
1993
|
result += `<code>${escapeHtml(span.content)}</code>`;
|
|
1529
1994
|
continue;
|
|
1530
1995
|
}
|
|
1996
|
+
if (span.type === "mathInline") {
|
|
1997
|
+
result += renderMathToHtml(span.content, false);
|
|
1998
|
+
continue;
|
|
1999
|
+
}
|
|
2000
|
+
if (span.type === "footnoteRef") {
|
|
2001
|
+
const id = escapeHtml(span.footnoteId || span.content);
|
|
2002
|
+
result += `<sup><a href="#fn-${id}" id="fnref-${id}" class="markforge-fnref">[${escapeHtml(span.content)}]</a></sup>`;
|
|
2003
|
+
continue;
|
|
2004
|
+
}
|
|
1531
2005
|
if (span.type === "htmlInline") {
|
|
1532
2006
|
result += span.content;
|
|
1533
2007
|
continue;
|
|
1534
2008
|
}
|
|
1535
|
-
|
|
2009
|
+
const content = tokens ? replaceDocumentTokens(span.content, tokens) : span.content;
|
|
2010
|
+
result += escapeHtml(content);
|
|
1536
2011
|
}
|
|
1537
2012
|
return result;
|
|
1538
2013
|
}
|
|
1539
|
-
async function
|
|
1540
|
-
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
1541
|
-
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
1542
|
-
let customCss = "";
|
|
1543
|
-
for (const cssPath of resolved.css) {
|
|
1544
|
-
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
1545
|
-
if (fs3.existsSync(fullCssPath)) {
|
|
1546
|
-
customCss += `
|
|
1547
|
-
/* Custom CSS: ${cssPath} */
|
|
1548
|
-
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
2014
|
+
async function renderNodesToHtml(nodes, resolved, baseDir = process.cwd(), tokens) {
|
|
1552
2015
|
let bodyHtml = "";
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
`;
|
|
1556
|
-
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
1557
|
-
`;
|
|
1558
|
-
if (resolved.subtitle) {
|
|
1559
|
-
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
1560
|
-
`;
|
|
1561
|
-
}
|
|
1562
|
-
if (resolved.author || resolved.date || resolved.version) {
|
|
1563
|
-
bodyHtml += ` <div class="document-meta">
|
|
1564
|
-
`;
|
|
1565
|
-
if (resolved.author) {
|
|
1566
|
-
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
1567
|
-
`;
|
|
1568
|
-
}
|
|
1569
|
-
if (resolved.version) {
|
|
1570
|
-
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
1571
|
-
`;
|
|
1572
|
-
}
|
|
1573
|
-
if (resolved.date) {
|
|
1574
|
-
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
1575
|
-
`;
|
|
1576
|
-
}
|
|
1577
|
-
bodyHtml += ` </div>
|
|
1578
|
-
`;
|
|
1579
|
-
}
|
|
1580
|
-
bodyHtml += ` </header>
|
|
1581
|
-
`;
|
|
1582
|
-
}
|
|
1583
|
-
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
1584
|
-
bodyHtml += ` <nav class="table-of-contents">
|
|
1585
|
-
`;
|
|
1586
|
-
bodyHtml += ` <h2>Table of Contents</h2>
|
|
1587
|
-
<ul>
|
|
1588
|
-
`;
|
|
1589
|
-
for (const entry of doc.tocEntries) {
|
|
1590
|
-
const indent = " ".repeat(entry.level);
|
|
1591
|
-
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
1592
|
-
`;
|
|
1593
|
-
}
|
|
1594
|
-
bodyHtml += ` </ul>
|
|
1595
|
-
</nav>
|
|
1596
|
-
`;
|
|
1597
|
-
}
|
|
1598
|
-
for (const node of doc.nodes) {
|
|
2016
|
+
const tokenCtx = tokens || resolved;
|
|
2017
|
+
for (const node of nodes) {
|
|
1599
2018
|
if (node.type === "heading") {
|
|
1600
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2019
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1601
2020
|
bodyHtml += ` <h${node.level} id="${node.id}">${inner}</h${node.level}>
|
|
1602
2021
|
`;
|
|
1603
2022
|
continue;
|
|
1604
2023
|
}
|
|
1605
2024
|
if (node.type === "paragraph") {
|
|
1606
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2025
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1607
2026
|
bodyHtml += ` <p>${inner}</p>
|
|
2027
|
+
`;
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
if (node.type === "mathBlock") {
|
|
2031
|
+
bodyHtml += ` <div class="math-block">${renderMathToHtml(node.text || "", true)}</div>
|
|
2032
|
+
`;
|
|
2033
|
+
continue;
|
|
2034
|
+
}
|
|
2035
|
+
if (node.type === "columns") {
|
|
2036
|
+
const cols = node.columnsCount || 2;
|
|
2037
|
+
const gap = node.columnGap || "1.5rem";
|
|
2038
|
+
let colChildrenHtml = "";
|
|
2039
|
+
for (const col of node.children || []) {
|
|
2040
|
+
const colInner = await renderNodesToHtml(col.children || [], resolved, baseDir, tokenCtx);
|
|
2041
|
+
colChildrenHtml += ` <div class="markforge-col">
|
|
2042
|
+
${colInner} </div>
|
|
2043
|
+
`;
|
|
2044
|
+
}
|
|
2045
|
+
bodyHtml += ` <div class="markforge-columns" style="--cols: ${cols}; --col-gap: ${gap};">
|
|
2046
|
+
${colChildrenHtml} </div>
|
|
1608
2047
|
`;
|
|
1609
2048
|
continue;
|
|
1610
2049
|
}
|
|
@@ -1624,7 +2063,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1624
2063
|
continue;
|
|
1625
2064
|
}
|
|
1626
2065
|
if (node.type === "callout") {
|
|
1627
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2066
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1628
2067
|
const CALLOUT_STYLES = {
|
|
1629
2068
|
NOTE: { bg: "#ECFDFD", border: "#33CDCF", titleColor: "#009DA0" },
|
|
1630
2069
|
TIP: { bg: "#ecfdf5", border: "#10b981", titleColor: "#10b981" },
|
|
@@ -1644,7 +2083,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1644
2083
|
continue;
|
|
1645
2084
|
}
|
|
1646
2085
|
if (node.type === "blockquote") {
|
|
1647
|
-
const inner = await renderInlinesToHtml(node.inlines, baseDir);
|
|
2086
|
+
const inner = await renderInlinesToHtml(node.inlines, baseDir, tokenCtx);
|
|
1648
2087
|
bodyHtml += ` <blockquote>${inner}</blockquote>
|
|
1649
2088
|
`;
|
|
1650
2089
|
continue;
|
|
@@ -1658,7 +2097,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1658
2097
|
for (const cell of row.children || []) {
|
|
1659
2098
|
const tag = cell.isHeader ? "th" : "td";
|
|
1660
2099
|
const align = cell.align ? ` align="${cell.align}"` : "";
|
|
1661
|
-
const inner = await renderInlinesToHtml(cell.inlines, baseDir);
|
|
2100
|
+
const inner = await renderInlinesToHtml(cell.inlines, baseDir, tokenCtx);
|
|
1662
2101
|
bodyHtml += ` <${tag}${align}>${inner}</${tag}>
|
|
1663
2102
|
`;
|
|
1664
2103
|
}
|
|
@@ -1674,7 +2113,7 @@ ${escapeHtml(node.text || "")}
|
|
|
1674
2113
|
bodyHtml += ` <${tag}>
|
|
1675
2114
|
`;
|
|
1676
2115
|
for (const item of node.children) {
|
|
1677
|
-
const inner = await renderInlinesToHtml(item.inlines, baseDir);
|
|
2116
|
+
const inner = await renderInlinesToHtml(item.inlines, baseDir, tokenCtx);
|
|
1678
2117
|
bodyHtml += ` <li>${inner}</li>
|
|
1679
2118
|
`;
|
|
1680
2119
|
}
|
|
@@ -1693,70 +2132,508 @@ ${escapeHtml(node.text || "")}
|
|
|
1693
2132
|
continue;
|
|
1694
2133
|
}
|
|
1695
2134
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
width: 100vw;
|
|
1706
|
-
height: 100vh;
|
|
1707
|
-
pointer-events: none;
|
|
1708
|
-
z-index: 0;
|
|
1709
|
-
user-select: none;
|
|
1710
|
-
-webkit-user-select: none;
|
|
2135
|
+
return bodyHtml;
|
|
2136
|
+
}
|
|
2137
|
+
async function renderCoverPageHtml(cover, baseDir = process.cwd()) {
|
|
2138
|
+
let logoHtml = "";
|
|
2139
|
+
if (cover.logo) {
|
|
2140
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
2141
|
+
const src = resolvedLogo ? resolvedLogo.dataUri : cover.logo;
|
|
2142
|
+
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;";
|
|
2143
|
+
logoHtml = `<div class="cover-logo"><img src="${src}" alt="Logo" style="${widthStyle} object-fit: contain;" /></div>`;
|
|
1711
2144
|
}
|
|
1712
|
-
.
|
|
2145
|
+
const badgeHtml = cover.badge ? `<div class="cover-badge" style="${cover.badgeColor ? `background-color: ${cover.badgeColor};` : ""}${cover.badgeTextColor ? `color: ${cover.badgeTextColor};` : ""}">${escapeHtml(cover.badge)}</div>` : "";
|
|
2146
|
+
const titleHtml = `<h1 class="cover-title">${escapeHtml(cover.title)}</h1>`;
|
|
2147
|
+
const subtitleHtml = cover.subtitle ? `<div class="cover-subtitle">${escapeHtml(cover.subtitle)}</div>` : "";
|
|
2148
|
+
const metaItems = [];
|
|
2149
|
+
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>`);
|
|
2150
|
+
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>`);
|
|
2151
|
+
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>`);
|
|
2152
|
+
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>`);
|
|
2153
|
+
const metaHtml = metaItems.length > 0 ? `<div class="cover-meta">${metaItems.join("\n")}</div>` : "";
|
|
2154
|
+
const footerHtml = cover.footerText ? `<div class="cover-footer-text">${escapeHtml(cover.footerText)}</div>` : "";
|
|
2155
|
+
const css = `
|
|
2156
|
+
.markforge-cover {
|
|
2157
|
+
min-height: 100vh;
|
|
2158
|
+
box-sizing: border-box;
|
|
2159
|
+
display: flex;
|
|
2160
|
+
flex-direction: column;
|
|
2161
|
+
justify-content: space-between;
|
|
2162
|
+
padding: 4rem 3.5rem;
|
|
2163
|
+
page-break-after: always;
|
|
2164
|
+
break-after: page;
|
|
1713
2165
|
position: relative;
|
|
1714
|
-
z-index:
|
|
2166
|
+
z-index: 2;
|
|
2167
|
+
background: ${cover.bgGradient || "#FFFFFF"};
|
|
2168
|
+
-webkit-print-color-adjust: exact;
|
|
2169
|
+
print-color-adjust: exact;
|
|
2170
|
+
color: ${cover.textColor || "#0F172A"};
|
|
2171
|
+
}
|
|
2172
|
+
.markforge-cover.cover-modern {
|
|
2173
|
+
border-top: 8px solid #0D998D;
|
|
2174
|
+
}
|
|
2175
|
+
.markforge-cover.cover-corporate-split {
|
|
2176
|
+
border-left: 12px solid #0D998D;
|
|
2177
|
+
}
|
|
2178
|
+
.markforge-cover.cover-card {
|
|
2179
|
+
background: #F8FAFC;
|
|
2180
|
+
}
|
|
2181
|
+
.cover-top {
|
|
2182
|
+
display: flex;
|
|
2183
|
+
justify-content: space-between;
|
|
2184
|
+
align-items: flex-start;
|
|
2185
|
+
width: 100%;
|
|
2186
|
+
}
|
|
2187
|
+
.cover-badge {
|
|
2188
|
+
display: inline-block;
|
|
2189
|
+
padding: 0.35rem 0.85rem;
|
|
2190
|
+
font-size: 0.78rem;
|
|
2191
|
+
font-weight: 700;
|
|
2192
|
+
letter-spacing: 0.08em;
|
|
2193
|
+
text-transform: uppercase;
|
|
2194
|
+
background-color: #ECFDFD;
|
|
2195
|
+
color: #0D998D;
|
|
2196
|
+
border-radius: 4px;
|
|
2197
|
+
border: 1px solid #33CDCF;
|
|
2198
|
+
}
|
|
2199
|
+
.cover-body {
|
|
2200
|
+
margin: auto 0;
|
|
2201
|
+
}
|
|
2202
|
+
.cover-title {
|
|
2203
|
+
font-size: 2.8rem;
|
|
2204
|
+
font-weight: 800;
|
|
2205
|
+
line-height: 1.15;
|
|
2206
|
+
margin: 0 0 1rem 0;
|
|
2207
|
+
color: inherit;
|
|
2208
|
+
}
|
|
2209
|
+
.cover-subtitle {
|
|
2210
|
+
font-size: 1.35rem;
|
|
2211
|
+
font-weight: 400;
|
|
2212
|
+
color: #64748B;
|
|
2213
|
+
margin: 0 0 2rem 0;
|
|
2214
|
+
line-height: 1.4;
|
|
2215
|
+
}
|
|
2216
|
+
.cover-meta {
|
|
2217
|
+
display: flex;
|
|
2218
|
+
flex-direction: column;
|
|
2219
|
+
gap: 0.5rem;
|
|
2220
|
+
border-top: 1.5px solid #E2E8F0;
|
|
2221
|
+
padding-top: 1.5rem;
|
|
2222
|
+
max-width: 480px;
|
|
2223
|
+
}
|
|
2224
|
+
.cover-meta-item {
|
|
2225
|
+
font-size: 0.92rem;
|
|
2226
|
+
display: flex;
|
|
2227
|
+
gap: 0.75rem;
|
|
2228
|
+
}
|
|
2229
|
+
.cover-meta-label {
|
|
2230
|
+
font-weight: 600;
|
|
2231
|
+
color: #64748B;
|
|
2232
|
+
min-width: 110px;
|
|
2233
|
+
}
|
|
2234
|
+
.cover-meta-value {
|
|
2235
|
+
font-weight: 500;
|
|
2236
|
+
color: #0F172A;
|
|
2237
|
+
}
|
|
2238
|
+
.cover-bottom {
|
|
2239
|
+
display: flex;
|
|
2240
|
+
justify-content: space-between;
|
|
2241
|
+
align-items: flex-end;
|
|
2242
|
+
font-size: 0.82rem;
|
|
2243
|
+
color: #94A3B8;
|
|
1715
2244
|
}
|
|
1716
2245
|
@media print {
|
|
1717
|
-
.
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
left: 0;
|
|
1721
|
-
width: 100vw;
|
|
2246
|
+
.markforge-cover {
|
|
2247
|
+
page-break-after: always;
|
|
2248
|
+
break-after: page;
|
|
1722
2249
|
height: 100vh;
|
|
2250
|
+
min-height: 100vh;
|
|
2251
|
+
max-height: 100vh;
|
|
2252
|
+
box-sizing: border-box;
|
|
2253
|
+
overflow: hidden;
|
|
2254
|
+
margin: 0;
|
|
1723
2255
|
-webkit-print-color-adjust: exact;
|
|
1724
2256
|
print-color-adjust: exact;
|
|
1725
2257
|
}
|
|
1726
2258
|
}
|
|
1727
2259
|
`;
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
2260
|
+
const html = ` <section class="markforge-cover cover-${cover.preset}">
|
|
2261
|
+
<div class="cover-top">
|
|
2262
|
+
${logoHtml}
|
|
2263
|
+
${badgeHtml}
|
|
2264
|
+
</div>
|
|
2265
|
+
<div class="cover-body">
|
|
2266
|
+
${titleHtml}
|
|
2267
|
+
${subtitleHtml}
|
|
2268
|
+
${metaHtml}
|
|
2269
|
+
</div>
|
|
2270
|
+
<div class="cover-bottom">
|
|
2271
|
+
${footerHtml}
|
|
2272
|
+
</div>
|
|
2273
|
+
</section>
|
|
2274
|
+
`;
|
|
2275
|
+
return { html, css };
|
|
2276
|
+
}
|
|
2277
|
+
async function renderBackCoverHtml(backCover, baseDir = process.cwd()) {
|
|
2278
|
+
let logoHtml = "";
|
|
2279
|
+
if (backCover.logo) {
|
|
2280
|
+
const resolved = await resolveImage(backCover.logo, baseDir);
|
|
2281
|
+
const src = resolved ? resolved.dataUri : backCover.logo;
|
|
2282
|
+
const widthStyle = backCover.logoWidth ? `style="width: ${typeof backCover.logoWidth === "number" ? `${backCover.logoWidth}px` : backCover.logoWidth}; max-width: 100%;"` : `style="max-width: 160px; height: auto;"`;
|
|
2283
|
+
logoHtml = `<div class="back-logo"><img src="${src}" alt="Brand Logo" ${widthStyle} /></div>`;
|
|
2284
|
+
}
|
|
2285
|
+
const badgeHtml = backCover.badge ? `<div class="back-badge" style="${backCover.badgeColor ? `background-color: ${backCover.badgeColor};` : ""}${backCover.badgeTextColor ? `color: ${backCover.badgeTextColor};` : ""}">${escapeHtml(backCover.badge)}</div>` : "";
|
|
2286
|
+
const titleHtml = `<h1 class="back-title">${escapeHtml(backCover.title)}</h1>`;
|
|
2287
|
+
const subtitleHtml = backCover.subtitle ? `<div class="back-subtitle">${escapeHtml(backCover.subtitle)}</div>` : "";
|
|
2288
|
+
const contactItems = [];
|
|
2289
|
+
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>`);
|
|
2290
|
+
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>`);
|
|
2291
|
+
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>`);
|
|
2292
|
+
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>`);
|
|
2293
|
+
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>`);
|
|
2294
|
+
if (backCover.social) {
|
|
2295
|
+
for (const [network, url] of Object.entries(backCover.social)) {
|
|
2296
|
+
if (url) {
|
|
2297
|
+
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>`);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
const contactHtml = contactItems.length > 0 ? `<div class="back-contact-grid">${contactItems.join("\n")}</div>` : "";
|
|
2302
|
+
const copyrightHtml = backCover.copyright ? `<div class="back-copyright">${escapeHtml(backCover.copyright)}</div>` : "";
|
|
2303
|
+
const isDark = backCover.preset === "corporate";
|
|
2304
|
+
const css = `
|
|
2305
|
+
.markforge-back-cover {
|
|
2306
|
+
min-height: 100vh;
|
|
2307
|
+
box-sizing: border-box;
|
|
2308
|
+
display: flex;
|
|
2309
|
+
flex-direction: column;
|
|
2310
|
+
justify-content: space-between;
|
|
2311
|
+
padding: 4rem 3.5rem;
|
|
2312
|
+
page-break-before: always;
|
|
2313
|
+
break-before: page;
|
|
2314
|
+
position: relative;
|
|
2315
|
+
z-index: 2;
|
|
2316
|
+
background: ${backCover.bgGradient || (isDark ? "#0F172A" : "#FFFFFF")};
|
|
2317
|
+
color: ${backCover.textColor || (isDark ? "#F8FAFC" : "#0F172A")};
|
|
2318
|
+
}
|
|
2319
|
+
.markforge-back-cover.back-modern {
|
|
2320
|
+
border-bottom: 8px solid #0D998D;
|
|
2321
|
+
}
|
|
2322
|
+
.markforge-back-cover.back-corporate {
|
|
2323
|
+
border-left: 12px solid #33CDCF;
|
|
2324
|
+
}
|
|
2325
|
+
.markforge-back-cover.back-card {
|
|
2326
|
+
background: #F8FAFC;
|
|
2327
|
+
}
|
|
2328
|
+
.back-top {
|
|
2329
|
+
display: flex;
|
|
2330
|
+
justify-content: space-between;
|
|
2331
|
+
align-items: flex-start;
|
|
2332
|
+
width: 100%;
|
|
2333
|
+
}
|
|
2334
|
+
.back-badge {
|
|
2335
|
+
display: inline-block;
|
|
2336
|
+
padding: 0.35rem 0.85rem;
|
|
2337
|
+
font-size: 0.78rem;
|
|
2338
|
+
font-weight: 700;
|
|
2339
|
+
letter-spacing: 0.08em;
|
|
2340
|
+
text-transform: uppercase;
|
|
2341
|
+
background-color: #ECFDFD;
|
|
2342
|
+
color: #0D998D;
|
|
2343
|
+
border-radius: 4px;
|
|
2344
|
+
border: 1px solid #33CDCF;
|
|
2345
|
+
}
|
|
2346
|
+
.back-body {
|
|
2347
|
+
margin: auto 0;
|
|
2348
|
+
}
|
|
2349
|
+
.back-title {
|
|
2350
|
+
font-size: 2.6rem;
|
|
2351
|
+
font-weight: 800;
|
|
2352
|
+
line-height: 1.15;
|
|
2353
|
+
margin: 0 0 0.75rem 0;
|
|
2354
|
+
color: inherit;
|
|
2355
|
+
}
|
|
2356
|
+
.back-subtitle {
|
|
2357
|
+
font-size: 1.25rem;
|
|
2358
|
+
font-weight: 400;
|
|
2359
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2360
|
+
margin: 0 0 2rem 0;
|
|
2361
|
+
line-height: 1.4;
|
|
2362
|
+
}
|
|
2363
|
+
.back-contact-grid {
|
|
2364
|
+
display: flex;
|
|
2365
|
+
flex-direction: column;
|
|
2366
|
+
gap: 0.6rem;
|
|
2367
|
+
border-top: 1.5px solid ${isDark ? "#334155" : "#E2E8F0"};
|
|
2368
|
+
padding-top: 1.5rem;
|
|
2369
|
+
max-width: 540px;
|
|
2370
|
+
}
|
|
2371
|
+
.back-contact-item {
|
|
2372
|
+
font-size: 0.92rem;
|
|
2373
|
+
display: flex;
|
|
2374
|
+
gap: 0.75rem;
|
|
2375
|
+
}
|
|
2376
|
+
.back-contact-label {
|
|
2377
|
+
font-weight: 600;
|
|
2378
|
+
color: ${isDark ? "#94A3B8" : "#64748B"};
|
|
2379
|
+
min-width: 110px;
|
|
2380
|
+
}
|
|
2381
|
+
.back-contact-link {
|
|
2382
|
+
color: #0D998D;
|
|
2383
|
+
text-decoration: none;
|
|
2384
|
+
font-weight: 600;
|
|
2385
|
+
}
|
|
2386
|
+
.back-contact-link:hover {
|
|
2387
|
+
text-decoration: underline;
|
|
2388
|
+
}
|
|
2389
|
+
.back-copyright {
|
|
2390
|
+
font-size: 0.82rem;
|
|
2391
|
+
color: ${isDark ? "#64748B" : "#94A3B8"};
|
|
2392
|
+
border-top: 1px solid ${isDark ? "#1E293B" : "#F1F5F9"};
|
|
2393
|
+
padding-top: 1rem;
|
|
2394
|
+
margin-top: 2rem;
|
|
2395
|
+
}
|
|
2396
|
+
@media print {
|
|
2397
|
+
.markforge-back-cover {
|
|
2398
|
+
page: back-cover-page;
|
|
2399
|
+
page-break-before: always;
|
|
2400
|
+
break-before: page;
|
|
2401
|
+
page-break-after: avoid;
|
|
2402
|
+
break-after: avoid;
|
|
2403
|
+
min-height: 100vh;
|
|
2404
|
+
height: 100vh;
|
|
2405
|
+
max-height: 100vh;
|
|
2406
|
+
margin: 0;
|
|
2407
|
+
box-sizing: border-box;
|
|
2408
|
+
overflow: hidden;
|
|
2409
|
+
-webkit-print-color-adjust: exact;
|
|
2410
|
+
print-color-adjust: exact;
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
`;
|
|
2414
|
+
const html = ` <section class="markforge-back-cover back-${backCover.preset}">
|
|
2415
|
+
<div class="back-top">
|
|
2416
|
+
${logoHtml}
|
|
2417
|
+
${badgeHtml}
|
|
2418
|
+
</div>
|
|
2419
|
+
<div class="back-body">
|
|
2420
|
+
${titleHtml}
|
|
2421
|
+
${subtitleHtml}
|
|
2422
|
+
${contactHtml}
|
|
2423
|
+
</div>
|
|
2424
|
+
${copyrightHtml}
|
|
2425
|
+
</section>
|
|
2426
|
+
`;
|
|
2427
|
+
return { html, css };
|
|
2428
|
+
}
|
|
2429
|
+
async function buildHtmlDocument(doc, config, baseDir = process.cwd()) {
|
|
2430
|
+
var _a, _b;
|
|
2431
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2432
|
+
const baseThemeCss = generateThemeCss(resolved.theme);
|
|
2433
|
+
let customCss = "";
|
|
2434
|
+
for (const cssPath of resolved.css) {
|
|
2435
|
+
const fullCssPath = path3.isAbsolute(cssPath) ? cssPath : path3.resolve(baseDir, cssPath);
|
|
2436
|
+
if (fs3.existsSync(fullCssPath)) {
|
|
2437
|
+
customCss += `
|
|
2438
|
+
/* Custom CSS: ${cssPath} */
|
|
2439
|
+
` + fs3.readFileSync(fullCssPath, "utf-8");
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
const inlinedCss = doc.inlinedStyles.join("\n");
|
|
2443
|
+
const extraCss = `
|
|
2444
|
+
.markforge-columns {
|
|
2445
|
+
display: grid;
|
|
2446
|
+
grid-template-columns: repeat(var(--cols, 2), minmax(0, 1fr));
|
|
2447
|
+
gap: var(--col-gap, 1.5rem);
|
|
2448
|
+
margin: 1.5rem 0;
|
|
2449
|
+
}
|
|
2450
|
+
.markforge-col {
|
|
2451
|
+
min-width: 0;
|
|
2452
|
+
}
|
|
2453
|
+
.markforge-fnref {
|
|
2454
|
+
text-decoration: none;
|
|
2455
|
+
font-size: 0.8em;
|
|
2456
|
+
vertical-align: super;
|
|
2457
|
+
color: #0D998D;
|
|
2458
|
+
font-weight: 700;
|
|
2459
|
+
}
|
|
2460
|
+
.markforge-footnotes {
|
|
2461
|
+
margin-top: 3rem;
|
|
2462
|
+
padding-top: 1rem;
|
|
2463
|
+
font-size: 0.88rem;
|
|
2464
|
+
color: #64748B;
|
|
2465
|
+
}
|
|
2466
|
+
.markforge-footnotes hr {
|
|
2467
|
+
border: 0;
|
|
2468
|
+
border-top: 1px solid #E2E8F0;
|
|
2469
|
+
margin-bottom: 1rem;
|
|
2470
|
+
}
|
|
2471
|
+
.markforge-fn-return {
|
|
2472
|
+
text-decoration: none;
|
|
2473
|
+
color: #0D998D;
|
|
2474
|
+
}
|
|
2475
|
+
.math-block {
|
|
2476
|
+
margin: 1.5rem 0;
|
|
2477
|
+
text-align: center;
|
|
2478
|
+
overflow-x: auto;
|
|
2479
|
+
}
|
|
2480
|
+
`;
|
|
2481
|
+
let coverHtml = "";
|
|
2482
|
+
let coverCss = "";
|
|
2483
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
2484
|
+
const coverRes = await renderCoverPageHtml(resolved.coverPage, baseDir);
|
|
2485
|
+
coverHtml = coverRes.html;
|
|
2486
|
+
coverCss = coverRes.css;
|
|
2487
|
+
}
|
|
2488
|
+
let backHtml = "";
|
|
2489
|
+
let backCss = "";
|
|
2490
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
2491
|
+
const backRes = await renderBackCoverHtml(resolved.backCover, baseDir);
|
|
2492
|
+
backHtml = backRes.html;
|
|
2493
|
+
backCss = backRes.css;
|
|
2494
|
+
}
|
|
2495
|
+
let bodyHtml = "";
|
|
2496
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2497
|
+
bodyHtml += ` <header class="document-header">
|
|
2498
|
+
`;
|
|
2499
|
+
bodyHtml += ` <h1 class="document-title">${escapeHtml(resolved.title)}</h1>
|
|
2500
|
+
`;
|
|
2501
|
+
if (resolved.subtitle) {
|
|
2502
|
+
bodyHtml += ` <div class="document-subtitle">${escapeHtml(resolved.subtitle)}</div>
|
|
2503
|
+
`;
|
|
2504
|
+
}
|
|
2505
|
+
if (resolved.author || resolved.date || resolved.version) {
|
|
2506
|
+
bodyHtml += ` <div class="document-meta">
|
|
2507
|
+
`;
|
|
2508
|
+
if (resolved.author) {
|
|
2509
|
+
bodyHtml += ` <span>Author: ${escapeHtml(resolved.author)}</span>
|
|
2510
|
+
`;
|
|
2511
|
+
}
|
|
2512
|
+
if (resolved.version) {
|
|
2513
|
+
bodyHtml += ` <span>Version: ${escapeHtml(resolved.version)}</span>
|
|
2514
|
+
`;
|
|
2515
|
+
}
|
|
2516
|
+
if (resolved.date) {
|
|
2517
|
+
bodyHtml += ` <span>Date: ${escapeHtml(resolved.date)}</span>
|
|
2518
|
+
`;
|
|
2519
|
+
}
|
|
2520
|
+
bodyHtml += ` </div>
|
|
2521
|
+
`;
|
|
2522
|
+
}
|
|
2523
|
+
bodyHtml += ` </header>
|
|
2524
|
+
`;
|
|
2525
|
+
}
|
|
2526
|
+
if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
|
|
2527
|
+
applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
|
|
2528
|
+
}
|
|
2529
|
+
if (resolved.toc && doc.tocEntries.length > 0) {
|
|
2530
|
+
bodyHtml += ` <nav class="table-of-contents">
|
|
2531
|
+
`;
|
|
2532
|
+
bodyHtml += ` <h2>Table of Contents</h2>
|
|
2533
|
+
<ul>
|
|
2534
|
+
`;
|
|
2535
|
+
for (const entry of doc.tocEntries) {
|
|
2536
|
+
const indent = " ".repeat(entry.level);
|
|
2537
|
+
bodyHtml += ` ${indent}<li><a href="#${entry.id}">${escapeHtml(entry.text)}</a></li>
|
|
2538
|
+
`;
|
|
2539
|
+
}
|
|
2540
|
+
bodyHtml += ` </ul>
|
|
2541
|
+
</nav>
|
|
2542
|
+
`;
|
|
2543
|
+
}
|
|
2544
|
+
const mergedTokens = {
|
|
2545
|
+
...config.metadata,
|
|
2546
|
+
...doc.metadata,
|
|
2547
|
+
...resolved,
|
|
2548
|
+
title: resolved.title,
|
|
2549
|
+
subtitle: resolved.subtitle,
|
|
2550
|
+
author: resolved.author,
|
|
2551
|
+
version: resolved.version,
|
|
2552
|
+
date: resolved.date,
|
|
2553
|
+
company: resolved.company
|
|
2554
|
+
};
|
|
2555
|
+
const nodesHtml = await renderNodesToHtml(doc.nodes, resolved, baseDir, mergedTokens);
|
|
2556
|
+
bodyHtml += ` <main class="markforge-content-body">
|
|
2557
|
+
${nodesHtml} </main>
|
|
2558
|
+
`;
|
|
2559
|
+
let footnotesHtml = "";
|
|
2560
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
2561
|
+
let fnListHtml = "";
|
|
2562
|
+
for (const def of doc.footnoteDefs) {
|
|
2563
|
+
const defInner = await renderInlinesToHtml(def.inlines, baseDir, mergedTokens);
|
|
2564
|
+
fnListHtml += ` <li id="fn-${escapeHtml(def.id)}">${defInner} <a href="#fnref-${escapeHtml(def.id)}" class="markforge-fn-return">↩</a></li>
|
|
2565
|
+
`;
|
|
2566
|
+
}
|
|
2567
|
+
footnotesHtml = `
|
|
2568
|
+
<footer class="markforge-footnotes">
|
|
2569
|
+
<hr />
|
|
2570
|
+
<ol>
|
|
2571
|
+
${fnListHtml} </ol>
|
|
2572
|
+
</footer>
|
|
2573
|
+
`;
|
|
2574
|
+
}
|
|
2575
|
+
let watermarkCss = "";
|
|
2576
|
+
let watermarkHtml = "";
|
|
2577
|
+
if (resolved.watermark) {
|
|
2578
|
+
const wm = resolved.watermark;
|
|
2579
|
+
watermarkCss = `
|
|
2580
|
+
.document-watermark {
|
|
2581
|
+
position: fixed;
|
|
2582
|
+
top: 0;
|
|
2583
|
+
left: 0;
|
|
2584
|
+
right: 0;
|
|
2585
|
+
bottom: 0;
|
|
2586
|
+
width: 100%;
|
|
2587
|
+
height: 100%;
|
|
2588
|
+
pointer-events: none;
|
|
2589
|
+
z-index: 0;
|
|
2590
|
+
user-select: none;
|
|
2591
|
+
-webkit-user-select: none;
|
|
2592
|
+
-webkit-print-color-adjust: exact;
|
|
2593
|
+
print-color-adjust: exact;
|
|
2594
|
+
}
|
|
2595
|
+
.document-container {
|
|
2596
|
+
position: relative;
|
|
2597
|
+
z-index: 1;
|
|
2598
|
+
}
|
|
2599
|
+
@media print {
|
|
2600
|
+
.document-watermark {
|
|
2601
|
+
display: none !important;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
`;
|
|
2605
|
+
watermarkHtml = ` <div id="markforge-watermark" class="document-watermark" aria-hidden="true"></div>
|
|
2606
|
+
<script>
|
|
2607
|
+
(function() {
|
|
2608
|
+
try {
|
|
2609
|
+
var canvas = document.createElement('canvas');
|
|
2610
|
+
var dpr = 2;
|
|
2611
|
+
var width = 1200;
|
|
2612
|
+
var height = 1600;
|
|
2613
|
+
canvas.width = width * dpr;
|
|
2614
|
+
canvas.height = height * dpr;
|
|
2615
|
+
var ctx = canvas.getContext('2d');
|
|
2616
|
+
if (ctx) {
|
|
2617
|
+
ctx.scale(dpr, dpr);
|
|
2618
|
+
ctx.translate(width / 2, height / 2);
|
|
2619
|
+
ctx.rotate((-Math.abs(${wm.rotate || 45}) * Math.PI) / 180);
|
|
2620
|
+
ctx.textAlign = 'center';
|
|
2621
|
+
ctx.textBaseline = 'middle';
|
|
2622
|
+
ctx.font = '900 ${wm.fontSize * 1.5}px system-ui, -apple-system, sans-serif';
|
|
2623
|
+
ctx.fillStyle = '${wm.color}';
|
|
2624
|
+
ctx.globalAlpha = ${wm.opacity};
|
|
2625
|
+
try { ctx.letterSpacing = '0.15em'; } catch(e) {}
|
|
2626
|
+
ctx.fillText(${JSON.stringify(wm.text.toUpperCase())}, 0, 0);
|
|
2627
|
+
var dataUrl = canvas.toDataURL('image/png');
|
|
2628
|
+
var wmEl = document.getElementById('markforge-watermark');
|
|
2629
|
+
if (wmEl) {
|
|
2630
|
+
wmEl.style.backgroundImage = 'url("' + dataUrl + '")';
|
|
2631
|
+
wmEl.style.backgroundRepeat = 'no-repeat';
|
|
2632
|
+
wmEl.style.backgroundPosition = 'center center';
|
|
2633
|
+
wmEl.style.backgroundSize = 'contain';
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
} catch(err) {}
|
|
1760
2637
|
})();
|
|
1761
2638
|
</script>
|
|
1762
2639
|
`;
|
|
@@ -1766,10 +2643,6 @@ ${escapeHtml(node.text || "")}
|
|
|
1766
2643
|
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
1767
2644
|
const sig = resolved.signatures;
|
|
1768
2645
|
const numItems = sig.items.length;
|
|
1769
|
-
let justifyCss = "flex-end";
|
|
1770
|
-
if (sig.align === "left") justifyCss = "flex-start";
|
|
1771
|
-
else if (sig.align === "center") justifyCss = "center";
|
|
1772
|
-
else if (sig.align === "space-between") justifyCss = "space-between";
|
|
1773
2646
|
signaturesCss = `
|
|
1774
2647
|
.markforge-signatures {
|
|
1775
2648
|
margin-top: ${sig.spacingBefore};
|
|
@@ -1881,6 +2754,10 @@ ${itemCards}
|
|
|
1881
2754
|
<style>
|
|
1882
2755
|
${THEME_COMPONENTS}
|
|
1883
2756
|
${baseThemeCss}
|
|
2757
|
+
${KATEX_INLINE_CSS}
|
|
2758
|
+
${extraCss}
|
|
2759
|
+
${coverCss}
|
|
2760
|
+
${backCss}
|
|
1884
2761
|
${customCss}
|
|
1885
2762
|
${inlinedCss}
|
|
1886
2763
|
${watermarkCss}
|
|
@@ -1888,14 +2765,95 @@ ${signaturesCss}
|
|
|
1888
2765
|
</style>
|
|
1889
2766
|
</head>
|
|
1890
2767
|
<body>
|
|
1891
|
-
${watermarkHtml} <div class="document-container">
|
|
1892
|
-
${bodyHtml}${signaturesHtml} </div>
|
|
2768
|
+
${watermarkHtml}${coverHtml} <div class="document-container">
|
|
2769
|
+
${bodyHtml}${footnotesHtml}${signaturesHtml} </div>
|
|
1893
2770
|
${mermaidScript}
|
|
1894
|
-
</body>
|
|
2771
|
+
${backHtml}</body>
|
|
1895
2772
|
</html>`;
|
|
1896
2773
|
}
|
|
1897
2774
|
|
|
1898
2775
|
// src/core/pdf/pdfBuilder.ts
|
|
2776
|
+
function escapeXml(str) {
|
|
2777
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2778
|
+
}
|
|
2779
|
+
function generateWatermarkPngBuffer(chromePath, wm) {
|
|
2780
|
+
const tmpHtml = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.html`);
|
|
2781
|
+
const tmpPng = path4.join(os.tmpdir(), `markforge-wm-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
|
|
2782
|
+
try {
|
|
2783
|
+
const text = escapeXml(wm.text.toUpperCase());
|
|
2784
|
+
const fontSize = (wm.fontSize || 52) * 1.5;
|
|
2785
|
+
const color = wm.color || "#E11D48";
|
|
2786
|
+
const opacity = wm.opacity !== void 0 ? wm.opacity : 0.12;
|
|
2787
|
+
const rotate = wm.rotate !== void 0 ? wm.rotate : -45;
|
|
2788
|
+
const html = `<!DOCTYPE html>
|
|
2789
|
+
<html>
|
|
2790
|
+
<head>
|
|
2791
|
+
<meta charset="utf-8">
|
|
2792
|
+
<style>
|
|
2793
|
+
html, body {
|
|
2794
|
+
margin: 0;
|
|
2795
|
+
padding: 0;
|
|
2796
|
+
width: 1200px;
|
|
2797
|
+
height: 1600px;
|
|
2798
|
+
background: transparent;
|
|
2799
|
+
overflow: hidden;
|
|
2800
|
+
}
|
|
2801
|
+
.wm-box {
|
|
2802
|
+
width: 1200px;
|
|
2803
|
+
height: 1600px;
|
|
2804
|
+
display: flex;
|
|
2805
|
+
align-items: center;
|
|
2806
|
+
justify-content: center;
|
|
2807
|
+
transform: rotate(${rotate}deg);
|
|
2808
|
+
}
|
|
2809
|
+
.wm-text {
|
|
2810
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
2811
|
+
font-weight: 900;
|
|
2812
|
+
font-size: ${fontSize}px;
|
|
2813
|
+
color: ${color};
|
|
2814
|
+
opacity: ${opacity};
|
|
2815
|
+
letter-spacing: 0.15em;
|
|
2816
|
+
text-transform: uppercase;
|
|
2817
|
+
white-space: nowrap;
|
|
2818
|
+
}
|
|
2819
|
+
</style>
|
|
2820
|
+
</head>
|
|
2821
|
+
<body>
|
|
2822
|
+
<div class="wm-box"><span class="wm-text">${text}</span></div>
|
|
2823
|
+
</body>
|
|
2824
|
+
</html>`;
|
|
2825
|
+
fs4.writeFileSync(tmpHtml, html, "utf8");
|
|
2826
|
+
const fileUrl = pathToFileURL2(tmpHtml).href;
|
|
2827
|
+
const isWin = process.platform === "win32";
|
|
2828
|
+
spawnSync(
|
|
2829
|
+
chromePath,
|
|
2830
|
+
[
|
|
2831
|
+
"--headless=new",
|
|
2832
|
+
"--disable-gpu",
|
|
2833
|
+
"--disable-sync",
|
|
2834
|
+
"--disable-extensions",
|
|
2835
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
2836
|
+
`--screenshot=${tmpPng}`,
|
|
2837
|
+
"--window-size=1200,1600",
|
|
2838
|
+
"--default-background-color=00000000",
|
|
2839
|
+
fileUrl
|
|
2840
|
+
],
|
|
2841
|
+
{ timeout: 15e3, windowsHide: true }
|
|
2842
|
+
);
|
|
2843
|
+
if (fs4.existsSync(tmpPng) && fs4.statSync(tmpPng).size > 0) {
|
|
2844
|
+
return fs4.readFileSync(tmpPng);
|
|
2845
|
+
}
|
|
2846
|
+
return null;
|
|
2847
|
+
} catch {
|
|
2848
|
+
return null;
|
|
2849
|
+
} finally {
|
|
2850
|
+
try {
|
|
2851
|
+
if (fs4.existsSync(tmpHtml)) fs4.unlinkSync(tmpHtml);
|
|
2852
|
+
if (fs4.existsSync(tmpPng)) fs4.unlinkSync(tmpPng);
|
|
2853
|
+
} catch {
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
1899
2857
|
function findChromeExecutable() {
|
|
1900
2858
|
if (process.env.CHROME_PATH && fs4.existsSync(process.env.CHROME_PATH)) {
|
|
1901
2859
|
return process.env.CHROME_PATH;
|
|
@@ -1960,7 +2918,7 @@ function findChromeExecutable() {
|
|
|
1960
2918
|
return null;
|
|
1961
2919
|
}
|
|
1962
2920
|
function injectPagedMediaStyles(html, config, metadata) {
|
|
1963
|
-
var _a, _b, _c, _d, _e, _f;
|
|
2921
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
1964
2922
|
const resolved = resolveDocumentConfig(metadata || {}, config);
|
|
1965
2923
|
const size = resolved.paperSize;
|
|
1966
2924
|
const orientation = resolved.orientation;
|
|
@@ -2004,6 +2962,66 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
2004
2962
|
${fontStyle}
|
|
2005
2963
|
}`;
|
|
2006
2964
|
};
|
|
2965
|
+
const coverPageCss = ((_a = resolved.coverPage) == null ? void 0 : _a.enabled) ? `
|
|
2966
|
+
@page :first {
|
|
2967
|
+
margin-top: 0;
|
|
2968
|
+
margin-bottom: 0;
|
|
2969
|
+
margin-left: 0;
|
|
2970
|
+
margin-right: 0;
|
|
2971
|
+
background-image: none !important;
|
|
2972
|
+
@top-left { content: none; }
|
|
2973
|
+
@top-center { content: none; }
|
|
2974
|
+
@top-right { content: none; }
|
|
2975
|
+
@bottom-left { content: none; }
|
|
2976
|
+
@bottom-center { content: none; }
|
|
2977
|
+
@bottom-right { content: none; }
|
|
2978
|
+
}` : "";
|
|
2979
|
+
const backCoverCss = ((_b = resolved.backCover) == null ? void 0 : _b.enabled) ? `
|
|
2980
|
+
@page back-cover-page {
|
|
2981
|
+
size: ${size} ${orientation};
|
|
2982
|
+
margin: 0;
|
|
2983
|
+
background-image: none !important;
|
|
2984
|
+
@top-left { content: none; }
|
|
2985
|
+
@top-center { content: none; }
|
|
2986
|
+
@top-right { content: none; }
|
|
2987
|
+
@bottom-left { content: none; }
|
|
2988
|
+
@bottom-center { content: none; }
|
|
2989
|
+
@bottom-right { content: none; }
|
|
2990
|
+
}
|
|
2991
|
+
.markforge-back-cover {
|
|
2992
|
+
page: back-cover-page;
|
|
2993
|
+
min-height: 100vh;
|
|
2994
|
+
height: 100vh;
|
|
2995
|
+
box-sizing: border-box;
|
|
2996
|
+
break-before: page;
|
|
2997
|
+
break-after: avoid;
|
|
2998
|
+
}` : "";
|
|
2999
|
+
const tocPageCss = resolved.toc ? `
|
|
3000
|
+
@page toc-page {
|
|
3001
|
+
size: ${size} ${orientation};
|
|
3002
|
+
margin-top: ${top};
|
|
3003
|
+
margin-bottom: ${bottom};
|
|
3004
|
+
margin-left: ${left};
|
|
3005
|
+
margin-right: ${right};
|
|
3006
|
+
${buildZoneCss("top-left", (_c = resolved.header) == null ? void 0 : _c.left)}
|
|
3007
|
+
${buildZoneCss("top-center", (_d = resolved.header) == null ? void 0 : _d.center)}
|
|
3008
|
+
${buildZoneCss("top-right", (_e = resolved.header) == null ? void 0 : _e.right)}
|
|
3009
|
+
${buildZoneCss("bottom-left", (_f = resolved.footer) == null ? void 0 : _f.left)}
|
|
3010
|
+
${buildZoneCss("bottom-center", (_g = resolved.footer) == null ? void 0 : _g.center)}
|
|
3011
|
+
@bottom-right {
|
|
3012
|
+
content: counter(page, lower-roman);
|
|
3013
|
+
font-size: 9pt;
|
|
3014
|
+
color: #94a3b8;
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
.table-of-contents {
|
|
3018
|
+
page: toc-page;
|
|
3019
|
+
page-break-after: always;
|
|
3020
|
+
break-after: page;
|
|
3021
|
+
}
|
|
3022
|
+
.markforge-content-body {
|
|
3023
|
+
counter-reset: page 1;
|
|
3024
|
+
}` : "";
|
|
2007
3025
|
const pagedCss = `
|
|
2008
3026
|
@page {
|
|
2009
3027
|
size: ${size} ${orientation};
|
|
@@ -2011,15 +3029,19 @@ function injectPagedMediaStyles(html, config, metadata) {
|
|
|
2011
3029
|
margin-bottom: ${bottom};
|
|
2012
3030
|
margin-left: ${left};
|
|
2013
3031
|
margin-right: ${right};
|
|
2014
|
-
${buildZoneCss("top-left", (
|
|
2015
|
-
${buildZoneCss("top-center", (
|
|
2016
|
-
${buildZoneCss("top-right", (
|
|
2017
|
-
${buildZoneCss("bottom-left", (
|
|
2018
|
-
${buildZoneCss("bottom-center", (
|
|
2019
|
-
${buildZoneCss("bottom-right", (
|
|
3032
|
+
${buildZoneCss("top-left", (_h = resolved.header) == null ? void 0 : _h.left)}
|
|
3033
|
+
${buildZoneCss("top-center", (_i = resolved.header) == null ? void 0 : _i.center)}
|
|
3034
|
+
${buildZoneCss("top-right", (_j = resolved.header) == null ? void 0 : _j.right)}
|
|
3035
|
+
${buildZoneCss("bottom-left", (_k = resolved.footer) == null ? void 0 : _k.left)}
|
|
3036
|
+
${buildZoneCss("bottom-center", (_l = resolved.footer) == null ? void 0 : _l.center)}
|
|
3037
|
+
${buildZoneCss("bottom-right", (_m = resolved.footer) == null ? void 0 : _m.right, true)}
|
|
2020
3038
|
}
|
|
3039
|
+
${coverPageCss}
|
|
3040
|
+
${tocPageCss}
|
|
3041
|
+
${backCoverCss}
|
|
2021
3042
|
@media print {
|
|
2022
3043
|
body { padding: 0; }
|
|
3044
|
+
.document-watermark { display: none !important; }
|
|
2023
3045
|
h1, h2, h3, pre, table, blockquote, .callout {
|
|
2024
3046
|
break-inside: avoid;
|
|
2025
3047
|
}
|
|
@@ -2067,6 +3089,7 @@ startxref
|
|
|
2067
3089
|
return Buffer.from(pdfBody, "utf-8");
|
|
2068
3090
|
}
|
|
2069
3091
|
async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
3092
|
+
var _a, _b, _c;
|
|
2070
3093
|
const baseHtml = await buildHtmlDocument(doc, config, baseDir);
|
|
2071
3094
|
const pagedHtml = injectPagedMediaStyles(baseHtml, config, doc.metadata);
|
|
2072
3095
|
const chromePath = findChromeExecutable();
|
|
@@ -2076,6 +3099,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2076
3099
|
const tmpHtml = path4.join(tmpDir, `markforge_${tmpId}.html`);
|
|
2077
3100
|
const tmpPdf = path4.join(tmpDir, `markforge_${tmpId}.pdf`);
|
|
2078
3101
|
const tmpProfile = path4.join(tmpDir, `markforge_prof_${tmpId}`);
|
|
3102
|
+
const isWin = process.platform === "win32";
|
|
2079
3103
|
const isolatedFlags = [
|
|
2080
3104
|
`--user-data-dir=${tmpProfile}`,
|
|
2081
3105
|
"--no-first-run",
|
|
@@ -2086,7 +3110,6 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2086
3110
|
"--disable-default-apps",
|
|
2087
3111
|
"--disable-extensions",
|
|
2088
3112
|
"--disable-domain-reliability",
|
|
2089
|
-
"--disable-client-side-phishing-detection",
|
|
2090
3113
|
"--disable-breakpad",
|
|
2091
3114
|
"--disable-component-extensions-with-background-pages",
|
|
2092
3115
|
"--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculatedNewTabPage,ChromeWhatsNewUI,PrivacySandboxSettings4",
|
|
@@ -2095,12 +3118,10 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2095
3118
|
"--mute-audio",
|
|
2096
3119
|
"--no-service-autorun",
|
|
2097
3120
|
"--disable-gpu",
|
|
2098
|
-
"--no-sandbox",
|
|
2099
|
-
"--disable-setuid-sandbox",
|
|
2100
|
-
"--allow-file-access-from-files",
|
|
2101
|
-
"--disable-web-security",
|
|
3121
|
+
...isWin ? [] : ["--no-sandbox", "--disable-setuid-sandbox"],
|
|
2102
3122
|
"--force-color-profile=srgb",
|
|
2103
|
-
"--no-pdf-header-footer"
|
|
3123
|
+
"--no-pdf-header-footer",
|
|
3124
|
+
"--window-size=1200,1600"
|
|
2104
3125
|
];
|
|
2105
3126
|
try {
|
|
2106
3127
|
fs4.writeFileSync(tmpHtml, pagedHtml, "utf-8");
|
|
@@ -2115,7 +3136,7 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2115
3136
|
`--print-to-pdf=${tmpPdf}`,
|
|
2116
3137
|
fileUrl
|
|
2117
3138
|
],
|
|
2118
|
-
{ timeout: 3e4 }
|
|
3139
|
+
{ timeout: 3e4, windowsHide: true }
|
|
2119
3140
|
);
|
|
2120
3141
|
if ((res.status !== 0 || !fs4.existsSync(tmpPdf)) && chromePath) {
|
|
2121
3142
|
res = spawnSync(
|
|
@@ -2126,12 +3147,79 @@ async function buildPdfDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2126
3147
|
`--print-to-pdf=${tmpPdf}`,
|
|
2127
3148
|
fileUrl
|
|
2128
3149
|
],
|
|
2129
|
-
{ timeout: 3e4 }
|
|
3150
|
+
{ timeout: 3e4, windowsHide: true }
|
|
2130
3151
|
);
|
|
2131
3152
|
}
|
|
2132
3153
|
if (fs4.existsSync(tmpPdf) && fs4.statSync(tmpPdf).size > 0) {
|
|
2133
3154
|
const pdfBuffer = fs4.readFileSync(tmpPdf);
|
|
2134
|
-
|
|
3155
|
+
try {
|
|
3156
|
+
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
|
3157
|
+
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
3158
|
+
if (resolved.title) pdfDoc.setTitle(resolved.title);
|
|
3159
|
+
if (resolved.author) pdfDoc.setAuthor(resolved.author);
|
|
3160
|
+
if (resolved.subtitle) pdfDoc.setSubject(resolved.subtitle);
|
|
3161
|
+
pdfDoc.setCreator("MarkForge Enterprise Document Generator");
|
|
3162
|
+
pdfDoc.setProducer("MarkForge (by Ma'sum)");
|
|
3163
|
+
pdfDoc.setModificationDate(/* @__PURE__ */ new Date());
|
|
3164
|
+
if (((_a = resolved.backCover) == null ? void 0 : _a.enabled) && pdfDoc.getPageCount() > 2) {
|
|
3165
|
+
pdfDoc.removePage(pdfDoc.getPageCount() - 1);
|
|
3166
|
+
}
|
|
3167
|
+
if (resolved.watermark) {
|
|
3168
|
+
const wmPng = generateWatermarkPngBuffer(chromePath, resolved.watermark);
|
|
3169
|
+
if (wmPng) {
|
|
3170
|
+
const embeddedPng = await pdfDoc.embedPng(wmPng);
|
|
3171
|
+
const totalPages = pdfDoc.getPageCount();
|
|
3172
|
+
const pages = pdfDoc.getPages();
|
|
3173
|
+
const startPageIndex = ((_b = resolved.coverPage) == null ? void 0 : _b.enabled) ? 1 : 0;
|
|
3174
|
+
const endPageIndex = ((_c = resolved.backCover) == null ? void 0 : _c.enabled) ? totalPages - 1 : totalPages;
|
|
3175
|
+
for (let i = startPageIndex; i < endPageIndex; i++) {
|
|
3176
|
+
const page = pages[i];
|
|
3177
|
+
const { width, height } = page.getSize();
|
|
3178
|
+
page.drawImage(embeddedPng, {
|
|
3179
|
+
x: 0,
|
|
3180
|
+
y: 0,
|
|
3181
|
+
width,
|
|
3182
|
+
height
|
|
3183
|
+
});
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
const savedBytes = await pdfDoc.save();
|
|
3188
|
+
let finalBuffer = Buffer.from(savedBytes);
|
|
3189
|
+
if (resolved.security) {
|
|
3190
|
+
const sec = resolved.security;
|
|
3191
|
+
const hasUserPassword = typeof sec.userPassword === "string" && sec.userPassword.length > 0;
|
|
3192
|
+
const hasOwnerPassword = typeof sec.ownerPassword === "string" && sec.ownerPassword.length > 0;
|
|
3193
|
+
if (hasUserPassword || hasOwnerPassword) {
|
|
3194
|
+
try {
|
|
3195
|
+
const userPass = sec.userPassword ?? "";
|
|
3196
|
+
const ownerPass = sec.ownerPassword ?? userPass;
|
|
3197
|
+
const perms = sec.permissions;
|
|
3198
|
+
const encryptedBytes = await encryptPDF(
|
|
3199
|
+
new Uint8Array(finalBuffer),
|
|
3200
|
+
userPass,
|
|
3201
|
+
{
|
|
3202
|
+
ownerPassword: ownerPass,
|
|
3203
|
+
algorithm: "AES-256",
|
|
3204
|
+
allowPrinting: (perms == null ? void 0 : perms.printing) !== "none",
|
|
3205
|
+
allowHighQualityPrint: (perms == null ? void 0 : perms.printing) === "highResolution",
|
|
3206
|
+
allowModifying: (perms == null ? void 0 : perms.modifying) ?? true,
|
|
3207
|
+
allowCopying: (perms == null ? void 0 : perms.copying) ?? true,
|
|
3208
|
+
allowAnnotating: (perms == null ? void 0 : perms.annotating) ?? true,
|
|
3209
|
+
allowFillingForms: (perms == null ? void 0 : perms.fillingForms) ?? true,
|
|
3210
|
+
allowExtraction: (perms == null ? void 0 : perms.contentAccessibility) ?? true,
|
|
3211
|
+
allowAssembly: (perms == null ? void 0 : perms.documentAssembly) ?? true
|
|
3212
|
+
}
|
|
3213
|
+
);
|
|
3214
|
+
finalBuffer = Buffer.from(encryptedBytes);
|
|
3215
|
+
} catch {
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
return finalBuffer;
|
|
3220
|
+
} catch {
|
|
3221
|
+
return pdfBuffer;
|
|
3222
|
+
}
|
|
2135
3223
|
}
|
|
2136
3224
|
} catch {
|
|
2137
3225
|
} finally {
|
|
@@ -2386,6 +3474,29 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2386
3474
|
);
|
|
2387
3475
|
continue;
|
|
2388
3476
|
}
|
|
3477
|
+
if (span.type === "mathInline") {
|
|
3478
|
+
runs.push(
|
|
3479
|
+
new TextRun({
|
|
3480
|
+
text: span.content,
|
|
3481
|
+
font: "Cambria Math",
|
|
3482
|
+
italics: true,
|
|
3483
|
+
size: options.size,
|
|
3484
|
+
color: options.color || "0F172A"
|
|
3485
|
+
})
|
|
3486
|
+
);
|
|
3487
|
+
continue;
|
|
3488
|
+
}
|
|
3489
|
+
if (span.type === "footnoteRef") {
|
|
3490
|
+
runs.push(
|
|
3491
|
+
new TextRun({
|
|
3492
|
+
text: `[${span.content}]`,
|
|
3493
|
+
superScript: true,
|
|
3494
|
+
color: "009DA0",
|
|
3495
|
+
bold: true
|
|
3496
|
+
})
|
|
3497
|
+
);
|
|
3498
|
+
continue;
|
|
3499
|
+
}
|
|
2389
3500
|
runs.push(
|
|
2390
3501
|
new TextRun({
|
|
2391
3502
|
text: span.content,
|
|
@@ -2400,7 +3511,7 @@ async function convertInlinesToTextRuns(spans = [], baseDir = process.cwd(), opt
|
|
|
2400
3511
|
return runs;
|
|
2401
3512
|
}
|
|
2402
3513
|
async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
2403
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
3514
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2404
3515
|
const resolved = resolveDocumentConfig(doc.metadata, config);
|
|
2405
3516
|
const docElements = [];
|
|
2406
3517
|
const themeProps = typeof resolved.theme === "object" ? resolved.theme : {};
|
|
@@ -2411,7 +3522,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2411
3522
|
const borderHex = (themeProps.borderColor || "#E2E8F0").replace("#", "");
|
|
2412
3523
|
const cardBgHex = (themeProps.cardBackground || "#F8FAFC").replace("#", "");
|
|
2413
3524
|
const defaultFont = themeProps.fontFamily ? themeProps.fontFamily.split(",")[0].replace(/['"]/g, "").trim() : "Segoe UI";
|
|
2414
|
-
if (resolved.title) {
|
|
3525
|
+
if (resolved.title && !((_a = resolved.coverPage) == null ? void 0 : _a.enabled)) {
|
|
2415
3526
|
docElements.push(
|
|
2416
3527
|
new Paragraph({
|
|
2417
3528
|
children: [
|
|
@@ -2472,6 +3583,9 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2472
3583
|
);
|
|
2473
3584
|
}
|
|
2474
3585
|
}
|
|
3586
|
+
if (((_b = resolved.numberHeadings) == null ? void 0 : _b.enabled) !== false && resolved.numberHeadings) {
|
|
3587
|
+
applyHeadingNumbering(doc.nodes, doc.tocEntries, resolved.numberHeadings);
|
|
3588
|
+
}
|
|
2475
3589
|
if (resolved.toc) {
|
|
2476
3590
|
const headingNodes = doc.nodes.filter(
|
|
2477
3591
|
(n) => n.type === "heading" && typeof n.level === "number" && n.level >= 1 && n.level <= 3
|
|
@@ -2541,7 +3655,11 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2541
3655
|
]
|
|
2542
3656
|
});
|
|
2543
3657
|
docElements.push(tocCard);
|
|
2544
|
-
docElements.push(
|
|
3658
|
+
docElements.push(
|
|
3659
|
+
new Paragraph({
|
|
3660
|
+
children: [new PageBreak()]
|
|
3661
|
+
})
|
|
3662
|
+
);
|
|
2545
3663
|
}
|
|
2546
3664
|
}
|
|
2547
3665
|
for (const node of doc.nodes) {
|
|
@@ -2551,7 +3669,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2551
3669
|
font: defaultFont,
|
|
2552
3670
|
size: 34,
|
|
2553
3671
|
// 17pt
|
|
2554
|
-
color:
|
|
3672
|
+
color: primaryDarkHex,
|
|
2555
3673
|
bold: true
|
|
2556
3674
|
});
|
|
2557
3675
|
docElements.push(
|
|
@@ -2597,7 +3715,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2597
3715
|
font: defaultFont,
|
|
2598
3716
|
size: 22,
|
|
2599
3717
|
// 11pt
|
|
2600
|
-
color:
|
|
3718
|
+
color: primaryDarkHex,
|
|
2601
3719
|
bold: true
|
|
2602
3720
|
});
|
|
2603
3721
|
docElements.push(
|
|
@@ -2827,7 +3945,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2827
3945
|
}
|
|
2828
3946
|
if (node.type === "table" && node.children) {
|
|
2829
3947
|
const tableRows = [];
|
|
2830
|
-
const numCols = ((
|
|
3948
|
+
const numCols = ((_d = (_c = node.children[0]) == null ? void 0 : _c.children) == null ? void 0 : _d.length) || 1;
|
|
2831
3949
|
const colWidth = Math.floor(9e3 / numCols);
|
|
2832
3950
|
for (let rowIdx = 0; rowIdx < node.children.length; rowIdx++) {
|
|
2833
3951
|
const rowNode = node.children[rowIdx];
|
|
@@ -2837,7 +3955,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2837
3955
|
if (rowNode.children) {
|
|
2838
3956
|
for (let colIdx = 0; colIdx < rowNode.children.length; colIdx++) {
|
|
2839
3957
|
const cellNode = rowNode.children[colIdx];
|
|
2840
|
-
const align = (
|
|
3958
|
+
const align = (_e = node.align) == null ? void 0 : _e[colIdx];
|
|
2841
3959
|
let alignment = AlignmentType.LEFT;
|
|
2842
3960
|
if (align === "center") alignment = AlignmentType.CENTER;
|
|
2843
3961
|
if (align === "right") alignment = AlignmentType.RIGHT;
|
|
@@ -2976,16 +4094,123 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
2976
4094
|
}
|
|
2977
4095
|
continue;
|
|
2978
4096
|
}
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
4097
|
+
if (node.type === "mathBlock") {
|
|
4098
|
+
docElements.push(
|
|
4099
|
+
new Paragraph({
|
|
4100
|
+
alignment: AlignmentType.CENTER,
|
|
4101
|
+
children: [
|
|
4102
|
+
new TextRun({
|
|
4103
|
+
text: node.text || "",
|
|
4104
|
+
font: "Cambria Math",
|
|
4105
|
+
italics: true,
|
|
4106
|
+
size: 24,
|
|
4107
|
+
// 12pt
|
|
4108
|
+
color: textHex
|
|
4109
|
+
})
|
|
4110
|
+
],
|
|
4111
|
+
spacing: { before: 180, after: 180 },
|
|
4112
|
+
shading: { fill: cardBgHex, type: ShadingType.CLEAR },
|
|
4113
|
+
border: {
|
|
4114
|
+
top: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4115
|
+
bottom: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4116
|
+
left: { style: BorderStyle.SINGLE, size: 4, color: borderHex },
|
|
4117
|
+
right: { style: BorderStyle.SINGLE, size: 4, color: borderHex }
|
|
4118
|
+
}
|
|
4119
|
+
})
|
|
4120
|
+
);
|
|
4121
|
+
continue;
|
|
4122
|
+
}
|
|
4123
|
+
if (node.type === "columns") {
|
|
4124
|
+
const cols = node.columnsCount || 2;
|
|
4125
|
+
const contentWidth = Math.max(
|
|
4126
|
+
1e3,
|
|
4127
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4128
|
+
);
|
|
4129
|
+
const cellWidthDxa = Math.floor(contentWidth / cols);
|
|
4130
|
+
const cells = [];
|
|
4131
|
+
for (const col of node.children || []) {
|
|
4132
|
+
const colParagraphs = [];
|
|
4133
|
+
for (const childNode of col.children || []) {
|
|
4134
|
+
if (childNode.type === "heading") {
|
|
4135
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, bold: true, size: 24, color: primaryDarkHex });
|
|
4136
|
+
colParagraphs.push(new Paragraph({ children: runs, spacing: { before: 120, after: 60 } }));
|
|
4137
|
+
} else if (childNode.type === "paragraph") {
|
|
4138
|
+
const runs = await convertInlinesToTextRuns(childNode.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
4139
|
+
colParagraphs.push(new Paragraph({ children: runs, spacing: { after: 100 } }));
|
|
4140
|
+
} else if (childNode.type === "list" && childNode.children) {
|
|
4141
|
+
for (const item of childNode.children) {
|
|
4142
|
+
const runs = await convertInlinesToTextRuns(item.inlines, baseDir, { font: defaultFont, size: 21, color: textHex });
|
|
4143
|
+
colParagraphs.push(new Paragraph({ children: [new TextRun({ text: "\u2022 ", font: defaultFont, color: primaryHex }), ...runs], spacing: { after: 40 } }));
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
}
|
|
4147
|
+
if (colParagraphs.length === 0) colParagraphs.push(new Paragraph({}));
|
|
4148
|
+
cells.push(
|
|
4149
|
+
new TableCell({
|
|
4150
|
+
width: { size: cellWidthDxa, type: WidthType.DXA },
|
|
4151
|
+
borders: {
|
|
4152
|
+
top: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4153
|
+
bottom: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4154
|
+
left: { style: BorderStyle.NONE, size: 0, color: "auto" },
|
|
4155
|
+
right: { style: BorderStyle.NONE, size: 0, color: "auto" }
|
|
4156
|
+
},
|
|
4157
|
+
margins: { top: 60, bottom: 60, left: 100, right: 100 },
|
|
4158
|
+
children: colParagraphs
|
|
4159
|
+
})
|
|
4160
|
+
);
|
|
4161
|
+
}
|
|
4162
|
+
docElements.push(
|
|
4163
|
+
new Table({
|
|
4164
|
+
width: { size: 100, type: WidthType.PERCENTAGE },
|
|
4165
|
+
rows: [new TableRow({ children: cells })]
|
|
4166
|
+
})
|
|
4167
|
+
);
|
|
4168
|
+
docElements.push(new Paragraph({ spacing: { after: 120 } }));
|
|
4169
|
+
continue;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
if (doc.footnoteDefs && doc.footnoteDefs.length > 0) {
|
|
4173
|
+
docElements.push(
|
|
4174
|
+
new Paragraph({
|
|
4175
|
+
border: {
|
|
4176
|
+
top: { style: BorderStyle.SINGLE, size: 4, color: borderHex, space: 8 }
|
|
4177
|
+
},
|
|
4178
|
+
spacing: { before: 360, after: 120 }
|
|
4179
|
+
})
|
|
4180
|
+
);
|
|
4181
|
+
for (const def of doc.footnoteDefs) {
|
|
4182
|
+
const defRuns = await convertInlinesToTextRuns(def.inlines, baseDir, {
|
|
4183
|
+
font: defaultFont,
|
|
4184
|
+
size: 18,
|
|
4185
|
+
// 9pt
|
|
4186
|
+
color: textMutedHex
|
|
4187
|
+
});
|
|
4188
|
+
docElements.push(
|
|
4189
|
+
new Paragraph({
|
|
4190
|
+
children: [
|
|
4191
|
+
new TextRun({
|
|
4192
|
+
text: `[${def.id}] `,
|
|
4193
|
+
bold: true,
|
|
4194
|
+
color: primaryDarkHex,
|
|
4195
|
+
font: defaultFont,
|
|
4196
|
+
size: 18
|
|
4197
|
+
}),
|
|
4198
|
+
...defRuns
|
|
4199
|
+
],
|
|
4200
|
+
spacing: { after: 60 }
|
|
4201
|
+
})
|
|
4202
|
+
);
|
|
4203
|
+
}
|
|
4204
|
+
}
|
|
4205
|
+
if (resolved.signatures && resolved.signatures.items.length > 0) {
|
|
4206
|
+
const sig = resolved.signatures;
|
|
4207
|
+
const numItems = sig.items.length;
|
|
4208
|
+
const contentWidth = Math.max(
|
|
4209
|
+
1e3,
|
|
4210
|
+
resolved.paperDimensions.widthTwip - resolved.margins.leftTwip - resolved.margins.rightTwip
|
|
4211
|
+
);
|
|
4212
|
+
docElements.push(new Paragraph({ spacing: { before: sig.spacingBeforeTwip } }));
|
|
4213
|
+
const sigCells = [];
|
|
2989
4214
|
const colWidths = [];
|
|
2990
4215
|
if (numItems === 1) {
|
|
2991
4216
|
const cardWidth = Math.min(3400, Math.floor(contentWidth * 0.42));
|
|
@@ -3037,7 +4262,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3037
4262
|
const centerPos = Math.round(contentWidthTwip / 2);
|
|
3038
4263
|
const rightPos = contentWidthTwip;
|
|
3039
4264
|
const headerRuns = [];
|
|
3040
|
-
if ((
|
|
4265
|
+
if ((_f = resolved.header) == null ? void 0 : _f.left) {
|
|
3041
4266
|
headerRuns.push(
|
|
3042
4267
|
new TextRun({
|
|
3043
4268
|
text: resolved.header.left.text,
|
|
@@ -3050,7 +4275,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3050
4275
|
);
|
|
3051
4276
|
}
|
|
3052
4277
|
headerRuns.push(new TextRun({ text: " " }));
|
|
3053
|
-
if ((
|
|
4278
|
+
if ((_g = resolved.header) == null ? void 0 : _g.center) {
|
|
3054
4279
|
headerRuns.push(
|
|
3055
4280
|
new TextRun({
|
|
3056
4281
|
text: resolved.header.center.text,
|
|
@@ -3063,7 +4288,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3063
4288
|
);
|
|
3064
4289
|
}
|
|
3065
4290
|
headerRuns.push(new TextRun({ text: " " }));
|
|
3066
|
-
if ((
|
|
4291
|
+
if ((_h = resolved.header) == null ? void 0 : _h.right) {
|
|
3067
4292
|
headerRuns.push(
|
|
3068
4293
|
new TextRun({
|
|
3069
4294
|
text: resolved.header.right.text,
|
|
@@ -3102,7 +4327,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3102
4327
|
]
|
|
3103
4328
|
}) : void 0;
|
|
3104
4329
|
const footerRuns = [];
|
|
3105
|
-
if ((
|
|
4330
|
+
if ((_i = resolved.footer) == null ? void 0 : _i.left) {
|
|
3106
4331
|
footerRuns.push(
|
|
3107
4332
|
new TextRun({
|
|
3108
4333
|
text: resolved.footer.left.text,
|
|
@@ -3115,7 +4340,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3115
4340
|
);
|
|
3116
4341
|
}
|
|
3117
4342
|
footerRuns.push(new TextRun({ text: " " }));
|
|
3118
|
-
if ((
|
|
4343
|
+
if ((_j = resolved.footer) == null ? void 0 : _j.center) {
|
|
3119
4344
|
footerRuns.push(
|
|
3120
4345
|
new TextRun({
|
|
3121
4346
|
text: resolved.footer.center.text,
|
|
@@ -3128,7 +4353,7 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3128
4353
|
);
|
|
3129
4354
|
}
|
|
3130
4355
|
footerRuns.push(new TextRun({ text: " " }));
|
|
3131
|
-
if ((
|
|
4356
|
+
if ((_k = resolved.footer) == null ? void 0 : _k.right) {
|
|
3132
4357
|
const rZone = resolved.footer.right;
|
|
3133
4358
|
const rColor = rZone.color.replace("#", "");
|
|
3134
4359
|
const rSize = (rZone.fontSize || 9) * 2;
|
|
@@ -3213,6 +4438,95 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3213
4438
|
]
|
|
3214
4439
|
}) : void 0;
|
|
3215
4440
|
const isLandscape = resolved.orientation === "landscape";
|
|
4441
|
+
const docSections = [];
|
|
4442
|
+
if (resolved.coverPage && resolved.coverPage.enabled) {
|
|
4443
|
+
const coverElements = await buildDocxCoverPageElements(
|
|
4444
|
+
resolved.coverPage,
|
|
4445
|
+
defaultFont,
|
|
4446
|
+
textHex,
|
|
4447
|
+
primaryHex,
|
|
4448
|
+
primaryDarkHex,
|
|
4449
|
+
textMutedHex,
|
|
4450
|
+
baseDir
|
|
4451
|
+
);
|
|
4452
|
+
docSections.push({
|
|
4453
|
+
properties: {
|
|
4454
|
+
page: {
|
|
4455
|
+
size: {
|
|
4456
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4457
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4458
|
+
orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
4459
|
+
},
|
|
4460
|
+
margin: {
|
|
4461
|
+
top: resolved.margins.topTwip,
|
|
4462
|
+
bottom: resolved.margins.bottomTwip,
|
|
4463
|
+
left: resolved.margins.leftTwip,
|
|
4464
|
+
right: resolved.margins.rightTwip
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
},
|
|
4468
|
+
headers: void 0,
|
|
4469
|
+
footers: void 0,
|
|
4470
|
+
children: coverElements
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
docSections.push({
|
|
4474
|
+
properties: {
|
|
4475
|
+
page: {
|
|
4476
|
+
pageNumbers: {
|
|
4477
|
+
start: 1,
|
|
4478
|
+
formatType: NumberFormat.DECIMAL
|
|
4479
|
+
},
|
|
4480
|
+
size: {
|
|
4481
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4482
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4483
|
+
orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
4484
|
+
},
|
|
4485
|
+
margin: {
|
|
4486
|
+
top: resolved.margins.topTwip,
|
|
4487
|
+
bottom: resolved.margins.bottomTwip,
|
|
4488
|
+
left: resolved.margins.leftTwip,
|
|
4489
|
+
right: resolved.margins.rightTwip,
|
|
4490
|
+
header: 720,
|
|
4491
|
+
footer: 720
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4494
|
+
},
|
|
4495
|
+
headers: docHeader ? { default: docHeader } : void 0,
|
|
4496
|
+
footers: docFooter ? { default: docFooter } : void 0,
|
|
4497
|
+
children: docElements
|
|
4498
|
+
});
|
|
4499
|
+
if (resolved.backCover && resolved.backCover.enabled) {
|
|
4500
|
+
const backElements = await buildDocxBackCoverElements(
|
|
4501
|
+
resolved.backCover,
|
|
4502
|
+
defaultFont,
|
|
4503
|
+
textHex,
|
|
4504
|
+
primaryHex,
|
|
4505
|
+
primaryDarkHex,
|
|
4506
|
+
textMutedHex,
|
|
4507
|
+
baseDir
|
|
4508
|
+
);
|
|
4509
|
+
docSections.push({
|
|
4510
|
+
properties: {
|
|
4511
|
+
page: {
|
|
4512
|
+
size: {
|
|
4513
|
+
width: resolved.paperDimensions.widthTwip,
|
|
4514
|
+
height: resolved.paperDimensions.heightTwip,
|
|
4515
|
+
orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
4516
|
+
},
|
|
4517
|
+
margin: {
|
|
4518
|
+
top: resolved.margins.topTwip,
|
|
4519
|
+
bottom: resolved.margins.bottomTwip,
|
|
4520
|
+
left: resolved.margins.leftTwip,
|
|
4521
|
+
right: resolved.margins.rightTwip
|
|
4522
|
+
}
|
|
4523
|
+
}
|
|
4524
|
+
},
|
|
4525
|
+
headers: void 0,
|
|
4526
|
+
footers: void 0,
|
|
4527
|
+
children: backElements
|
|
4528
|
+
});
|
|
4529
|
+
}
|
|
3216
4530
|
const document = new Document({
|
|
3217
4531
|
styles: {
|
|
3218
4532
|
default: {
|
|
@@ -3233,33 +4547,250 @@ async function buildDocxDocument(doc, config, baseDir = process.cwd()) {
|
|
|
3233
4547
|
}
|
|
3234
4548
|
}
|
|
3235
4549
|
},
|
|
3236
|
-
sections:
|
|
3237
|
-
{
|
|
3238
|
-
properties: {
|
|
3239
|
-
page: {
|
|
3240
|
-
size: {
|
|
3241
|
-
width: resolved.paperDimensions.widthTwip,
|
|
3242
|
-
height: resolved.paperDimensions.heightTwip,
|
|
3243
|
-
orientation: isLandscape ? PageOrientation.LANDSCAPE : PageOrientation.PORTRAIT
|
|
3244
|
-
},
|
|
3245
|
-
margin: {
|
|
3246
|
-
top: resolved.margins.topTwip,
|
|
3247
|
-
bottom: resolved.margins.bottomTwip,
|
|
3248
|
-
left: resolved.margins.leftTwip,
|
|
3249
|
-
right: resolved.margins.rightTwip,
|
|
3250
|
-
header: 720,
|
|
3251
|
-
footer: 720
|
|
3252
|
-
}
|
|
3253
|
-
}
|
|
3254
|
-
},
|
|
3255
|
-
headers: docHeader ? { default: docHeader } : void 0,
|
|
3256
|
-
footers: docFooter ? { default: docFooter } : void 0,
|
|
3257
|
-
children: docElements
|
|
3258
|
-
}
|
|
3259
|
-
]
|
|
4550
|
+
sections: docSections
|
|
3260
4551
|
});
|
|
3261
4552
|
return await Packer.toBuffer(document);
|
|
3262
4553
|
}
|
|
4554
|
+
async function buildDocxBackCoverElements(backCover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4555
|
+
var _a;
|
|
4556
|
+
const elements = [];
|
|
4557
|
+
elements.push(new Paragraph({ spacing: { before: 1800 } }));
|
|
4558
|
+
if (backCover.logo) {
|
|
4559
|
+
const resolvedLogo = await resolveImage(backCover.logo, baseDir);
|
|
4560
|
+
if (resolvedLogo) {
|
|
4561
|
+
const logoW = typeof backCover.logoWidth === "number" ? backCover.logoWidth : 140;
|
|
4562
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4563
|
+
elements.push(
|
|
4564
|
+
new Paragraph({
|
|
4565
|
+
children: [
|
|
4566
|
+
new ImageRun({
|
|
4567
|
+
data: resolvedLogo.buffer,
|
|
4568
|
+
transformation: {
|
|
4569
|
+
width: logoW,
|
|
4570
|
+
height: Math.round(logoW * 0.75)
|
|
4571
|
+
},
|
|
4572
|
+
type: logoType
|
|
4573
|
+
})
|
|
4574
|
+
],
|
|
4575
|
+
spacing: { after: 240 }
|
|
4576
|
+
})
|
|
4577
|
+
);
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
if (backCover.badge) {
|
|
4581
|
+
elements.push(
|
|
4582
|
+
new Paragraph({
|
|
4583
|
+
children: [
|
|
4584
|
+
new TextRun({
|
|
4585
|
+
text: `[ ${backCover.badge.toUpperCase()} ]`,
|
|
4586
|
+
font: defaultFont,
|
|
4587
|
+
size: 20,
|
|
4588
|
+
bold: true,
|
|
4589
|
+
color: primaryDarkHex
|
|
4590
|
+
})
|
|
4591
|
+
],
|
|
4592
|
+
spacing: { after: 240 }
|
|
4593
|
+
})
|
|
4594
|
+
);
|
|
4595
|
+
}
|
|
4596
|
+
elements.push(
|
|
4597
|
+
new Paragraph({
|
|
4598
|
+
children: [
|
|
4599
|
+
new TextRun({
|
|
4600
|
+
text: backCover.title,
|
|
4601
|
+
font: defaultFont,
|
|
4602
|
+
size: 52,
|
|
4603
|
+
// 26pt
|
|
4604
|
+
bold: true,
|
|
4605
|
+
color: textHex
|
|
4606
|
+
})
|
|
4607
|
+
],
|
|
4608
|
+
spacing: { after: 140 }
|
|
4609
|
+
})
|
|
4610
|
+
);
|
|
4611
|
+
if (backCover.subtitle) {
|
|
4612
|
+
elements.push(
|
|
4613
|
+
new Paragraph({
|
|
4614
|
+
children: [
|
|
4615
|
+
new TextRun({
|
|
4616
|
+
text: backCover.subtitle,
|
|
4617
|
+
font: defaultFont,
|
|
4618
|
+
size: 24,
|
|
4619
|
+
// 12pt
|
|
4620
|
+
color: textMutedHex
|
|
4621
|
+
})
|
|
4622
|
+
],
|
|
4623
|
+
spacing: { after: 480 }
|
|
4624
|
+
})
|
|
4625
|
+
);
|
|
4626
|
+
}
|
|
4627
|
+
elements.push(
|
|
4628
|
+
new Paragraph({
|
|
4629
|
+
border: {
|
|
4630
|
+
bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4631
|
+
},
|
|
4632
|
+
spacing: { after: 480 }
|
|
4633
|
+
})
|
|
4634
|
+
);
|
|
4635
|
+
const contactRuns = [];
|
|
4636
|
+
if (backCover.company) contactRuns.push(new TextRun({ text: `Organization: ${backCover.company}
|
|
4637
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4638
|
+
if (backCover.address) contactRuns.push(new TextRun({ text: `Address: ${backCover.address}
|
|
4639
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4640
|
+
if (backCover.email) contactRuns.push(new TextRun({ text: `Email: ${backCover.email}
|
|
4641
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4642
|
+
if (backCover.phone) contactRuns.push(new TextRun({ text: `Phone: ${backCover.phone}
|
|
4643
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4644
|
+
if (backCover.website) contactRuns.push(new TextRun({ text: `Website: ${backCover.website}
|
|
4645
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4646
|
+
if (backCover.social) {
|
|
4647
|
+
for (const [net, url] of Object.entries(backCover.social)) {
|
|
4648
|
+
if (url) {
|
|
4649
|
+
contactRuns.push(new TextRun({ text: `${net.toUpperCase()}: ${url}
|
|
4650
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4651
|
+
}
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
if (contactRuns.length > 0) {
|
|
4655
|
+
elements.push(
|
|
4656
|
+
new Paragraph({
|
|
4657
|
+
children: contactRuns,
|
|
4658
|
+
spacing: { before: 360, after: 360 }
|
|
4659
|
+
})
|
|
4660
|
+
);
|
|
4661
|
+
}
|
|
4662
|
+
if (backCover.copyright) {
|
|
4663
|
+
elements.push(
|
|
4664
|
+
new Paragraph({
|
|
4665
|
+
children: [
|
|
4666
|
+
new TextRun({
|
|
4667
|
+
text: backCover.copyright,
|
|
4668
|
+
font: defaultFont,
|
|
4669
|
+
size: 18,
|
|
4670
|
+
color: "94A3B8"
|
|
4671
|
+
})
|
|
4672
|
+
],
|
|
4673
|
+
spacing: { before: 720 }
|
|
4674
|
+
})
|
|
4675
|
+
);
|
|
4676
|
+
}
|
|
4677
|
+
return elements;
|
|
4678
|
+
}
|
|
4679
|
+
async function buildDocxCoverPageElements(cover, defaultFont, textHex, primaryHex, primaryDarkHex, textMutedHex, baseDir) {
|
|
4680
|
+
var _a;
|
|
4681
|
+
const elements = [];
|
|
4682
|
+
elements.push(new Paragraph({ spacing: { before: 1800 } }));
|
|
4683
|
+
if (cover.logo) {
|
|
4684
|
+
const resolvedLogo = await resolveImage(cover.logo, baseDir);
|
|
4685
|
+
if (resolvedLogo) {
|
|
4686
|
+
const logoW = typeof cover.logoWidth === "number" ? cover.logoWidth : 140;
|
|
4687
|
+
const logoType = ((_a = resolvedLogo.mimeType) == null ? void 0 : _a.includes("png")) ? "png" : "jpg";
|
|
4688
|
+
elements.push(
|
|
4689
|
+
new Paragraph({
|
|
4690
|
+
children: [
|
|
4691
|
+
new ImageRun({
|
|
4692
|
+
data: resolvedLogo.buffer,
|
|
4693
|
+
transformation: {
|
|
4694
|
+
width: logoW,
|
|
4695
|
+
height: Math.round(logoW * 0.75)
|
|
4696
|
+
},
|
|
4697
|
+
type: logoType
|
|
4698
|
+
})
|
|
4699
|
+
],
|
|
4700
|
+
spacing: { after: 240 }
|
|
4701
|
+
})
|
|
4702
|
+
);
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
if (cover.badge) {
|
|
4706
|
+
elements.push(
|
|
4707
|
+
new Paragraph({
|
|
4708
|
+
children: [
|
|
4709
|
+
new TextRun({
|
|
4710
|
+
text: `[ ${cover.badge.toUpperCase()} ]`,
|
|
4711
|
+
font: defaultFont,
|
|
4712
|
+
size: 20,
|
|
4713
|
+
bold: true,
|
|
4714
|
+
color: primaryDarkHex
|
|
4715
|
+
})
|
|
4716
|
+
],
|
|
4717
|
+
spacing: { after: 240 }
|
|
4718
|
+
})
|
|
4719
|
+
);
|
|
4720
|
+
}
|
|
4721
|
+
elements.push(
|
|
4722
|
+
new Paragraph({
|
|
4723
|
+
children: [
|
|
4724
|
+
new TextRun({
|
|
4725
|
+
text: cover.title,
|
|
4726
|
+
font: defaultFont,
|
|
4727
|
+
size: 56,
|
|
4728
|
+
// 28pt
|
|
4729
|
+
bold: true,
|
|
4730
|
+
color: textHex
|
|
4731
|
+
})
|
|
4732
|
+
],
|
|
4733
|
+
spacing: { after: 140 }
|
|
4734
|
+
})
|
|
4735
|
+
);
|
|
4736
|
+
if (cover.subtitle) {
|
|
4737
|
+
elements.push(
|
|
4738
|
+
new Paragraph({
|
|
4739
|
+
children: [
|
|
4740
|
+
new TextRun({
|
|
4741
|
+
text: cover.subtitle,
|
|
4742
|
+
font: defaultFont,
|
|
4743
|
+
size: 26,
|
|
4744
|
+
// 13pt
|
|
4745
|
+
color: textMutedHex
|
|
4746
|
+
})
|
|
4747
|
+
],
|
|
4748
|
+
spacing: { after: 480 }
|
|
4749
|
+
})
|
|
4750
|
+
);
|
|
4751
|
+
}
|
|
4752
|
+
elements.push(
|
|
4753
|
+
new Paragraph({
|
|
4754
|
+
border: {
|
|
4755
|
+
bottom: { style: BorderStyle.SINGLE, size: 16, color: primaryHex, space: 8 }
|
|
4756
|
+
},
|
|
4757
|
+
spacing: { after: 480 }
|
|
4758
|
+
})
|
|
4759
|
+
);
|
|
4760
|
+
if (cover.company || cover.author || cover.version || cover.date) {
|
|
4761
|
+
const metaRuns = [];
|
|
4762
|
+
if (cover.company) metaRuns.push(new TextRun({ text: `Organization: ${cover.company}
|
|
4763
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4764
|
+
if (cover.author) metaRuns.push(new TextRun({ text: `Author: ${cover.author}
|
|
4765
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4766
|
+
if (cover.version) metaRuns.push(new TextRun({ text: `Version: ${cover.version}
|
|
4767
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4768
|
+
if (cover.date) metaRuns.push(new TextRun({ text: `Date: ${cover.date}
|
|
4769
|
+
`, font: defaultFont, size: 21, color: textHex }));
|
|
4770
|
+
elements.push(
|
|
4771
|
+
new Paragraph({
|
|
4772
|
+
children: metaRuns,
|
|
4773
|
+
spacing: { before: 360, after: 360 }
|
|
4774
|
+
})
|
|
4775
|
+
);
|
|
4776
|
+
}
|
|
4777
|
+
if (cover.footerText) {
|
|
4778
|
+
elements.push(
|
|
4779
|
+
new Paragraph({
|
|
4780
|
+
children: [
|
|
4781
|
+
new TextRun({
|
|
4782
|
+
text: cover.footerText,
|
|
4783
|
+
font: defaultFont,
|
|
4784
|
+
size: 18,
|
|
4785
|
+
color: "94A3B8"
|
|
4786
|
+
})
|
|
4787
|
+
],
|
|
4788
|
+
spacing: { before: 720 }
|
|
4789
|
+
})
|
|
4790
|
+
);
|
|
4791
|
+
}
|
|
4792
|
+
return elements;
|
|
4793
|
+
}
|
|
3263
4794
|
async function buildDocxSignatureCell(item, sig, widthDxa, defaultFont, baseDir) {
|
|
3264
4795
|
const cellParagraphs = [];
|
|
3265
4796
|
if (item.title) {
|
|
@@ -3487,14 +5018,885 @@ async function compileMarkdown(inputFilePathOrContent, userConfig = {}, onProgre
|
|
|
3487
5018
|
};
|
|
3488
5019
|
}
|
|
3489
5020
|
|
|
5021
|
+
// src/server/previewServer.ts
|
|
5022
|
+
import * as http from "http";
|
|
5023
|
+
import * as fs7 from "fs";
|
|
5024
|
+
import * as path7 from "path";
|
|
5025
|
+
async function startPreviewServer(options) {
|
|
5026
|
+
const absoluteFilePath = path7.resolve(process.cwd(), options.filePath);
|
|
5027
|
+
if (!fs7.existsSync(absoluteFilePath)) {
|
|
5028
|
+
throw new Error(`MarkForge preview error: File not found at "${absoluteFilePath}"`);
|
|
5029
|
+
}
|
|
5030
|
+
const baseDir = path7.dirname(absoluteFilePath);
|
|
5031
|
+
const { config: fileConfig } = await loadConfig(void 0, baseDir);
|
|
5032
|
+
const baseConfig = options.config || fileConfig;
|
|
5033
|
+
const port = options.port || 3e3;
|
|
5034
|
+
const sseClients = /* @__PURE__ */ new Set();
|
|
5035
|
+
const broadcastReload = () => {
|
|
5036
|
+
sseClients.forEach((client) => {
|
|
5037
|
+
try {
|
|
5038
|
+
client.write(`event: reload
|
|
5039
|
+
data: ${Date.now()}
|
|
5040
|
+
|
|
5041
|
+
`);
|
|
5042
|
+
} catch {
|
|
5043
|
+
sseClients.delete(client);
|
|
5044
|
+
}
|
|
5045
|
+
});
|
|
5046
|
+
};
|
|
5047
|
+
let debounceTimer = null;
|
|
5048
|
+
const watcher = fs7.watch(baseDir, { recursive: false }, (_event, filename) => {
|
|
5049
|
+
if (!filename) return;
|
|
5050
|
+
const changedPath = path7.resolve(baseDir, filename);
|
|
5051
|
+
if (changedPath === absoluteFilePath || filename.includes("markforge") || filename.endsWith(".css")) {
|
|
5052
|
+
if (debounceTimer) clearTimeout(debounceTimer);
|
|
5053
|
+
debounceTimer = setTimeout(() => {
|
|
5054
|
+
broadcastReload();
|
|
5055
|
+
}, 150);
|
|
5056
|
+
}
|
|
5057
|
+
});
|
|
5058
|
+
const server = http.createServer(async (req, res) => {
|
|
5059
|
+
const url = new URL(req.url || "/", `http://localhost:${port}`);
|
|
5060
|
+
if (url.pathname === "/events") {
|
|
5061
|
+
res.writeHead(200, {
|
|
5062
|
+
"Content-Type": "text/event-stream",
|
|
5063
|
+
"Cache-Control": "no-cache, no-transform",
|
|
5064
|
+
Connection: "keep-alive"
|
|
5065
|
+
});
|
|
5066
|
+
res.write(`data: connected
|
|
5067
|
+
|
|
5068
|
+
`);
|
|
5069
|
+
sseClients.add(res);
|
|
5070
|
+
req.on("close", () => {
|
|
5071
|
+
sseClients.delete(res);
|
|
5072
|
+
});
|
|
5073
|
+
return;
|
|
5074
|
+
}
|
|
5075
|
+
if (url.pathname === "/api/file-content" && req.method === "GET") {
|
|
5076
|
+
try {
|
|
5077
|
+
const content = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5078
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5079
|
+
res.end(
|
|
5080
|
+
JSON.stringify({
|
|
5081
|
+
content,
|
|
5082
|
+
fileName: path7.basename(absoluteFilePath),
|
|
5083
|
+
filePath: absoluteFilePath
|
|
5084
|
+
})
|
|
5085
|
+
);
|
|
5086
|
+
} catch (err) {
|
|
5087
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5088
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
5089
|
+
res.end(JSON.stringify({ error: msg }));
|
|
5090
|
+
}
|
|
5091
|
+
return;
|
|
5092
|
+
}
|
|
5093
|
+
if (url.pathname === "/api/save-content" && req.method === "POST") {
|
|
5094
|
+
let body = "";
|
|
5095
|
+
req.on("data", (chunk) => {
|
|
5096
|
+
body += chunk;
|
|
5097
|
+
});
|
|
5098
|
+
req.on("end", () => {
|
|
5099
|
+
try {
|
|
5100
|
+
const parsed = JSON.parse(body);
|
|
5101
|
+
if (typeof parsed.content === "string") {
|
|
5102
|
+
fs7.writeFileSync(absoluteFilePath, parsed.content, "utf-8");
|
|
5103
|
+
broadcastReload();
|
|
5104
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
5105
|
+
res.end(JSON.stringify({ success: true, savedAt: Date.now() }));
|
|
5106
|
+
} else {
|
|
5107
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
5108
|
+
res.end(JSON.stringify({ error: "Missing content field in request body" }));
|
|
5109
|
+
}
|
|
5110
|
+
} catch (err) {
|
|
5111
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5112
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
5113
|
+
res.end(JSON.stringify({ error: msg }));
|
|
5114
|
+
}
|
|
5115
|
+
});
|
|
5116
|
+
return;
|
|
5117
|
+
}
|
|
5118
|
+
if (url.pathname === "/api/export" && (req.method === "GET" || req.method === "POST")) {
|
|
5119
|
+
const format = url.searchParams.get("format") || "docx";
|
|
5120
|
+
try {
|
|
5121
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5122
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
5123
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
5124
|
+
const mergedConfig = { ...baseConfig, ...resolvedConfig };
|
|
5125
|
+
const fileBase = path7.basename(absoluteFilePath, path7.extname(absoluteFilePath));
|
|
5126
|
+
if (format === "docx") {
|
|
5127
|
+
const buffer = await buildDocxDocument(doc, mergedConfig, baseDir);
|
|
5128
|
+
res.writeHead(200, {
|
|
5129
|
+
"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
5130
|
+
"Content-Disposition": `attachment; filename="${fileBase}.docx"`
|
|
5131
|
+
});
|
|
5132
|
+
res.end(buffer);
|
|
5133
|
+
return;
|
|
5134
|
+
} else if (format === "pdf") {
|
|
5135
|
+
const buffer = await buildPdfDocument(doc, mergedConfig, baseDir);
|
|
5136
|
+
res.writeHead(200, {
|
|
5137
|
+
"Content-Type": "application/pdf",
|
|
5138
|
+
"Content-Disposition": `attachment; filename="${fileBase}.pdf"`
|
|
5139
|
+
});
|
|
5140
|
+
res.end(buffer);
|
|
5141
|
+
return;
|
|
5142
|
+
} else {
|
|
5143
|
+
const html = await buildHtmlDocument(doc, mergedConfig, baseDir);
|
|
5144
|
+
res.writeHead(200, {
|
|
5145
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
5146
|
+
"Content-Disposition": `attachment; filename="${fileBase}.html"`
|
|
5147
|
+
});
|
|
5148
|
+
res.end(html);
|
|
5149
|
+
return;
|
|
5150
|
+
}
|
|
5151
|
+
} catch (err) {
|
|
5152
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5153
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
5154
|
+
res.end(`Export failed: ${msg}`);
|
|
5155
|
+
return;
|
|
5156
|
+
}
|
|
5157
|
+
}
|
|
5158
|
+
if (url.pathname === "/document-content") {
|
|
5159
|
+
try {
|
|
5160
|
+
const mdContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5161
|
+
const doc = parseMarkdownDocument(mdContent);
|
|
5162
|
+
const { config: resolvedConfig } = await loadConfig(void 0, baseDir);
|
|
5163
|
+
const html = await buildHtmlDocument(doc, { ...baseConfig, ...resolvedConfig }, baseDir);
|
|
5164
|
+
const injectedScript = `
|
|
5165
|
+
<script>
|
|
5166
|
+
(function() {
|
|
5167
|
+
var evtSource = new EventSource('/events');
|
|
5168
|
+
evtSource.addEventListener('reload', function() {
|
|
5169
|
+
var scrollPos = window.scrollY;
|
|
5170
|
+
sessionStorage.setItem('markforge_scroll', scrollPos);
|
|
5171
|
+
window.location.reload();
|
|
5172
|
+
});
|
|
5173
|
+
window.addEventListener('load', function() {
|
|
5174
|
+
var saved = sessionStorage.getItem('markforge_scroll');
|
|
5175
|
+
if (saved) {
|
|
5176
|
+
window.scrollTo(0, parseInt(saved, 10));
|
|
5177
|
+
}
|
|
5178
|
+
});
|
|
5179
|
+
})();
|
|
5180
|
+
</script>
|
|
5181
|
+
`;
|
|
5182
|
+
const finalHtml = html.replace("</body>", `${injectedScript}</body>`);
|
|
5183
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5184
|
+
res.end(finalHtml);
|
|
5185
|
+
} catch (err) {
|
|
5186
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5187
|
+
res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
|
|
5188
|
+
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>`);
|
|
5189
|
+
}
|
|
5190
|
+
return;
|
|
5191
|
+
}
|
|
5192
|
+
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
5193
|
+
const fileName = path7.basename(absoluteFilePath);
|
|
5194
|
+
const initialContent = fs7.readFileSync(absoluteFilePath, "utf-8");
|
|
5195
|
+
const appHtml = `<!DOCTYPE html>
|
|
5196
|
+
<html lang="en">
|
|
5197
|
+
<head>
|
|
5198
|
+
<meta charset="UTF-8">
|
|
5199
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
5200
|
+
<title>MarkForge Live Studio - ${escapeHtml2(fileName)}</title>
|
|
5201
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
5202
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
5203
|
+
<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">
|
|
5204
|
+
<style>
|
|
5205
|
+
:root {
|
|
5206
|
+
--mf-primary: #0D998D;
|
|
5207
|
+
--mf-primary-dark: #008277;
|
|
5208
|
+
--mf-primary-light: #ECFDFD;
|
|
5209
|
+
--mf-primary-border: #33CDCF;
|
|
5210
|
+
--mf-dark: #0F172A;
|
|
5211
|
+
--mf-slate: #1E293B;
|
|
5212
|
+
--mf-editor-bg: #0F172A;
|
|
5213
|
+
--mf-editor-gutter: #1E293B;
|
|
5214
|
+
--mf-editor-text: #F8FAFC;
|
|
5215
|
+
--mf-muted: #64748B;
|
|
5216
|
+
--mf-light-border: #E2E8F0;
|
|
5217
|
+
--mf-bg: #F1F5F9;
|
|
5218
|
+
}
|
|
5219
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5220
|
+
body {
|
|
5221
|
+
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
5222
|
+
background: var(--mf-bg);
|
|
5223
|
+
color: var(--mf-dark);
|
|
5224
|
+
display: flex;
|
|
5225
|
+
flex-direction: column;
|
|
5226
|
+
height: 100vh;
|
|
5227
|
+
overflow: hidden;
|
|
5228
|
+
}
|
|
5229
|
+
header {
|
|
5230
|
+
background: #FFFFFF;
|
|
5231
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5232
|
+
min-height: 56px;
|
|
5233
|
+
display: flex;
|
|
5234
|
+
flex-wrap: wrap;
|
|
5235
|
+
align-items: center;
|
|
5236
|
+
justify-content: space-between;
|
|
5237
|
+
padding: 0.4rem 1.2rem;
|
|
5238
|
+
z-index: 10;
|
|
5239
|
+
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04);
|
|
5240
|
+
gap: 0.75rem;
|
|
5241
|
+
}
|
|
5242
|
+
.brand-section {
|
|
5243
|
+
display: flex;
|
|
5244
|
+
align-items: center;
|
|
5245
|
+
gap: 0.75rem;
|
|
5246
|
+
}
|
|
5247
|
+
.brand-badge {
|
|
5248
|
+
font-size: 0.72rem;
|
|
5249
|
+
font-weight: 800;
|
|
5250
|
+
letter-spacing: 0.08em;
|
|
5251
|
+
background: var(--mf-dark);
|
|
5252
|
+
color: #FFFFFF;
|
|
5253
|
+
padding: 0.25rem 0.55rem;
|
|
5254
|
+
border-radius: 4px;
|
|
5255
|
+
text-transform: uppercase;
|
|
5256
|
+
}
|
|
5257
|
+
.file-name {
|
|
5258
|
+
font-size: 0.9rem;
|
|
5259
|
+
font-weight: 700;
|
|
5260
|
+
color: var(--mf-dark);
|
|
5261
|
+
}
|
|
5262
|
+
.sync-status {
|
|
5263
|
+
display: flex;
|
|
5264
|
+
align-items: center;
|
|
5265
|
+
gap: 0.35rem;
|
|
5266
|
+
font-size: 0.75rem;
|
|
5267
|
+
font-weight: 600;
|
|
5268
|
+
color: var(--mf-primary-dark);
|
|
5269
|
+
background: var(--mf-primary-light);
|
|
5270
|
+
padding: 0.2rem 0.55rem;
|
|
5271
|
+
border-radius: 9999px;
|
|
5272
|
+
border: 1px solid var(--mf-primary-border);
|
|
5273
|
+
}
|
|
5274
|
+
.sync-dot {
|
|
5275
|
+
width: 7px;
|
|
5276
|
+
height: 7px;
|
|
5277
|
+
background-color: var(--mf-primary);
|
|
5278
|
+
border-radius: 50%;
|
|
5279
|
+
box-shadow: 0 0 0 2px rgba(13, 153, 141, 0.2);
|
|
5280
|
+
}
|
|
5281
|
+
.toolbar-section {
|
|
5282
|
+
display: flex;
|
|
5283
|
+
align-items: center;
|
|
5284
|
+
gap: 0.3rem;
|
|
5285
|
+
background: #F8FAFC;
|
|
5286
|
+
padding: 0.25rem 0.4rem;
|
|
5287
|
+
border-radius: 6px;
|
|
5288
|
+
border: 1px solid var(--mf-light-border);
|
|
5289
|
+
}
|
|
5290
|
+
.tool-btn {
|
|
5291
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5292
|
+
font-size: 0.75rem;
|
|
5293
|
+
font-weight: 600;
|
|
5294
|
+
padding: 0.25rem 0.45rem;
|
|
5295
|
+
background: transparent;
|
|
5296
|
+
border: 1px solid transparent;
|
|
5297
|
+
border-radius: 4px;
|
|
5298
|
+
cursor: pointer;
|
|
5299
|
+
color: var(--mf-slate);
|
|
5300
|
+
transition: all 0.1s ease;
|
|
5301
|
+
}
|
|
5302
|
+
.tool-btn:hover {
|
|
5303
|
+
background: #FFFFFF;
|
|
5304
|
+
border-color: var(--mf-light-border);
|
|
5305
|
+
color: var(--mf-primary-dark);
|
|
5306
|
+
}
|
|
5307
|
+
.tool-divider {
|
|
5308
|
+
width: 1px;
|
|
5309
|
+
height: 16px;
|
|
5310
|
+
background: var(--mf-light-border);
|
|
5311
|
+
margin: 0 0.15rem;
|
|
5312
|
+
}
|
|
5313
|
+
.controls {
|
|
5314
|
+
display: flex;
|
|
5315
|
+
align-items: center;
|
|
5316
|
+
gap: 0.5rem;
|
|
5317
|
+
}
|
|
5318
|
+
.view-toggles {
|
|
5319
|
+
display: flex;
|
|
5320
|
+
background: #F1F5F9;
|
|
5321
|
+
padding: 2px;
|
|
5322
|
+
border-radius: 6px;
|
|
5323
|
+
border: 1px solid var(--mf-light-border);
|
|
5324
|
+
}
|
|
5325
|
+
.toggle-btn {
|
|
5326
|
+
font-family: inherit;
|
|
5327
|
+
font-size: 0.74rem;
|
|
5328
|
+
font-weight: 600;
|
|
5329
|
+
padding: 0.25rem 0.55rem;
|
|
5330
|
+
border: none;
|
|
5331
|
+
background: transparent;
|
|
5332
|
+
border-radius: 4px;
|
|
5333
|
+
cursor: pointer;
|
|
5334
|
+
color: var(--mf-muted);
|
|
5335
|
+
transition: all 0.15s ease;
|
|
5336
|
+
}
|
|
5337
|
+
.toggle-btn.active {
|
|
5338
|
+
background: #FFFFFF;
|
|
5339
|
+
color: var(--mf-dark);
|
|
5340
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.08);
|
|
5341
|
+
}
|
|
5342
|
+
.btn {
|
|
5343
|
+
font-family: inherit;
|
|
5344
|
+
font-size: 0.78rem;
|
|
5345
|
+
font-weight: 600;
|
|
5346
|
+
padding: 0.35rem 0.75rem;
|
|
5347
|
+
border-radius: 6px;
|
|
5348
|
+
cursor: pointer;
|
|
5349
|
+
text-decoration: none;
|
|
5350
|
+
transition: all 0.15s ease;
|
|
5351
|
+
display: inline-flex;
|
|
5352
|
+
align-items: center;
|
|
5353
|
+
gap: 0.3rem;
|
|
5354
|
+
border: 1px solid var(--mf-light-border);
|
|
5355
|
+
background: #FFFFFF;
|
|
5356
|
+
color: var(--mf-dark);
|
|
5357
|
+
}
|
|
5358
|
+
.btn:hover {
|
|
5359
|
+
background: #F8FAFC;
|
|
5360
|
+
border-color: #CBD5E1;
|
|
5361
|
+
}
|
|
5362
|
+
.btn-primary {
|
|
5363
|
+
background: var(--mf-primary);
|
|
5364
|
+
color: #FFFFFF;
|
|
5365
|
+
border-color: var(--mf-primary);
|
|
5366
|
+
}
|
|
5367
|
+
.btn-primary:hover {
|
|
5368
|
+
background: var(--mf-primary-dark);
|
|
5369
|
+
border-color: var(--mf-primary-dark);
|
|
5370
|
+
}
|
|
5371
|
+
.save-indicator {
|
|
5372
|
+
font-size: 0.75rem;
|
|
5373
|
+
font-weight: 600;
|
|
5374
|
+
color: var(--mf-muted);
|
|
5375
|
+
min-width: 65px;
|
|
5376
|
+
text-align: right;
|
|
5377
|
+
}
|
|
5378
|
+
.save-indicator.saved {
|
|
5379
|
+
color: var(--mf-primary-dark);
|
|
5380
|
+
}
|
|
5381
|
+
.save-indicator.saving {
|
|
5382
|
+
color: #D97706;
|
|
5383
|
+
}
|
|
5384
|
+
.save-indicator.unsaved {
|
|
5385
|
+
color: #E11D48;
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5388
|
+
/* Main Workspace Splitter Layout */
|
|
5389
|
+
main.workspace {
|
|
5390
|
+
flex: 1;
|
|
5391
|
+
display: flex;
|
|
5392
|
+
height: calc(100vh - 56px);
|
|
5393
|
+
overflow: hidden;
|
|
5394
|
+
background: var(--mf-bg);
|
|
5395
|
+
position: relative;
|
|
5396
|
+
}
|
|
5397
|
+
.editor-pane {
|
|
5398
|
+
width: 50%;
|
|
5399
|
+
height: 100%;
|
|
5400
|
+
display: flex;
|
|
5401
|
+
flex-direction: column;
|
|
5402
|
+
background: var(--mf-editor-bg);
|
|
5403
|
+
border-right: 1px solid #334155;
|
|
5404
|
+
overflow: hidden;
|
|
5405
|
+
}
|
|
5406
|
+
.editor-header {
|
|
5407
|
+
background: #090D16;
|
|
5408
|
+
border-bottom: 1px solid #1E293B;
|
|
5409
|
+
padding: 0.4rem 0.8rem;
|
|
5410
|
+
display: flex;
|
|
5411
|
+
align-items: center;
|
|
5412
|
+
justify-content: space-between;
|
|
5413
|
+
color: #94A3B8;
|
|
5414
|
+
font-size: 0.74rem;
|
|
5415
|
+
font-weight: 500;
|
|
5416
|
+
}
|
|
5417
|
+
.editor-container {
|
|
5418
|
+
flex: 1;
|
|
5419
|
+
display: flex;
|
|
5420
|
+
position: relative;
|
|
5421
|
+
overflow: hidden;
|
|
5422
|
+
background: var(--mf-editor-bg);
|
|
5423
|
+
}
|
|
5424
|
+
.line-numbers {
|
|
5425
|
+
width: 44px;
|
|
5426
|
+
padding: 0.8rem 0.4rem;
|
|
5427
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5428
|
+
font-size: 13px;
|
|
5429
|
+
line-height: 1.55;
|
|
5430
|
+
color: #475569;
|
|
5431
|
+
text-align: right;
|
|
5432
|
+
user-select: none;
|
|
5433
|
+
background: var(--mf-editor-gutter);
|
|
5434
|
+
overflow: hidden;
|
|
5435
|
+
border-right: 1px solid #1E293B;
|
|
5436
|
+
}
|
|
5437
|
+
.code-editor {
|
|
5438
|
+
flex: 1;
|
|
5439
|
+
padding: 0.8rem 1rem;
|
|
5440
|
+
font-family: 'JetBrains Mono', monospace;
|
|
5441
|
+
font-size: 13px;
|
|
5442
|
+
line-height: 1.55;
|
|
5443
|
+
color: var(--mf-editor-text);
|
|
5444
|
+
background: transparent;
|
|
5445
|
+
border: none;
|
|
5446
|
+
outline: none;
|
|
5447
|
+
resize: none;
|
|
5448
|
+
white-space: pre;
|
|
5449
|
+
overflow-wrap: normal;
|
|
5450
|
+
overflow: auto;
|
|
5451
|
+
tab-size: 2;
|
|
5452
|
+
}
|
|
5453
|
+
|
|
5454
|
+
/* Draggable Splitter Handle */
|
|
5455
|
+
.splitter {
|
|
5456
|
+
width: 8px;
|
|
5457
|
+
cursor: col-resize;
|
|
5458
|
+
background: #E2E8F0;
|
|
5459
|
+
transition: background 0.15s ease;
|
|
5460
|
+
position: relative;
|
|
5461
|
+
z-index: 5;
|
|
5462
|
+
}
|
|
5463
|
+
.splitter:hover, .splitter.active {
|
|
5464
|
+
background: var(--mf-primary);
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5467
|
+
/* Right Preview Pane */
|
|
5468
|
+
.preview-pane {
|
|
5469
|
+
width: 50%;
|
|
5470
|
+
height: 100%;
|
|
5471
|
+
display: flex;
|
|
5472
|
+
flex-direction: column;
|
|
5473
|
+
background: #FFFFFF;
|
|
5474
|
+
overflow: hidden;
|
|
5475
|
+
}
|
|
5476
|
+
.preview-header {
|
|
5477
|
+
background: #FFFFFF;
|
|
5478
|
+
border-bottom: 1px solid var(--mf-light-border);
|
|
5479
|
+
padding: 0.35rem 0.8rem;
|
|
5480
|
+
display: flex;
|
|
5481
|
+
align-items: center;
|
|
5482
|
+
justify-content: space-between;
|
|
5483
|
+
color: var(--mf-muted);
|
|
5484
|
+
font-size: 0.74rem;
|
|
5485
|
+
font-weight: 600;
|
|
5486
|
+
}
|
|
5487
|
+
.viewport-selector {
|
|
5488
|
+
display: flex;
|
|
5489
|
+
gap: 0.25rem;
|
|
5490
|
+
}
|
|
5491
|
+
.vp-btn {
|
|
5492
|
+
font-size: 0.72rem;
|
|
5493
|
+
padding: 0.15rem 0.4rem;
|
|
5494
|
+
border: 1px solid var(--mf-light-border);
|
|
5495
|
+
background: #F8FAFC;
|
|
5496
|
+
border-radius: 4px;
|
|
5497
|
+
cursor: pointer;
|
|
5498
|
+
color: var(--mf-muted);
|
|
5499
|
+
}
|
|
5500
|
+
.vp-btn.active {
|
|
5501
|
+
background: var(--mf-primary-light);
|
|
5502
|
+
color: var(--mf-primary-dark);
|
|
5503
|
+
border-color: var(--mf-primary-border);
|
|
5504
|
+
}
|
|
5505
|
+
.preview-wrapper {
|
|
5506
|
+
flex: 1;
|
|
5507
|
+
display: flex;
|
|
5508
|
+
justify-content: center;
|
|
5509
|
+
align-items: stretch;
|
|
5510
|
+
background: #F1F5F9;
|
|
5511
|
+
overflow: hidden;
|
|
5512
|
+
}
|
|
5513
|
+
iframe {
|
|
5514
|
+
width: 100%;
|
|
5515
|
+
height: 100%;
|
|
5516
|
+
border: none;
|
|
5517
|
+
background: #FFFFFF;
|
|
5518
|
+
transition: max-width 0.2s ease;
|
|
5519
|
+
}
|
|
5520
|
+
.author-footer {
|
|
5521
|
+
font-size: 0.72rem;
|
|
5522
|
+
color: var(--mf-muted);
|
|
5523
|
+
padding-right: 0.5rem;
|
|
5524
|
+
}
|
|
5525
|
+
.author-footer a {
|
|
5526
|
+
color: var(--mf-primary-dark);
|
|
5527
|
+
text-decoration: none;
|
|
5528
|
+
font-weight: 600;
|
|
5529
|
+
}
|
|
5530
|
+
</style>
|
|
5531
|
+
</head>
|
|
5532
|
+
<body>
|
|
5533
|
+
<header>
|
|
5534
|
+
<div class="brand-section">
|
|
5535
|
+
<span class="brand-badge">MARKFORGE STUDIO</span>
|
|
5536
|
+
<span class="file-name" title="${escapeHtml2(absoluteFilePath)}">${escapeHtml2(fileName)}</span>
|
|
5537
|
+
<div class="sync-status">
|
|
5538
|
+
<div class="sync-dot"></div>
|
|
5539
|
+
<span>Live Sync Active</span>
|
|
5540
|
+
</div>
|
|
5541
|
+
</div>
|
|
5542
|
+
|
|
5543
|
+
<!-- Quick Formatting Toolbar -->
|
|
5544
|
+
<div class="toolbar-section">
|
|
5545
|
+
<button class="tool-btn" onclick="insertFormat('h1')" title="Heading 1">H1</button>
|
|
5546
|
+
<button class="tool-btn" onclick="insertFormat('h2')" title="Heading 2">H2</button>
|
|
5547
|
+
<button class="tool-btn" onclick="insertFormat('h3')" title="Heading 3">H3</button>
|
|
5548
|
+
<div class="tool-divider"></div>
|
|
5549
|
+
<button class="tool-btn" onclick="insertFormat('bold')" title="Bold">B</button>
|
|
5550
|
+
<button class="tool-btn" onclick="insertFormat('italic')" title="Italic">I</button>
|
|
5551
|
+
<button class="tool-btn" onclick="insertFormat('code')" title="Inline Code"><></button>
|
|
5552
|
+
<button class="tool-btn" onclick="insertFormat('quote')" title="Blockquote">></button>
|
|
5553
|
+
<div class="tool-divider"></div>
|
|
5554
|
+
<button class="tool-btn" onclick="insertFormat('table')" title="GFM Table">Table</button>
|
|
5555
|
+
<button class="tool-btn" onclick="insertFormat('list')" title="List">List</button>
|
|
5556
|
+
<button class="tool-btn" onclick="insertFormat('task')" title="Task Checklist">Task</button>
|
|
5557
|
+
<div class="tool-divider"></div>
|
|
5558
|
+
<button class="tool-btn" onclick="insertFormat('callout')" title="Callout Box">Callout</button>
|
|
5559
|
+
<button class="tool-btn" onclick="insertFormat('math')" title="LaTeX Math">Math</button>
|
|
5560
|
+
<button class="tool-btn" onclick="insertFormat('columns')" title="Multi-Columns">Columns</button>
|
|
5561
|
+
<button class="tool-btn" onclick="insertFormat('footnote')" title="Footnote">Footnote</button>
|
|
5562
|
+
<button class="tool-btn" onclick="insertFormat('mermaid')" title="Mermaid Diagram">Mermaid</button>
|
|
5563
|
+
</div>
|
|
5564
|
+
|
|
5565
|
+
<!-- Controls & View Mode -->
|
|
5566
|
+
<div class="controls">
|
|
5567
|
+
<div class="view-toggles">
|
|
5568
|
+
<button class="toggle-btn active" id="btn-split" onclick="setViewMode('split')">Split</button>
|
|
5569
|
+
<button class="toggle-btn" id="btn-edit" onclick="setViewMode('edit')">Editor</button>
|
|
5570
|
+
<button class="toggle-btn" id="btn-prev" onclick="setViewMode('prev')">Preview</button>
|
|
5571
|
+
</div>
|
|
5572
|
+
<span class="save-indicator saved" id="save-status">Saved</span>
|
|
5573
|
+
<button class="btn btn-primary" onclick="saveContentManual()" title="Save (Ctrl+S)">Save</button>
|
|
5574
|
+
<button class="btn" onclick="exportDoc('docx')" title="Download Word Document">DOCX</button>
|
|
5575
|
+
<button class="btn" onclick="exportDoc('pdf')" title="Download PDF Document">PDF</button>
|
|
5576
|
+
<button class="btn" onclick="printDoc()" title="Print / PDF dialog">Print</button>
|
|
5577
|
+
</div>
|
|
5578
|
+
</header>
|
|
5579
|
+
|
|
5580
|
+
<main class="workspace" id="workspace">
|
|
5581
|
+
<!-- Left: Code Editor Pane -->
|
|
5582
|
+
<div class="editor-pane" id="editor-pane">
|
|
5583
|
+
<div class="editor-header">
|
|
5584
|
+
<span>MARKDOWN SOURCE</span>
|
|
5585
|
+
<span id="editor-stats">Lines: 1 | Words: 0 | UTF-8</span>
|
|
5586
|
+
</div>
|
|
5587
|
+
<div class="editor-container">
|
|
5588
|
+
<div class="line-numbers" id="line-numbers">1</div>
|
|
5589
|
+
<textarea class="code-editor" id="code-editor" spellcheck="false" placeholder="Write markdown here...">${escapeHtml2(initialContent)}</textarea>
|
|
5590
|
+
</div>
|
|
5591
|
+
</div>
|
|
5592
|
+
|
|
5593
|
+
<!-- Middle: Draggable Splitter Handle -->
|
|
5594
|
+
<div class="splitter" id="splitter"></div>
|
|
5595
|
+
|
|
5596
|
+
<!-- Right: Rendered Preview Pane -->
|
|
5597
|
+
<div class="preview-pane" id="preview-pane">
|
|
5598
|
+
<div class="preview-header">
|
|
5599
|
+
<span>RENDERED PREVIEW</span>
|
|
5600
|
+
<div class="viewport-selector">
|
|
5601
|
+
<button class="vp-btn active" onclick="setViewport('100%')" id="vp-full">100% Full</button>
|
|
5602
|
+
<button class="vp-btn" onclick="setViewport('820px')" id="vp-a4">A4 (820px)</button>
|
|
5603
|
+
<button class="vp-btn" onclick="setViewport('440px')" id="vp-mob">Mobile</button>
|
|
5604
|
+
</div>
|
|
5605
|
+
<span class="author-footer">Created by <a href="https://github.com/masumrpg" target="_blank">Ma'sum (@masumrpg)</a></span>
|
|
5606
|
+
</div>
|
|
5607
|
+
<div class="preview-wrapper">
|
|
5608
|
+
<iframe id="preview-frame" src="/document-content"></iframe>
|
|
5609
|
+
</div>
|
|
5610
|
+
</div>
|
|
5611
|
+
</main>
|
|
5612
|
+
|
|
5613
|
+
<script>
|
|
5614
|
+
var editor = document.getElementById('code-editor');
|
|
5615
|
+
var lineNumbers = document.getElementById('line-numbers');
|
|
5616
|
+
var stats = document.getElementById('editor-stats');
|
|
5617
|
+
var saveStatus = document.getElementById('save-status');
|
|
5618
|
+
var previewFrame = document.getElementById('preview-frame');
|
|
5619
|
+
var editorPane = document.getElementById('editor-pane');
|
|
5620
|
+
var previewPane = document.getElementById('preview-pane');
|
|
5621
|
+
var splitter = document.getElementById('splitter');
|
|
5622
|
+
var isDirty = false;
|
|
5623
|
+
var autoSaveTimeout = null;
|
|
5624
|
+
|
|
5625
|
+
// Update Line Numbers & Stats
|
|
5626
|
+
function updateStatsAndLines() {
|
|
5627
|
+
var lines = editor.value.split('\\n');
|
|
5628
|
+
var lineCount = lines.length;
|
|
5629
|
+
var numHtml = '';
|
|
5630
|
+
for (var i = 1; i <= lineCount; i++) {
|
|
5631
|
+
numHtml += i + '<br>';
|
|
5632
|
+
}
|
|
5633
|
+
lineNumbers.innerHTML = numHtml;
|
|
5634
|
+
|
|
5635
|
+
var words = editor.value.trim().length > 0 ? editor.value.trim().split(/\\s+/).length : 0;
|
|
5636
|
+
var chars = editor.value.length;
|
|
5637
|
+
stats.textContent = 'Lines: ' + lineCount + ' | Words: ' + words + ' | Chars: ' + chars + ' | UTF-8';
|
|
5638
|
+
}
|
|
5639
|
+
|
|
5640
|
+
// Synchronize vertical scroll between Line Numbers and Textarea
|
|
5641
|
+
editor.addEventListener('scroll', function() {
|
|
5642
|
+
lineNumbers.scrollTop = editor.scrollTop;
|
|
5643
|
+
});
|
|
5644
|
+
|
|
5645
|
+
// Handle Input & Debounced Auto-Save
|
|
5646
|
+
editor.addEventListener('input', function() {
|
|
5647
|
+
updateStatsAndLines();
|
|
5648
|
+
setSaveState('unsaved');
|
|
5649
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5650
|
+
autoSaveTimeout = setTimeout(function() {
|
|
5651
|
+
saveContent();
|
|
5652
|
+
}, 600);
|
|
5653
|
+
});
|
|
5654
|
+
|
|
5655
|
+
function setSaveState(state) {
|
|
5656
|
+
if (state === 'saved') {
|
|
5657
|
+
saveStatus.textContent = 'Saved';
|
|
5658
|
+
saveStatus.className = 'save-indicator saved';
|
|
5659
|
+
isDirty = false;
|
|
5660
|
+
} else if (state === 'saving') {
|
|
5661
|
+
saveStatus.textContent = 'Saving...';
|
|
5662
|
+
saveStatus.className = 'save-indicator saving';
|
|
5663
|
+
} else {
|
|
5664
|
+
saveStatus.textContent = 'Changes...';
|
|
5665
|
+
saveStatus.className = 'save-indicator unsaved';
|
|
5666
|
+
isDirty = true;
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
|
|
5670
|
+
// Save Content via API
|
|
5671
|
+
function saveContent(callback) {
|
|
5672
|
+
setSaveState('saving');
|
|
5673
|
+
fetch('/api/save-content', {
|
|
5674
|
+
method: 'POST',
|
|
5675
|
+
headers: { 'Content-Type': 'application/json' },
|
|
5676
|
+
body: JSON.stringify({ content: editor.value }),
|
|
5677
|
+
})
|
|
5678
|
+
.then(function(res) { return res.json(); })
|
|
5679
|
+
.then(function(data) {
|
|
5680
|
+
if (data.success) {
|
|
5681
|
+
setSaveState('saved');
|
|
5682
|
+
if (callback) callback();
|
|
5683
|
+
} else {
|
|
5684
|
+
saveStatus.textContent = 'Save Error';
|
|
5685
|
+
}
|
|
5686
|
+
})
|
|
5687
|
+
.catch(function() {
|
|
5688
|
+
saveStatus.textContent = 'Save Error';
|
|
5689
|
+
});
|
|
5690
|
+
}
|
|
5691
|
+
|
|
5692
|
+
function saveContentManual() {
|
|
5693
|
+
saveContent();
|
|
5694
|
+
}
|
|
5695
|
+
|
|
5696
|
+
// Keyboard Shortcuts: Tab (2 spaces), Shift+Tab, Ctrl+S
|
|
5697
|
+
editor.addEventListener('keydown', function(e) {
|
|
5698
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
|
|
5699
|
+
e.preventDefault();
|
|
5700
|
+
saveContent();
|
|
5701
|
+
return;
|
|
5702
|
+
}
|
|
5703
|
+
|
|
5704
|
+
if (e.key === 'Tab') {
|
|
5705
|
+
e.preventDefault();
|
|
5706
|
+
var start = this.selectionStart;
|
|
5707
|
+
var end = this.selectionEnd;
|
|
5708
|
+
this.value = this.value.substring(0, start) + ' ' + this.value.substring(end);
|
|
5709
|
+
this.selectionStart = this.selectionEnd = start + 2;
|
|
5710
|
+
updateStatsAndLines();
|
|
5711
|
+
setSaveState('unsaved');
|
|
5712
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5713
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5714
|
+
}
|
|
5715
|
+
});
|
|
5716
|
+
|
|
5717
|
+
// Formatting Snippet Injector
|
|
5718
|
+
function insertFormat(type) {
|
|
5719
|
+
var start = editor.selectionStart;
|
|
5720
|
+
var end = editor.selectionEnd;
|
|
5721
|
+
var selected = editor.value.substring(start, end);
|
|
5722
|
+
var replacement = '';
|
|
5723
|
+
|
|
5724
|
+
switch (type) {
|
|
5725
|
+
case 'h1': replacement = '# ' + (selected || 'Heading 1'); break;
|
|
5726
|
+
case 'h2': replacement = '## ' + (selected || 'Heading 2'); break;
|
|
5727
|
+
case 'h3': replacement = '### ' + (selected || 'Heading 3'); break;
|
|
5728
|
+
case 'bold': replacement = '**' + (selected || 'bold text') + '**'; break;
|
|
5729
|
+
case 'italic': replacement = '*' + (selected || 'italic text') + '*'; break;
|
|
5730
|
+
case 'code': replacement = '\`' + (selected || 'inline code') + '\`'; break;
|
|
5731
|
+
case 'quote': replacement = '> ' + (selected || 'Quote text'); break;
|
|
5732
|
+
case 'table':
|
|
5733
|
+
replacement = '\\n| Column 1 | Column 2 | Column 3 |\\n| :--- | :---: | ---: |\\n| Data A | Data B | Data C |\\n| Data D | Data E | Data F |\\n';
|
|
5734
|
+
break;
|
|
5735
|
+
case 'list': replacement = '- ' + (selected || 'List item'); break;
|
|
5736
|
+
case 'task': replacement = '- [ ] ' + (selected || 'Task item'); break;
|
|
5737
|
+
case 'callout':
|
|
5738
|
+
replacement = '> [!NOTE]\\n> ' + (selected || 'This is an important callout note.');
|
|
5739
|
+
break;
|
|
5740
|
+
case 'math':
|
|
5741
|
+
replacement = '$$\\n' + (selected || '\\\\int_{-\\\\infty}^{\\\\infty} e^{-x^2} dx = \\\\sqrt{\\\\pi}') + '\\n$$';
|
|
5742
|
+
break;
|
|
5743
|
+
case 'columns':
|
|
5744
|
+
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:::';
|
|
5745
|
+
break;
|
|
5746
|
+
case 'footnote':
|
|
5747
|
+
replacement = (selected || 'Statement with footnote') + '[^1]\\n\\n[^1]: Note description text.';
|
|
5748
|
+
break;
|
|
5749
|
+
case 'mermaid':
|
|
5750
|
+
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';
|
|
5751
|
+
break;
|
|
5752
|
+
}
|
|
5753
|
+
|
|
5754
|
+
editor.value = editor.value.substring(0, start) + replacement + editor.value.substring(end);
|
|
5755
|
+
editor.selectionStart = editor.selectionEnd = start + replacement.length;
|
|
5756
|
+
editor.focus();
|
|
5757
|
+
updateStatsAndLines();
|
|
5758
|
+
setSaveState('unsaved');
|
|
5759
|
+
if (autoSaveTimeout) clearTimeout(autoSaveTimeout);
|
|
5760
|
+
autoSaveTimeout = setTimeout(saveContent, 600);
|
|
5761
|
+
}
|
|
5762
|
+
|
|
5763
|
+
// View Mode Toggle (Split / Editor Only / Preview Only)
|
|
5764
|
+
function setViewMode(mode) {
|
|
5765
|
+
document.getElementById('btn-split').classList.remove('active');
|
|
5766
|
+
document.getElementById('btn-edit').classList.remove('active');
|
|
5767
|
+
document.getElementById('btn-prev').classList.remove('active');
|
|
5768
|
+
|
|
5769
|
+
if (mode === 'split') {
|
|
5770
|
+
document.getElementById('btn-split').classList.add('active');
|
|
5771
|
+
editorPane.style.display = 'flex';
|
|
5772
|
+
editorPane.style.width = '50%';
|
|
5773
|
+
previewPane.style.display = 'flex';
|
|
5774
|
+
previewPane.style.width = '50%';
|
|
5775
|
+
splitter.style.display = 'block';
|
|
5776
|
+
} else if (mode === 'edit') {
|
|
5777
|
+
document.getElementById('btn-edit').classList.add('active');
|
|
5778
|
+
editorPane.style.display = 'flex';
|
|
5779
|
+
editorPane.style.width = '100%';
|
|
5780
|
+
previewPane.style.display = 'none';
|
|
5781
|
+
splitter.style.display = 'none';
|
|
5782
|
+
} else if (mode === 'prev') {
|
|
5783
|
+
document.getElementById('btn-prev').classList.add('active');
|
|
5784
|
+
editorPane.style.display = 'none';
|
|
5785
|
+
previewPane.style.display = 'flex';
|
|
5786
|
+
previewPane.style.width = '100%';
|
|
5787
|
+
splitter.style.display = 'none';
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
|
|
5791
|
+
// Viewport Width Resizer
|
|
5792
|
+
function setViewport(width) {
|
|
5793
|
+
document.getElementById('vp-full').classList.remove('active');
|
|
5794
|
+
document.getElementById('vp-a4').classList.remove('active');
|
|
5795
|
+
document.getElementById('vp-mob').classList.remove('active');
|
|
5796
|
+
|
|
5797
|
+
if (width === '100%') {
|
|
5798
|
+
document.getElementById('vp-full').classList.add('active');
|
|
5799
|
+
previewFrame.style.maxWidth = '100%';
|
|
5800
|
+
} else if (width === '820px') {
|
|
5801
|
+
document.getElementById('vp-a4').classList.add('active');
|
|
5802
|
+
previewFrame.style.maxWidth = '820px';
|
|
5803
|
+
} else if (width === '440px') {
|
|
5804
|
+
document.getElementById('vp-mob').classList.add('active');
|
|
5805
|
+
previewFrame.style.maxWidth = '440px';
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5808
|
+
|
|
5809
|
+
// Draggable Splitter Handle Logic
|
|
5810
|
+
var isDragging = false;
|
|
5811
|
+
splitter.addEventListener('mousedown', function(e) {
|
|
5812
|
+
isDragging = true;
|
|
5813
|
+
splitter.classList.add('active');
|
|
5814
|
+
document.body.style.cursor = 'col-resize';
|
|
5815
|
+
document.body.style.userSelect = 'none';
|
|
5816
|
+
});
|
|
5817
|
+
|
|
5818
|
+
window.addEventListener('mousemove', function(e) {
|
|
5819
|
+
if (!isDragging) return;
|
|
5820
|
+
var totalWidth = document.getElementById('workspace').clientWidth;
|
|
5821
|
+
var newEditorWidth = (e.clientX / totalWidth) * 100;
|
|
5822
|
+
if (newEditorWidth > 15 && newEditorWidth < 85) {
|
|
5823
|
+
editorPane.style.width = newEditorWidth + '%';
|
|
5824
|
+
previewPane.style.width = (100 - newEditorWidth) + '%';
|
|
5825
|
+
}
|
|
5826
|
+
});
|
|
5827
|
+
|
|
5828
|
+
window.addEventListener('mouseup', function() {
|
|
5829
|
+
if (isDragging) {
|
|
5830
|
+
isDragging = false;
|
|
5831
|
+
splitter.classList.remove('active');
|
|
5832
|
+
document.body.style.cursor = '';
|
|
5833
|
+
document.body.style.userSelect = '';
|
|
5834
|
+
}
|
|
5835
|
+
});
|
|
5836
|
+
|
|
5837
|
+
// Document Print Action
|
|
5838
|
+
function printDoc() {
|
|
5839
|
+
previewFrame.contentWindow.print();
|
|
5840
|
+
}
|
|
5841
|
+
|
|
5842
|
+
// Document Export Action
|
|
5843
|
+
function exportDoc(format) {
|
|
5844
|
+
saveContent(function() {
|
|
5845
|
+
window.location.href = '/api/export?format=' + format;
|
|
5846
|
+
});
|
|
5847
|
+
}
|
|
5848
|
+
|
|
5849
|
+
// Initialize line stats
|
|
5850
|
+
updateStatsAndLines();
|
|
5851
|
+
</script>
|
|
5852
|
+
</body>
|
|
5853
|
+
</html>`;
|
|
5854
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5855
|
+
res.end(appHtml);
|
|
5856
|
+
return;
|
|
5857
|
+
}
|
|
5858
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
5859
|
+
res.end("Not Found");
|
|
5860
|
+
});
|
|
5861
|
+
return new Promise((resolve6, reject) => {
|
|
5862
|
+
server.listen(port, () => {
|
|
5863
|
+
const url = `http://localhost:${port}`;
|
|
5864
|
+
resolve6({
|
|
5865
|
+
server,
|
|
5866
|
+
port,
|
|
5867
|
+
url,
|
|
5868
|
+
close: async () => {
|
|
5869
|
+
watcher.close();
|
|
5870
|
+
sseClients.forEach((client) => {
|
|
5871
|
+
try {
|
|
5872
|
+
client.end();
|
|
5873
|
+
} catch {
|
|
5874
|
+
}
|
|
5875
|
+
});
|
|
5876
|
+
sseClients.clear();
|
|
5877
|
+
return new Promise((res) => {
|
|
5878
|
+
server.close(() => res());
|
|
5879
|
+
});
|
|
5880
|
+
}
|
|
5881
|
+
});
|
|
5882
|
+
});
|
|
5883
|
+
server.on("error", (err) => {
|
|
5884
|
+
reject(err);
|
|
5885
|
+
});
|
|
5886
|
+
});
|
|
5887
|
+
}
|
|
5888
|
+
function escapeHtml2(str) {
|
|
5889
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
5890
|
+
}
|
|
5891
|
+
|
|
3490
5892
|
// src/config/defineConfig.ts
|
|
3491
5893
|
function defineConfig(config) {
|
|
3492
5894
|
return config;
|
|
3493
5895
|
}
|
|
3494
5896
|
|
|
3495
5897
|
// src/version.ts
|
|
3496
|
-
import * as
|
|
3497
|
-
import * as
|
|
5898
|
+
import * as fs8 from "fs";
|
|
5899
|
+
import * as path8 from "path";
|
|
3498
5900
|
import { fileURLToPath } from "url";
|
|
3499
5901
|
try {
|
|
3500
5902
|
if (typeof globalThis !== "undefined" && (!globalThis.localStorage || typeof globalThis.localStorage.getItem !== "function")) {
|
|
@@ -3516,32 +5918,33 @@ try {
|
|
|
3516
5918
|
}
|
|
3517
5919
|
} catch {
|
|
3518
5920
|
}
|
|
3519
|
-
var FALLBACK_VERSION = "0.3.0";
|
|
3520
5921
|
function readVersionFromPackageJson(fromDir) {
|
|
3521
5922
|
let currentDir = fromDir;
|
|
3522
|
-
for (let i = 0; i <
|
|
5923
|
+
for (let i = 0; i < 10; i++) {
|
|
3523
5924
|
try {
|
|
3524
|
-
const pkgJsonPath =
|
|
3525
|
-
if (
|
|
3526
|
-
const pkg = JSON.parse(
|
|
5925
|
+
const pkgJsonPath = path8.join(currentDir, "package.json");
|
|
5926
|
+
if (fs8.existsSync(pkgJsonPath)) {
|
|
5927
|
+
const pkg = JSON.parse(fs8.readFileSync(pkgJsonPath, "utf-8"));
|
|
3527
5928
|
if (pkg.name === "@masumdev/markforge" && pkg.version) {
|
|
3528
5929
|
return pkg.version;
|
|
3529
5930
|
}
|
|
3530
5931
|
}
|
|
3531
5932
|
} catch {
|
|
3532
5933
|
}
|
|
3533
|
-
const parentDir =
|
|
5934
|
+
const parentDir = path8.dirname(currentDir);
|
|
3534
5935
|
if (parentDir === currentDir) break;
|
|
3535
5936
|
currentDir = parentDir;
|
|
3536
5937
|
}
|
|
3537
|
-
|
|
5938
|
+
throw new Error(
|
|
5939
|
+
"Failed to resolve '@masumdev/markforge' package version: package.json was not found or is missing a valid 'version' field."
|
|
5940
|
+
);
|
|
3538
5941
|
}
|
|
3539
5942
|
function getPackageDir() {
|
|
3540
5943
|
if (typeof __dirname !== "undefined") {
|
|
3541
5944
|
return __dirname;
|
|
3542
5945
|
}
|
|
3543
5946
|
try {
|
|
3544
|
-
return
|
|
5947
|
+
return path8.dirname(fileURLToPath(import.meta.url));
|
|
3545
5948
|
} catch {
|
|
3546
5949
|
return process.cwd();
|
|
3547
5950
|
}
|
|
@@ -3552,6 +5955,7 @@ function getMarkforgeVersion(fromDir = getPackageDir()) {
|
|
|
3552
5955
|
}
|
|
3553
5956
|
export {
|
|
3554
5957
|
DEFAULT_CONFIG,
|
|
5958
|
+
KATEX_INLINE_CSS,
|
|
3555
5959
|
MARKFORGE_VERSION,
|
|
3556
5960
|
Orientation,
|
|
3557
5961
|
OutputFormat,
|
|
@@ -3564,6 +5968,7 @@ export {
|
|
|
3564
5968
|
THEME_DEFAULT,
|
|
3565
5969
|
Theme,
|
|
3566
5970
|
WatermarkPosition,
|
|
5971
|
+
applyHeadingNumbering,
|
|
3567
5972
|
buildDocxDocument,
|
|
3568
5973
|
buildHtmlDocument,
|
|
3569
5974
|
buildPdfDocument,
|
|
@@ -3580,18 +5985,28 @@ export {
|
|
|
3580
5985
|
inlineHtmlImages,
|
|
3581
5986
|
loadConfig,
|
|
3582
5987
|
compileMarkdown as markforge,
|
|
5988
|
+
normalizeBackCover,
|
|
5989
|
+
normalizeCoverPage,
|
|
3583
5990
|
normalizeHeaderFooter,
|
|
3584
5991
|
normalizeHeaderFooterSlot,
|
|
5992
|
+
normalizeNumberHeadings,
|
|
5993
|
+
normalizeSecurity,
|
|
3585
5994
|
normalizeSignatures,
|
|
3586
5995
|
normalizeWatermark,
|
|
3587
5996
|
parseInlineSpans,
|
|
3588
5997
|
parseMarginToTwip2 as parseMarginToTwip,
|
|
5998
|
+
parseMarkdownDocument as parseMarkdown,
|
|
3589
5999
|
parseMarkdownDocument,
|
|
6000
|
+
renderBackCoverHtml,
|
|
6001
|
+
renderCoverPageHtml,
|
|
3590
6002
|
renderInlinesToHtml,
|
|
6003
|
+
renderMathToHtml,
|
|
3591
6004
|
renderMermaidToPng,
|
|
6005
|
+
renderNodesToHtml,
|
|
3592
6006
|
replaceDocumentTokens,
|
|
3593
6007
|
resolveDocumentConfig,
|
|
3594
6008
|
resolveImage,
|
|
3595
6009
|
slugify,
|
|
6010
|
+
startPreviewServer,
|
|
3596
6011
|
tokenizeCodeLine
|
|
3597
6012
|
};
|