@akairos/typeset-studio 0.2.1 → 0.2.2
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 +2 -3
- package/dist/assets/index-DDeEGeHD.js +317 -0
- package/dist/index.html +1 -1
- package/dist/lib/adapter.js +82 -234
- package/dist/lib/index.js +82 -234
- package/package.json +2 -5
- package/src/adapter.ts +85 -267
- package/src/core/canvas-draw.ts +9 -9
- package/src/core/engine.ts +14 -22
- package/src/core/pretext.ts +0 -8
- package/src/core/template.ts +1 -1
- package/src/core/typography.ts +0 -14
- package/src/index.ts +2 -2
- package/src/server/index.ts +1 -1
- package/src/server/routes/fonts.ts +1 -1
- package/src/server/routes/templates.ts +4 -14
- package/src/web/App.tsx +0 -2
- package/src/web/components/EditorSidebar.tsx +24 -48
- package/src/web/components/Header.tsx +1 -1
- package/src/web/components/TemplateModal.tsx +3 -14
- package/src/web/hooks/useTypeset.ts +21 -69
- package/dist/assets/index-Dpnz-F2g.js +0 -317
package/dist/lib/index.js
CHANGED
|
@@ -67,22 +67,22 @@ function computeStageDimensions(spec, safeArea, contentBottomGap = 20) {
|
|
|
67
67
|
function resolveCanvasFontFamily(fontName, isServer = false) {
|
|
68
68
|
const rawFirst = (fontName || "").split(",")[0] || "";
|
|
69
69
|
const trimmed = rawFirst.replace(/["'\\]/g, "").trim();
|
|
70
|
-
if (trimmed.includes("Noto Sans")
|
|
70
|
+
if (trimmed.includes("Noto Sans")) {
|
|
71
71
|
return isServer ? '"Hiragino Sans GB", sans-serif' : '"Noto Sans SC", "PingFang SC", sans-serif';
|
|
72
72
|
}
|
|
73
|
-
if (trimmed.includes("PingFang")
|
|
73
|
+
if (trimmed.includes("PingFang")) {
|
|
74
74
|
return isServer ? '"Hiragino Sans GB", sans-serif' : '"PingFang SC", "Noto Sans SC", sans-serif';
|
|
75
75
|
}
|
|
76
|
-
if (trimmed.includes("Noto Serif")
|
|
76
|
+
if (trimmed.includes("Noto Serif")) {
|
|
77
77
|
return isServer ? '"Songti SC", serif' : '"Noto Serif SC", "Songti SC", serif';
|
|
78
78
|
}
|
|
79
|
-
if (trimmed.includes("Songti") || trimmed === "
|
|
79
|
+
if (trimmed.includes("Songti") || trimmed === "STSong") {
|
|
80
80
|
return isServer ? '"Songti SC", serif' : '"Songti SC", "Noto Serif SC", serif';
|
|
81
81
|
}
|
|
82
82
|
if (trimmed.includes("Kai") || trimmed.includes("WenKai")) {
|
|
83
83
|
return isServer ? '"STKaiti", "Kaiti SC", "Songti SC", serif' : '"LXGW WenKai GB Screen", "LXGW WenKai Screen", "Kaiti SC", "STKaiti", serif';
|
|
84
84
|
}
|
|
85
|
-
if (trimmed.includes("Yuan")
|
|
85
|
+
if (trimmed.includes("Yuan")) {
|
|
86
86
|
return isServer ? '"STYuanti", "Yuanti SC", "Hiragino Sans GB", sans-serif' : '"Yuanti SC", "STYuanti", "PingFang SC", sans-serif';
|
|
87
87
|
}
|
|
88
88
|
if (trimmed.includes("Inter")) {
|
|
@@ -92,7 +92,7 @@ function resolveCanvasFontFamily(fontName, isServer = false) {
|
|
|
92
92
|
return isServer ? 'Georgia, "Songti SC", serif' : 'Georgia, "Songti SC", serif';
|
|
93
93
|
}
|
|
94
94
|
if (trimmed.toLowerCase().includes("serif") || trimmed.includes("Song")) {
|
|
95
|
-
return
|
|
95
|
+
return `"${trimmed}", "Songti SC", serif`;
|
|
96
96
|
}
|
|
97
97
|
return isServer ? `"${trimmed}", "Hiragino Sans GB", "PingFang SC", sans-serif` : `"${trimmed}", "PingFang SC", sans-serif`;
|
|
98
98
|
}
|
|
@@ -114,14 +114,14 @@ async function drawPageToContext(ctx, options) {
|
|
|
114
114
|
ctx.fillStyle = theme.bgColor;
|
|
115
115
|
ctx.fillRect(0, 0, spec.width, spec.height);
|
|
116
116
|
ctx.restore();
|
|
117
|
-
const {
|
|
117
|
+
const { stageX, stageY } = computeStageDimensions(spec, algorithm.safeArea, algorithm.contentBottomGap);
|
|
118
118
|
const isCover = config.aspectRatio === "wechat-cover-2.35";
|
|
119
119
|
const showHeader = chrome?.showHeader ?? !isCover;
|
|
120
120
|
const showWatermark = (chrome?.showWatermark ?? !isCover) && Boolean(config.watermarkText?.trim());
|
|
121
121
|
const showPageNum = (chrome?.showPageNumber ?? (!isCover && totalPageCount > 1)) && totalPageCount > 1;
|
|
122
122
|
if (showHeader && (showWatermark || showPageNum)) {
|
|
123
123
|
const headerY = Math.max(24, Math.floor(algorithm.safeArea.top * 0.45));
|
|
124
|
-
if (showWatermark
|
|
124
|
+
if (showWatermark) {
|
|
125
125
|
ctx.save();
|
|
126
126
|
ctx.font = resolveCanvasFont("600 14px PingFang SC", isServer);
|
|
127
127
|
ctx.fillStyle = theme.watermarkColor;
|
|
@@ -265,7 +265,7 @@ async function drawPageToContext(ctx, options) {
|
|
|
265
265
|
ctx.stroke();
|
|
266
266
|
ctx.restore();
|
|
267
267
|
}
|
|
268
|
-
if (showFooterText
|
|
268
|
+
if (showFooterText) {
|
|
269
269
|
ctx.save();
|
|
270
270
|
ctx.font = resolveCanvasFont("500 13px PingFang SC", isServer);
|
|
271
271
|
ctx.fillStyle = theme.watermarkColor || theme.captionColor;
|
|
@@ -6004,6 +6004,7 @@ function materializeRichInlineLineRange(prepared, line) {
|
|
|
6004
6004
|
end: line.end
|
|
6005
6005
|
};
|
|
6006
6006
|
}
|
|
6007
|
+
|
|
6007
6008
|
// src/core/pretext.ts
|
|
6008
6009
|
var initialized = false;
|
|
6009
6010
|
function ensurePretextRuntime() {
|
|
@@ -6583,21 +6584,21 @@ function runTypeset(params) {
|
|
|
6583
6584
|
headingFont: config.typography.headingFont,
|
|
6584
6585
|
bodyFont: config.typography.bodyFont
|
|
6585
6586
|
});
|
|
6587
|
+
const createFlowBlocks = (text) => parseContentBlocks(text).map((block) => ({
|
|
6588
|
+
...blockStyles[block.kind],
|
|
6589
|
+
kind: block.kind,
|
|
6590
|
+
text: block.text,
|
|
6591
|
+
runs: parseInlineRuns(block.text, highlightWords),
|
|
6592
|
+
bullet: block.kind === "list-item" ? block.bullet ?? "•" : undefined,
|
|
6593
|
+
textIndent: block.kind === "list-item" ? 28 : block.kind === "quote" ? 20 : 0,
|
|
6594
|
+
keepWithNext: ["heading", "subheading", "minor-heading"].includes(block.kind)
|
|
6595
|
+
}));
|
|
6586
6596
|
const pageSegments = splitTextByPages(markdown);
|
|
6587
6597
|
let pages = [];
|
|
6588
6598
|
if (pageSegments.length > 1) {
|
|
6589
6599
|
pages = pageSegments.map((segmentText, index) => {
|
|
6590
6600
|
const pageNum = index + 1;
|
|
6591
|
-
const
|
|
6592
|
-
const flowBlocks = rawBlocks.map((b) => ({
|
|
6593
|
-
...blockStyles[b.kind],
|
|
6594
|
-
kind: b.kind,
|
|
6595
|
-
text: b.text,
|
|
6596
|
-
runs: parseInlineRuns(b.text, highlightWords),
|
|
6597
|
-
bullet: b.kind === "list-item" ? b.bullet ?? "•" : undefined,
|
|
6598
|
-
textIndent: b.kind === "list-item" ? 28 : b.kind === "quote" ? 20 : 0,
|
|
6599
|
-
keepWithNext: ["heading", "subheading", "minor-heading"].includes(b.kind)
|
|
6600
|
-
}));
|
|
6601
|
+
const flowBlocks = createFlowBlocks(segmentText);
|
|
6601
6602
|
const pageImages = images.filter((img) => {
|
|
6602
6603
|
if (img.pageIndex !== undefined && img.pageIndex !== null && img.pageIndex > 0) {
|
|
6603
6604
|
return img.pageIndex === pageNum;
|
|
@@ -6624,16 +6625,7 @@ function runTypeset(params) {
|
|
|
6624
6625
|
};
|
|
6625
6626
|
});
|
|
6626
6627
|
} else {
|
|
6627
|
-
const
|
|
6628
|
-
const flowBlocks = rawBlocks.map((b) => ({
|
|
6629
|
-
...blockStyles[b.kind],
|
|
6630
|
-
kind: b.kind,
|
|
6631
|
-
text: b.text,
|
|
6632
|
-
runs: parseInlineRuns(b.text, highlightWords),
|
|
6633
|
-
bullet: b.kind === "list-item" ? b.bullet ?? "•" : undefined,
|
|
6634
|
-
textIndent: b.kind === "list-item" ? 28 : b.kind === "quote" ? 20 : 0,
|
|
6635
|
-
keepWithNext: ["heading", "subheading", "minor-heading"].includes(b.kind)
|
|
6636
|
-
}));
|
|
6628
|
+
const flowBlocks = createFlowBlocks(markdown);
|
|
6637
6629
|
pages = paginateBlocks(flowBlocks, stageWidth, stageHeight, images, {
|
|
6638
6630
|
obstacleGap: config.algorithm.obstacleGap,
|
|
6639
6631
|
pageImageLimit: config.aspectRatio === "longform-3-4" ? 2 : 1,
|
|
@@ -7132,6 +7124,7 @@ function findTemplateByKeyOrName(key) {
|
|
|
7132
7124
|
} catch {}
|
|
7133
7125
|
}
|
|
7134
7126
|
const files = readdirSync2(TEMPLATES_DIR).filter((f) => f.endsWith(".json"));
|
|
7127
|
+
const templates = [];
|
|
7135
7128
|
for (const file of files) {
|
|
7136
7129
|
try {
|
|
7137
7130
|
const content = readFileSync2(join3(TEMPLATES_DIR, file), "utf-8");
|
|
@@ -7139,18 +7132,10 @@ function findTemplateByKeyOrName(key) {
|
|
|
7139
7132
|
if (tpl.id?.toLowerCase() === lowerKey || tpl.name.toLowerCase() === lowerKey) {
|
|
7140
7133
|
return tpl;
|
|
7141
7134
|
}
|
|
7135
|
+
templates.push(tpl);
|
|
7142
7136
|
} catch {}
|
|
7143
7137
|
}
|
|
7144
|
-
|
|
7145
|
-
try {
|
|
7146
|
-
const content = readFileSync2(join3(TEMPLATES_DIR, file), "utf-8");
|
|
7147
|
-
const tpl = JSON.parse(content);
|
|
7148
|
-
if (tpl.name.toLowerCase().includes(lowerKey)) {
|
|
7149
|
-
return tpl;
|
|
7150
|
-
}
|
|
7151
|
-
} catch {}
|
|
7152
|
-
}
|
|
7153
|
-
return null;
|
|
7138
|
+
return templates.find((tpl) => tpl.name.toLowerCase().includes(lowerKey)) ?? null;
|
|
7154
7139
|
}
|
|
7155
7140
|
var templatesRouter = new Hono3;
|
|
7156
7141
|
templatesRouter.get("/", (c) => {
|
|
@@ -7442,7 +7427,7 @@ function mapBodyImages(bodyImages, pageSpecs) {
|
|
|
7442
7427
|
});
|
|
7443
7428
|
}
|
|
7444
7429
|
var REMOTE_STUDIO_URL = process.env.TYPESET_STUDIO_URL?.replace(/\/$/, "");
|
|
7445
|
-
async function renderRemoteBatch(studioUrl, body, outputDir,
|
|
7430
|
+
async function renderRemoteBatch(studioUrl, body, outputDir, route, kind) {
|
|
7446
7431
|
const res = await fetch(`${studioUrl}/api/render/export`, {
|
|
7447
7432
|
method: "POST",
|
|
7448
7433
|
headers: { "Content-Type": "application/json" },
|
|
@@ -7454,12 +7439,11 @@ async function renderRemoteBatch(studioUrl, body, outputDir, filenamePrefix, rou
|
|
|
7454
7439
|
const data = await res.json();
|
|
7455
7440
|
const assets = [];
|
|
7456
7441
|
const pagesSummary = [];
|
|
7457
|
-
for (
|
|
7458
|
-
const pageItem = data.pages[i];
|
|
7442
|
+
for (const pageItem of data.pages) {
|
|
7459
7443
|
const pageNum = pageItem.pageNumber;
|
|
7460
7444
|
const base64Data = pageItem.dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
7461
7445
|
const buffer = Buffer.from(base64Data, "base64");
|
|
7462
|
-
const filename = kind === "cover" ? "cover.png" :
|
|
7446
|
+
const filename = kind === "cover" ? "cover.png" : `article-${String(pageNum).padStart(2, "0")}.png`;
|
|
7463
7447
|
const filePath = join4(outputDir, filename);
|
|
7464
7448
|
await writeFile(filePath, buffer);
|
|
7465
7449
|
assets.push({
|
|
@@ -7476,9 +7460,33 @@ async function renderRemoteBatch(studioUrl, body, outputDir, filenamePrefix, rou
|
|
|
7476
7460
|
}
|
|
7477
7461
|
return { assets, pageCount: data.pageCount, pages: pagesSummary };
|
|
7478
7462
|
}
|
|
7463
|
+
async function renderLocalBatch(body, outputDir, route, kind) {
|
|
7464
|
+
const { result } = resolveTypesetResult(body);
|
|
7465
|
+
const pngs = await renderTypesetResultToPngs(result);
|
|
7466
|
+
const outputs = kind === "cover" ? pngs.slice(0, 1) : pngs;
|
|
7467
|
+
if (kind === "cover" && outputs.length === 0) {
|
|
7468
|
+
throw new Error("Typeset Studio failed to render cover buffer");
|
|
7469
|
+
}
|
|
7470
|
+
const assets = [];
|
|
7471
|
+
const pages = [];
|
|
7472
|
+
for (const [index, output] of outputs.entries()) {
|
|
7473
|
+
const pageNumber = index + 1;
|
|
7474
|
+
const filename = kind === "cover" ? "cover.png" : `article-${String(pageNumber).padStart(2, "0")}.png`;
|
|
7475
|
+
const path = join4(outputDir, filename);
|
|
7476
|
+
await writeFile(path, output.buffer);
|
|
7477
|
+
assets.push({ kind, route, path, ...kind === "page" ? { index: pageNumber } : {} });
|
|
7478
|
+
const images = result.pages[index].images;
|
|
7479
|
+
pages.push({
|
|
7480
|
+
page: pageNumber,
|
|
7481
|
+
imageCount: images.length,
|
|
7482
|
+
imageSources: images.map((image) => image.src)
|
|
7483
|
+
});
|
|
7484
|
+
}
|
|
7485
|
+
return { assets, pageCount: outputs.length, pages };
|
|
7486
|
+
}
|
|
7479
7487
|
var typesetStudioAdapter = {
|
|
7480
7488
|
name: "typeset-studio",
|
|
7481
|
-
version: "0.2.
|
|
7489
|
+
version: "0.2.2",
|
|
7482
7490
|
async doctor() {
|
|
7483
7491
|
const checks = [];
|
|
7484
7492
|
if (REMOTE_STUDIO_URL) {
|
|
@@ -7512,7 +7520,7 @@ var typesetStudioAdapter = {
|
|
|
7512
7520
|
} catch (err) {
|
|
7513
7521
|
checks.push({
|
|
7514
7522
|
name: "@napi-rs/canvas",
|
|
7515
|
-
ok:
|
|
7523
|
+
ok: Boolean(REMOTE_STUDIO_URL),
|
|
7516
7524
|
message: REMOTE_STUDIO_URL ? "@napi-rs/canvas not installed locally (using Cloud Typeset Studio microservice)" : `@napi-rs/canvas initialization failed: ${err}`
|
|
7517
7525
|
});
|
|
7518
7526
|
}
|
|
@@ -7533,201 +7541,41 @@ var typesetStudioAdapter = {
|
|
|
7533
7541
|
return checks;
|
|
7534
7542
|
},
|
|
7535
7543
|
async render(input) {
|
|
7536
|
-
const { route, outputDir,
|
|
7544
|
+
const { route, outputDir, bodyText, state } = input;
|
|
7537
7545
|
await mkdir(outputDir, { recursive: true });
|
|
7546
|
+
if (route === "blog")
|
|
7547
|
+
return { assets: [], pageCount: 0, pages: [] };
|
|
7538
7548
|
const account = state.route.account || "default";
|
|
7539
|
-
const
|
|
7540
|
-
const
|
|
7541
|
-
const watermarkText = account ? `@${account}` : undefined;
|
|
7542
|
-
const effectiveTitle = cleanTitle(title || state.metadata?.title || "");
|
|
7543
|
-
if (REMOTE_STUDIO_URL) {
|
|
7544
|
-
if (route === "wechat-article") {
|
|
7545
|
-
const coverImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7546
|
-
return await renderRemoteBatch(REMOTE_STUDIO_URL, {
|
|
7547
|
-
slot: "article_cover",
|
|
7548
|
-
route: "wechat-article",
|
|
7549
|
-
account,
|
|
7550
|
-
title: effectiveTitle,
|
|
7551
|
-
markdown: `# ${effectiveTitle}`,
|
|
7552
|
-
template: input.template,
|
|
7553
|
-
images: coverImages2,
|
|
7554
|
-
highlightWords,
|
|
7555
|
-
config: { footerText, watermarkText }
|
|
7556
|
-
}, outputDir, "cover", "wechat-article", "cover");
|
|
7557
|
-
}
|
|
7558
|
-
if (route === "wechat-newspic") {
|
|
7559
|
-
const newspicSpec2 = state.intent?.newspic_render;
|
|
7560
|
-
const paginationMode2 = input.template === "longform-3-4" ? "multi" : input.template === "poster-3-4" ? "single" : newspicSpec2?.pagination_mode || "multi";
|
|
7561
|
-
const isMultiPage2 = paginationMode2 === "multi";
|
|
7562
|
-
const coverImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7563
|
-
const coverRes = await renderRemoteBatch(REMOTE_STUDIO_URL, {
|
|
7564
|
-
slot: "newspic_cover",
|
|
7565
|
-
route: "wechat-newspic",
|
|
7566
|
-
kind: "cover",
|
|
7567
|
-
account,
|
|
7568
|
-
title: effectiveTitle,
|
|
7569
|
-
markdown: `# ${effectiveTitle}`,
|
|
7570
|
-
template: input.template,
|
|
7571
|
-
images: coverImages2,
|
|
7572
|
-
highlightWords,
|
|
7573
|
-
config: { footerText, watermarkText }
|
|
7574
|
-
}, outputDir, "cover", "wechat-newspic", "cover");
|
|
7575
|
-
if (!isMultiPage2) {
|
|
7576
|
-
return coverRes;
|
|
7577
|
-
}
|
|
7578
|
-
const contentImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7579
|
-
const contentRes = await renderRemoteBatch(REMOTE_STUDIO_URL, {
|
|
7580
|
-
slot: "newspic_content",
|
|
7581
|
-
route: "wechat-newspic",
|
|
7582
|
-
kind: "content",
|
|
7583
|
-
account,
|
|
7584
|
-
title: effectiveTitle,
|
|
7585
|
-
markdown: bodyText || `# ${effectiveTitle}`,
|
|
7586
|
-
template: input.template,
|
|
7587
|
-
images: contentImages2,
|
|
7588
|
-
highlightWords,
|
|
7589
|
-
config: { footerText, watermarkText }
|
|
7590
|
-
}, outputDir, "article", "wechat-newspic", "page");
|
|
7591
|
-
return {
|
|
7592
|
-
assets: [...coverRes.assets, ...contentRes.assets],
|
|
7593
|
-
pageCount: contentRes.pageCount,
|
|
7594
|
-
pages: contentRes.pages
|
|
7595
|
-
};
|
|
7596
|
-
}
|
|
7597
|
-
}
|
|
7598
|
-
if (route === "wechat-article") {
|
|
7599
|
-
const coverImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7600
|
-
const resolved = resolveTypesetResult({
|
|
7601
|
-
slot: "article_cover",
|
|
7602
|
-
route: "wechat-article",
|
|
7603
|
-
account,
|
|
7604
|
-
title: effectiveTitle,
|
|
7605
|
-
markdown: `# ${effectiveTitle}`,
|
|
7606
|
-
template: input.template,
|
|
7607
|
-
images: coverImages2,
|
|
7608
|
-
highlightWords,
|
|
7609
|
-
config: {
|
|
7610
|
-
footerText: footerText ?? undefined,
|
|
7611
|
-
watermarkText: watermarkText ?? undefined
|
|
7612
|
-
}
|
|
7613
|
-
});
|
|
7614
|
-
const pngOutputs = await renderTypesetResultToPngs(resolved.result);
|
|
7615
|
-
const coverBuffer2 = pngOutputs[0]?.buffer;
|
|
7616
|
-
if (!coverBuffer2) {
|
|
7617
|
-
throw new Error("Typeset Studio failed to render cover buffer");
|
|
7618
|
-
}
|
|
7619
|
-
const coverPath2 = join4(outputDir, "cover.png");
|
|
7620
|
-
await writeFile(coverPath2, coverBuffer2);
|
|
7621
|
-
const asset = {
|
|
7622
|
-
kind: "cover",
|
|
7623
|
-
route: "wechat-article",
|
|
7624
|
-
path: coverPath2
|
|
7625
|
-
};
|
|
7626
|
-
return {
|
|
7627
|
-
assets: [asset],
|
|
7628
|
-
pageCount: 1,
|
|
7629
|
-
pages: [
|
|
7630
|
-
{
|
|
7631
|
-
page: 1,
|
|
7632
|
-
imageCount: resolved.result.pages[0]?.images.length || 0,
|
|
7633
|
-
imageSources: (resolved.result.pages[0]?.images || []).map((img) => img.src)
|
|
7634
|
-
}
|
|
7635
|
-
]
|
|
7636
|
-
};
|
|
7637
|
-
}
|
|
7638
|
-
if (route === "blog") {
|
|
7639
|
-
return {
|
|
7640
|
-
assets: [],
|
|
7641
|
-
pageCount: 0,
|
|
7642
|
-
pages: []
|
|
7643
|
-
};
|
|
7644
|
-
}
|
|
7645
|
-
const newspicSpec = state.intent?.newspic_render;
|
|
7646
|
-
const paginationMode = input.template === "longform-3-4" ? "multi" : input.template === "poster-3-4" ? "single" : newspicSpec?.pagination_mode || "multi";
|
|
7647
|
-
const isMultiPage = paginationMode === "multi";
|
|
7648
|
-
const assets = [];
|
|
7649
|
-
const pagesSummary = [];
|
|
7650
|
-
const coverImages = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7651
|
-
const resolvedCover = resolveTypesetResult({
|
|
7652
|
-
slot: "newspic_cover",
|
|
7653
|
-
route: "wechat-newspic",
|
|
7654
|
-
kind: "cover",
|
|
7549
|
+
const title = cleanTitle(input.title || state.metadata?.title || "");
|
|
7550
|
+
const common = {
|
|
7655
7551
|
account,
|
|
7656
|
-
title
|
|
7657
|
-
markdown: `# ${effectiveTitle}`,
|
|
7552
|
+
title,
|
|
7658
7553
|
template: input.template,
|
|
7659
|
-
images:
|
|
7660
|
-
highlightWords,
|
|
7661
|
-
config: {
|
|
7662
|
-
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
const
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
if (!isMultiPage) {
|
|
7679
|
-
return {
|
|
7680
|
-
assets,
|
|
7681
|
-
pageCount: 1,
|
|
7682
|
-
pages: [
|
|
7683
|
-
{
|
|
7684
|
-
page: 1,
|
|
7685
|
-
imageCount: resolvedCover.result.pages[0]?.images.length || 0,
|
|
7686
|
-
imageSources: (resolvedCover.result.pages[0]?.images || []).map((img) => img.src)
|
|
7687
|
-
}
|
|
7688
|
-
]
|
|
7689
|
-
};
|
|
7690
|
-
}
|
|
7691
|
-
const contentImages = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7692
|
-
const resolvedContent = resolveTypesetResult({
|
|
7693
|
-
slot: "newspic_content",
|
|
7554
|
+
images: mapBodyImages(input.bodyImages, input.pageSpecs),
|
|
7555
|
+
highlightWords: state.route.highlight_words || [],
|
|
7556
|
+
config: { footerText: input.accountVisualParams?.footer, watermarkText: `@${account}` }
|
|
7557
|
+
};
|
|
7558
|
+
const articleCover = route === "wechat-article";
|
|
7559
|
+
const renderRoute = articleCover ? "wechat-article" : "wechat-newspic";
|
|
7560
|
+
const renderBatch = (body, kind) => REMOTE_STUDIO_URL && (route === "wechat-article" || route === "wechat-newspic") ? renderRemoteBatch(REMOTE_STUDIO_URL, body, outputDir, renderRoute, kind) : renderLocalBatch(body, outputDir, renderRoute, kind);
|
|
7561
|
+
const cover = await renderBatch({
|
|
7562
|
+
...common,
|
|
7563
|
+
route: renderRoute,
|
|
7564
|
+
slot: articleCover ? "article_cover" : "newspic_cover",
|
|
7565
|
+
...articleCover ? {} : { kind: "cover" },
|
|
7566
|
+
markdown: `# ${title}`
|
|
7567
|
+
}, "cover");
|
|
7568
|
+
const paginationMode = input.template === "longform-3-4" ? "multi" : input.template === "poster-3-4" ? "single" : state.intent?.newspic_render?.pagination_mode || "multi";
|
|
7569
|
+
if (articleCover || paginationMode !== "multi")
|
|
7570
|
+
return cover;
|
|
7571
|
+
const content = await renderBatch({
|
|
7572
|
+
...common,
|
|
7694
7573
|
route: "wechat-newspic",
|
|
7695
7574
|
kind: "content",
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
images: contentImages,
|
|
7701
|
-
highlightWords,
|
|
7702
|
-
config: {
|
|
7703
|
-
footerText: footerText ?? undefined,
|
|
7704
|
-
watermarkText: watermarkText ?? undefined
|
|
7705
|
-
}
|
|
7706
|
-
});
|
|
7707
|
-
const contentPngs = await renderTypesetResultToPngs(resolvedContent.result);
|
|
7708
|
-
for (let i = 0;i < contentPngs.length; i++) {
|
|
7709
|
-
const pageNum = i + 1;
|
|
7710
|
-
const fileName = `article-${String(pageNum).padStart(2, "0")}.png`;
|
|
7711
|
-
const pagePath = join4(outputDir, fileName);
|
|
7712
|
-
await writeFile(pagePath, contentPngs[i].buffer);
|
|
7713
|
-
assets.push({
|
|
7714
|
-
kind: "page",
|
|
7715
|
-
route: "wechat-newspic",
|
|
7716
|
-
index: pageNum,
|
|
7717
|
-
path: pagePath
|
|
7718
|
-
});
|
|
7719
|
-
const flowPage = resolvedContent.result.pages[i];
|
|
7720
|
-
pagesSummary.push({
|
|
7721
|
-
page: pageNum,
|
|
7722
|
-
imageCount: flowPage?.images.length || 0,
|
|
7723
|
-
imageSources: (flowPage?.images || []).map((img) => img.src)
|
|
7724
|
-
});
|
|
7725
|
-
}
|
|
7726
|
-
return {
|
|
7727
|
-
assets,
|
|
7728
|
-
pageCount: contentPngs.length,
|
|
7729
|
-
pages: pagesSummary
|
|
7730
|
-
};
|
|
7575
|
+
slot: "newspic_content",
|
|
7576
|
+
markdown: bodyText || `# ${title}`
|
|
7577
|
+
}, "page");
|
|
7578
|
+
return { ...content, assets: [...cover.assets, ...content.assets] };
|
|
7731
7579
|
}
|
|
7732
7580
|
};
|
|
7733
7581
|
var plugin = typesetStudioAdapter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akairos/typeset-studio",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Algorithmic typesetting studio & zzhub-pipeline image renderer plugin",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -55,13 +55,10 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@chenglou/pretext": "^0.0.8",
|
|
57
57
|
"@napi-rs/canvas": "^1.0.10",
|
|
58
|
-
"clsx": "^2.1.1",
|
|
59
58
|
"hono": "^4.7.4",
|
|
60
|
-
"html-to-image": "^1.11.11",
|
|
61
59
|
"lucide-react": "^1.16.0",
|
|
62
60
|
"react": "^19.0.0",
|
|
63
|
-
"react-dom": "^19.0.0"
|
|
64
|
-
"tailwind-merge": "^3.0.2"
|
|
61
|
+
"react-dom": "^19.0.0"
|
|
65
62
|
},
|
|
66
63
|
"devDependencies": {
|
|
67
64
|
"@types/bun": "latest",
|