@akairos/typeset-studio 0.2.0 → 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 +424 -543
- package/dist/lib/index.js +424 -543
- 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 +54 -25
- 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/adapter.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,
|
|
@@ -6696,331 +6688,357 @@ function applyTemplateImages(currentImages, templateImages) {
|
|
|
6696
6688
|
|
|
6697
6689
|
// src/server/routes/templates.ts
|
|
6698
6690
|
import { existsSync as existsSync3, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, unlinkSync, writeFileSync } from "fs";
|
|
6699
|
-
import { join as join3 } from "path";
|
|
6700
|
-
|
|
6691
|
+
import { dirname, join as join3, resolve as resolve2 } from "path";
|
|
6692
|
+
import { fileURLToPath } from "url";
|
|
6693
|
+
var __dirname = "/Users/aatrox/.oh-my-zzhub/core/typeset-studio/src/server/routes";
|
|
6694
|
+
function resolveDataDir() {
|
|
6695
|
+
if (process.env.TYPESET_DATA_DIR) {
|
|
6696
|
+
return process.env.TYPESET_DATA_DIR;
|
|
6697
|
+
}
|
|
6698
|
+
const currentDir = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
|
|
6699
|
+
const candidateLib = resolve2(currentDir, "../../data");
|
|
6700
|
+
if (existsSync3(candidateLib))
|
|
6701
|
+
return candidateLib;
|
|
6702
|
+
const candidateSrc = resolve2(currentDir, "../../../data");
|
|
6703
|
+
if (existsSync3(candidateSrc))
|
|
6704
|
+
return candidateSrc;
|
|
6705
|
+
const candidateCwd = resolve2(process.cwd(), "data");
|
|
6706
|
+
if (existsSync3(candidateCwd))
|
|
6707
|
+
return candidateCwd;
|
|
6708
|
+
return candidateLib;
|
|
6709
|
+
}
|
|
6710
|
+
var TEMPLATES_DIR = join3(resolveDataDir(), "templates");
|
|
6701
6711
|
function ensureTemplatesDir() {
|
|
6702
6712
|
if (!existsSync3(TEMPLATES_DIR)) {
|
|
6703
|
-
|
|
6713
|
+
try {
|
|
6714
|
+
mkdirSync(TEMPLATES_DIR, { recursive: true });
|
|
6715
|
+
} catch {}
|
|
6704
6716
|
}
|
|
6705
6717
|
seedDefaultTemplates();
|
|
6706
6718
|
}
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
algorithm
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
textAlign: "center"
|
|
6720
|
-
},
|
|
6721
|
-
theme: {
|
|
6722
|
-
bgColor: "#09090b",
|
|
6723
|
-
textColor: "#ffffff",
|
|
6724
|
-
headingColor: "#ffffff",
|
|
6725
|
-
accentColor: "#10b981",
|
|
6726
|
-
quoteColor: "#71717a",
|
|
6727
|
-
captionColor: "#71717a",
|
|
6728
|
-
watermarkColor: "#3f3f46",
|
|
6729
|
-
cardBg: "#18181b"
|
|
6730
|
-
},
|
|
6731
|
-
typography: {
|
|
6732
|
-
headingFont: "PingFang SC",
|
|
6733
|
-
bodyFont: "Songti SC"
|
|
6734
|
-
},
|
|
6735
|
-
footerText: "",
|
|
6736
|
-
watermarkText: "",
|
|
6737
|
-
chrome: {
|
|
6738
|
-
showHeader: false,
|
|
6739
|
-
showWatermark: false,
|
|
6740
|
-
showPageNumber: false,
|
|
6741
|
-
showFooter: false,
|
|
6742
|
-
showFooterDivider: false,
|
|
6743
|
-
showFooterText: false,
|
|
6744
|
-
showFillRatio: false
|
|
6745
|
-
},
|
|
6746
|
-
images: [
|
|
6747
|
-
{
|
|
6748
|
-
id: "tpl_img_cover_bg",
|
|
6749
|
-
src: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=1200&auto=format&fit=crop&q=80",
|
|
6750
|
-
alt: "封面装饰图",
|
|
6751
|
-
isAbsolute: true,
|
|
6752
|
-
x: 1350,
|
|
6753
|
-
y: 120,
|
|
6754
|
-
width: 880,
|
|
6755
|
-
height: 760,
|
|
6756
|
-
zIndex: 5,
|
|
6757
|
-
rotate: 0,
|
|
6758
|
-
opacity: 0.95,
|
|
6759
|
-
borderRadius: 24,
|
|
6760
|
-
layoutPreset: "custom"
|
|
6761
|
-
}
|
|
6762
|
-
]
|
|
6719
|
+
var BUILTIN_PRESETS = [
|
|
6720
|
+
{
|
|
6721
|
+
...getDefaultConfig("wechat-cover-2.35"),
|
|
6722
|
+
id: "tpl_wechat_cover_minimal",
|
|
6723
|
+
name: "微信纯净大图封面 (2.35:1)",
|
|
6724
|
+
slot: "article_cover",
|
|
6725
|
+
algorithm: {
|
|
6726
|
+
...getDefaultConfig("wechat-cover-2.35").algorithm,
|
|
6727
|
+
baseFontSize: 58,
|
|
6728
|
+
scaleRatio: 1.4,
|
|
6729
|
+
verticalAlign: "center",
|
|
6730
|
+
textAlign: "center"
|
|
6763
6731
|
},
|
|
6764
|
-
{
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
verticalAlign: "center",
|
|
6774
|
-
textAlign: "left"
|
|
6775
|
-
},
|
|
6776
|
-
theme: {
|
|
6777
|
-
bgColor: "#ffffff",
|
|
6778
|
-
textColor: "#111827",
|
|
6779
|
-
headingColor: "#000000",
|
|
6780
|
-
accentColor: "#059669",
|
|
6781
|
-
quoteColor: "#4b5563",
|
|
6782
|
-
captionColor: "#6b7280",
|
|
6783
|
-
watermarkColor: "#9ca3af",
|
|
6784
|
-
cardBg: "#f3f4f6"
|
|
6785
|
-
},
|
|
6786
|
-
typography: {
|
|
6787
|
-
headingFont: "PingFang SC",
|
|
6788
|
-
bodyFont: "Songti SC"
|
|
6789
|
-
},
|
|
6790
|
-
footerText: "",
|
|
6791
|
-
watermarkText: "SPECIAL EDITION · 专栏特辑",
|
|
6792
|
-
chrome: {
|
|
6793
|
-
showHeader: true,
|
|
6794
|
-
showWatermark: true,
|
|
6795
|
-
showPageNumber: false,
|
|
6796
|
-
showFooter: false,
|
|
6797
|
-
showFooterDivider: false,
|
|
6798
|
-
showFooterText: false,
|
|
6799
|
-
showFillRatio: false
|
|
6800
|
-
},
|
|
6801
|
-
images: [
|
|
6802
|
-
{
|
|
6803
|
-
id: "tpl_img_editorial_card",
|
|
6804
|
-
src: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=1200&auto=format&fit=crop&q=80",
|
|
6805
|
-
alt: "杂志插图",
|
|
6806
|
-
isAbsolute: true,
|
|
6807
|
-
x: 1400,
|
|
6808
|
-
y: 140,
|
|
6809
|
-
width: 820,
|
|
6810
|
-
height: 720,
|
|
6811
|
-
zIndex: 15,
|
|
6812
|
-
rotate: -2,
|
|
6813
|
-
opacity: 1,
|
|
6814
|
-
borderRadius: 20,
|
|
6815
|
-
layoutPreset: "custom"
|
|
6816
|
-
}
|
|
6817
|
-
]
|
|
6732
|
+
theme: {
|
|
6733
|
+
bgColor: "#09090b",
|
|
6734
|
+
textColor: "#ffffff",
|
|
6735
|
+
headingColor: "#ffffff",
|
|
6736
|
+
accentColor: "#10b981",
|
|
6737
|
+
quoteColor: "#71717a",
|
|
6738
|
+
captionColor: "#71717a",
|
|
6739
|
+
watermarkColor: "#3f3f46",
|
|
6740
|
+
cardBg: "#18181b"
|
|
6818
6741
|
},
|
|
6819
|
-
{
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
name: "贴图封面海报 (3:4)",
|
|
6823
|
-
slot: "newspic_cover",
|
|
6824
|
-
algorithm: {
|
|
6825
|
-
...getDefaultConfig("longform-3-4").algorithm,
|
|
6826
|
-
baseFontSize: 56,
|
|
6827
|
-
scaleRatio: 1.35,
|
|
6828
|
-
verticalAlign: "center",
|
|
6829
|
-
textAlign: "center"
|
|
6830
|
-
},
|
|
6831
|
-
theme: {
|
|
6832
|
-
bgColor: "#121212",
|
|
6833
|
-
textColor: "#ffffff",
|
|
6834
|
-
headingColor: "#ffffff",
|
|
6835
|
-
accentColor: "#f59e0b",
|
|
6836
|
-
quoteColor: "#a3a3a3",
|
|
6837
|
-
captionColor: "#737373",
|
|
6838
|
-
watermarkColor: "#525252",
|
|
6839
|
-
cardBg: "#1c1c1c"
|
|
6840
|
-
},
|
|
6841
|
-
typography: {
|
|
6842
|
-
headingFont: "PingFang SC",
|
|
6843
|
-
bodyFont: "Songti SC"
|
|
6844
|
-
},
|
|
6845
|
-
footerText: "Typeset Studio · 贴图精选",
|
|
6846
|
-
watermarkText: "FEATURED NEWSPIC",
|
|
6847
|
-
chrome: {
|
|
6848
|
-
showHeader: true,
|
|
6849
|
-
showWatermark: true,
|
|
6850
|
-
showPageNumber: false,
|
|
6851
|
-
showFooter: true,
|
|
6852
|
-
showFooterDivider: false,
|
|
6853
|
-
showFooterText: true,
|
|
6854
|
-
showFillRatio: false
|
|
6855
|
-
},
|
|
6856
|
-
images: [
|
|
6857
|
-
{
|
|
6858
|
-
id: "tpl_img_poster_bg",
|
|
6859
|
-
src: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=1200&auto=format&fit=crop&q=80",
|
|
6860
|
-
alt: "海报主图",
|
|
6861
|
-
isAbsolute: true,
|
|
6862
|
-
x: 100,
|
|
6863
|
-
y: 780,
|
|
6864
|
-
width: 1000,
|
|
6865
|
-
height: 650,
|
|
6866
|
-
zIndex: 10,
|
|
6867
|
-
rotate: 0,
|
|
6868
|
-
opacity: 1,
|
|
6869
|
-
borderRadius: 20,
|
|
6870
|
-
layoutPreset: "custom"
|
|
6871
|
-
}
|
|
6872
|
-
]
|
|
6742
|
+
typography: {
|
|
6743
|
+
headingFont: "PingFang SC",
|
|
6744
|
+
bodyFont: "Songti SC"
|
|
6873
6745
|
},
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
quoteColor: "#44403c",
|
|
6885
|
-
captionColor: "#78716c",
|
|
6886
|
-
watermarkColor: "#a8a29e",
|
|
6887
|
-
cardBg: "#ffffff"
|
|
6888
|
-
},
|
|
6889
|
-
typography: {
|
|
6890
|
-
headingFont: "Songti SC",
|
|
6891
|
-
bodyFont: "Songti SC"
|
|
6892
|
-
},
|
|
6893
|
-
footerText: "Typeset Studio · 纯算法空间排版",
|
|
6894
|
-
watermarkText: "@ANTIGRAVITY",
|
|
6895
|
-
chrome: {
|
|
6896
|
-
showHeader: true,
|
|
6897
|
-
showWatermark: true,
|
|
6898
|
-
showPageNumber: true,
|
|
6899
|
-
showFooter: true,
|
|
6900
|
-
showFooterDivider: true,
|
|
6901
|
-
showFooterText: true,
|
|
6902
|
-
showFillRatio: false
|
|
6903
|
-
}
|
|
6746
|
+
footerText: "",
|
|
6747
|
+
watermarkText: "",
|
|
6748
|
+
chrome: {
|
|
6749
|
+
showHeader: false,
|
|
6750
|
+
showWatermark: false,
|
|
6751
|
+
showPageNumber: false,
|
|
6752
|
+
showFooter: false,
|
|
6753
|
+
showFooterDivider: false,
|
|
6754
|
+
showFooterText: false,
|
|
6755
|
+
showFillRatio: false
|
|
6904
6756
|
},
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
accentColor: "#f59e0b",
|
|
6921
|
-
quoteColor: "#d4d4d8",
|
|
6922
|
-
captionColor: "#a1a1aa",
|
|
6923
|
-
watermarkColor: "#71717a",
|
|
6924
|
-
cardBg: "#27272a"
|
|
6925
|
-
},
|
|
6926
|
-
typography: {
|
|
6927
|
-
headingFont: "PingFang SC",
|
|
6928
|
-
bodyFont: "Kaiti SC"
|
|
6929
|
-
},
|
|
6930
|
-
footerText: "",
|
|
6931
|
-
watermarkText: "",
|
|
6932
|
-
chrome: {
|
|
6933
|
-
showHeader: false,
|
|
6934
|
-
showWatermark: false,
|
|
6935
|
-
showPageNumber: false,
|
|
6936
|
-
showFooter: false,
|
|
6937
|
-
showFooterDivider: false,
|
|
6938
|
-
showFooterText: false,
|
|
6939
|
-
showFillRatio: false
|
|
6757
|
+
images: [
|
|
6758
|
+
{
|
|
6759
|
+
id: "tpl_img_cover_bg",
|
|
6760
|
+
src: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=1200&auto=format&fit=crop&q=80",
|
|
6761
|
+
alt: "封面装饰图",
|
|
6762
|
+
isAbsolute: true,
|
|
6763
|
+
x: 1350,
|
|
6764
|
+
y: 120,
|
|
6765
|
+
width: 880,
|
|
6766
|
+
height: 760,
|
|
6767
|
+
zIndex: 5,
|
|
6768
|
+
rotate: 0,
|
|
6769
|
+
opacity: 0.95,
|
|
6770
|
+
borderRadius: 24,
|
|
6771
|
+
layoutPreset: "custom"
|
|
6940
6772
|
}
|
|
6773
|
+
]
|
|
6774
|
+
},
|
|
6775
|
+
{
|
|
6776
|
+
...getDefaultConfig("wechat-cover-2.35"),
|
|
6777
|
+
id: "tpl_wechat_cover_editorial",
|
|
6778
|
+
name: "微信杂志标封面 (2.35:1)",
|
|
6779
|
+
slot: "article_cover",
|
|
6780
|
+
algorithm: {
|
|
6781
|
+
...getDefaultConfig("wechat-cover-2.35").algorithm,
|
|
6782
|
+
baseFontSize: 54,
|
|
6783
|
+
scaleRatio: 1.35,
|
|
6784
|
+
verticalAlign: "center",
|
|
6785
|
+
textAlign: "left"
|
|
6941
6786
|
},
|
|
6942
|
-
{
|
|
6943
|
-
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6787
|
+
theme: {
|
|
6788
|
+
bgColor: "#ffffff",
|
|
6789
|
+
textColor: "#111827",
|
|
6790
|
+
headingColor: "#000000",
|
|
6791
|
+
accentColor: "#059669",
|
|
6792
|
+
quoteColor: "#4b5563",
|
|
6793
|
+
captionColor: "#6b7280",
|
|
6794
|
+
watermarkColor: "#9ca3af",
|
|
6795
|
+
cardBg: "#f3f4f6"
|
|
6796
|
+
},
|
|
6797
|
+
typography: {
|
|
6798
|
+
headingFont: "PingFang SC",
|
|
6799
|
+
bodyFont: "Songti SC"
|
|
6800
|
+
},
|
|
6801
|
+
footerText: "",
|
|
6802
|
+
watermarkText: "SPECIAL EDITION · 专栏特辑",
|
|
6803
|
+
chrome: {
|
|
6804
|
+
showHeader: true,
|
|
6805
|
+
showWatermark: true,
|
|
6806
|
+
showPageNumber: false,
|
|
6807
|
+
showFooter: false,
|
|
6808
|
+
showFooterDivider: false,
|
|
6809
|
+
showFooterText: false,
|
|
6810
|
+
showFillRatio: false
|
|
6811
|
+
},
|
|
6812
|
+
images: [
|
|
6813
|
+
{
|
|
6814
|
+
id: "tpl_img_editorial_card",
|
|
6815
|
+
src: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=1200&auto=format&fit=crop&q=80",
|
|
6816
|
+
alt: "杂志插图",
|
|
6817
|
+
isAbsolute: true,
|
|
6818
|
+
x: 1400,
|
|
6819
|
+
y: 140,
|
|
6820
|
+
width: 820,
|
|
6821
|
+
height: 720,
|
|
6822
|
+
zIndex: 15,
|
|
6823
|
+
rotate: -2,
|
|
6824
|
+
opacity: 1,
|
|
6825
|
+
borderRadius: 20,
|
|
6826
|
+
layoutPreset: "custom"
|
|
6977
6827
|
}
|
|
6828
|
+
]
|
|
6829
|
+
},
|
|
6830
|
+
{
|
|
6831
|
+
...getDefaultConfig("longform-3-4"),
|
|
6832
|
+
id: "tpl_newspic_poster_cover",
|
|
6833
|
+
name: "贴图封面海报 (3:4)",
|
|
6834
|
+
slot: "newspic_cover",
|
|
6835
|
+
algorithm: {
|
|
6836
|
+
...getDefaultConfig("longform-3-4").algorithm,
|
|
6837
|
+
baseFontSize: 56,
|
|
6838
|
+
scaleRatio: 1.35,
|
|
6839
|
+
verticalAlign: "center",
|
|
6840
|
+
textAlign: "center"
|
|
6978
6841
|
},
|
|
6979
|
-
{
|
|
6980
|
-
|
|
6981
|
-
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
6842
|
+
theme: {
|
|
6843
|
+
bgColor: "#121212",
|
|
6844
|
+
textColor: "#ffffff",
|
|
6845
|
+
headingColor: "#ffffff",
|
|
6846
|
+
accentColor: "#f59e0b",
|
|
6847
|
+
quoteColor: "#a3a3a3",
|
|
6848
|
+
captionColor: "#737373",
|
|
6849
|
+
watermarkColor: "#525252",
|
|
6850
|
+
cardBg: "#1c1c1c"
|
|
6851
|
+
},
|
|
6852
|
+
typography: {
|
|
6853
|
+
headingFont: "PingFang SC",
|
|
6854
|
+
bodyFont: "Songti SC"
|
|
6855
|
+
},
|
|
6856
|
+
footerText: "Typeset Studio · 贴图精选",
|
|
6857
|
+
watermarkText: "FEATURED NEWSPIC",
|
|
6858
|
+
chrome: {
|
|
6859
|
+
showHeader: true,
|
|
6860
|
+
showWatermark: true,
|
|
6861
|
+
showPageNumber: false,
|
|
6862
|
+
showFooter: true,
|
|
6863
|
+
showFooterDivider: false,
|
|
6864
|
+
showFooterText: true,
|
|
6865
|
+
showFillRatio: false
|
|
6866
|
+
},
|
|
6867
|
+
images: [
|
|
6868
|
+
{
|
|
6869
|
+
id: "tpl_img_poster_bg",
|
|
6870
|
+
src: "https://images.unsplash.com/photo-1618005182384-a83a8bd57fbe?w=1200&auto=format&fit=crop&q=80",
|
|
6871
|
+
alt: "海报主图",
|
|
6872
|
+
isAbsolute: true,
|
|
6873
|
+
x: 100,
|
|
6874
|
+
y: 780,
|
|
6875
|
+
width: 1000,
|
|
6876
|
+
height: 650,
|
|
6877
|
+
zIndex: 10,
|
|
6878
|
+
rotate: 0,
|
|
6879
|
+
opacity: 1,
|
|
6880
|
+
borderRadius: 20,
|
|
6881
|
+
layoutPreset: "custom"
|
|
7015
6882
|
}
|
|
6883
|
+
]
|
|
6884
|
+
},
|
|
6885
|
+
{
|
|
6886
|
+
...getDefaultConfig("longform-3-4"),
|
|
6887
|
+
id: "tpl_longform_paper_sage",
|
|
6888
|
+
name: "纸白典雅长图文 (3:4)",
|
|
6889
|
+
slot: "newspic_content",
|
|
6890
|
+
theme: {
|
|
6891
|
+
bgColor: "#fbfcfb",
|
|
6892
|
+
textColor: "#1c1917",
|
|
6893
|
+
headingColor: "#0c0a09",
|
|
6894
|
+
accentColor: "#047857",
|
|
6895
|
+
quoteColor: "#44403c",
|
|
6896
|
+
captionColor: "#78716c",
|
|
6897
|
+
watermarkColor: "#a8a29e",
|
|
6898
|
+
cardBg: "#ffffff"
|
|
6899
|
+
},
|
|
6900
|
+
typography: {
|
|
6901
|
+
headingFont: "Songti SC",
|
|
6902
|
+
bodyFont: "Songti SC"
|
|
6903
|
+
},
|
|
6904
|
+
footerText: "Typeset Studio · 纯算法空间排版",
|
|
6905
|
+
watermarkText: "@ANTIGRAVITY",
|
|
6906
|
+
chrome: {
|
|
6907
|
+
showHeader: true,
|
|
6908
|
+
showWatermark: true,
|
|
6909
|
+
showPageNumber: true,
|
|
6910
|
+
showFooter: true,
|
|
6911
|
+
showFooterDivider: true,
|
|
6912
|
+
showFooterText: true,
|
|
6913
|
+
showFillRatio: false
|
|
7016
6914
|
}
|
|
7017
|
-
|
|
7018
|
-
|
|
6915
|
+
},
|
|
6916
|
+
{
|
|
6917
|
+
...getDefaultConfig("square-1-1"),
|
|
6918
|
+
id: "tpl_square_quote_card",
|
|
6919
|
+
name: "金句留白方卡 (1:1)",
|
|
6920
|
+
algorithm: {
|
|
6921
|
+
...getDefaultConfig("square-1-1").algorithm,
|
|
6922
|
+
baseFontSize: 50,
|
|
6923
|
+
scaleRatio: 1.3,
|
|
6924
|
+
verticalAlign: "center",
|
|
6925
|
+
textAlign: "center"
|
|
6926
|
+
},
|
|
6927
|
+
theme: {
|
|
6928
|
+
bgColor: "#18181b",
|
|
6929
|
+
textColor: "#fafafa",
|
|
6930
|
+
headingColor: "#ffffff",
|
|
6931
|
+
accentColor: "#f59e0b",
|
|
6932
|
+
quoteColor: "#d4d4d8",
|
|
6933
|
+
captionColor: "#a1a1aa",
|
|
6934
|
+
watermarkColor: "#71717a",
|
|
6935
|
+
cardBg: "#27272a"
|
|
6936
|
+
},
|
|
6937
|
+
typography: {
|
|
6938
|
+
headingFont: "PingFang SC",
|
|
6939
|
+
bodyFont: "Kaiti SC"
|
|
6940
|
+
},
|
|
6941
|
+
footerText: "",
|
|
6942
|
+
watermarkText: "",
|
|
6943
|
+
chrome: {
|
|
6944
|
+
showHeader: false,
|
|
6945
|
+
showWatermark: false,
|
|
6946
|
+
showPageNumber: false,
|
|
6947
|
+
showFooter: false,
|
|
6948
|
+
showFooterDivider: false,
|
|
6949
|
+
showFooterText: false,
|
|
6950
|
+
showFillRatio: false
|
|
6951
|
+
}
|
|
6952
|
+
},
|
|
6953
|
+
{
|
|
6954
|
+
...getDefaultConfig("wide-16-9"),
|
|
6955
|
+
id: "tpl_wide_tech_banner",
|
|
6956
|
+
name: "极简技术宽屏 (16:9)",
|
|
6957
|
+
algorithm: {
|
|
6958
|
+
...getDefaultConfig("wide-16-9").algorithm,
|
|
6959
|
+
baseFontSize: 46,
|
|
6960
|
+
scaleRatio: 1.3,
|
|
6961
|
+
verticalAlign: "center",
|
|
6962
|
+
textAlign: "left"
|
|
6963
|
+
},
|
|
6964
|
+
theme: {
|
|
6965
|
+
bgColor: "#09090b",
|
|
6966
|
+
textColor: "#f4f4f5",
|
|
6967
|
+
headingColor: "#ffffff",
|
|
6968
|
+
accentColor: "#3b82f6",
|
|
6969
|
+
quoteColor: "#a1a1aa",
|
|
6970
|
+
captionColor: "#71717a",
|
|
6971
|
+
watermarkColor: "#52525b",
|
|
6972
|
+
cardBg: "#18181b"
|
|
6973
|
+
},
|
|
6974
|
+
typography: {
|
|
6975
|
+
headingFont: "PingFang SC",
|
|
6976
|
+
bodyFont: "SourceHanSansSC, sans-serif"
|
|
6977
|
+
},
|
|
6978
|
+
footerText: "TECH ENGINEERING INSIGHTS",
|
|
6979
|
+
watermarkText: "DEV // ARCHITECTURE",
|
|
6980
|
+
chrome: {
|
|
6981
|
+
showHeader: true,
|
|
6982
|
+
showWatermark: true,
|
|
6983
|
+
showPageNumber: false,
|
|
6984
|
+
showFooter: true,
|
|
6985
|
+
showFooterDivider: false,
|
|
6986
|
+
showFooterText: true,
|
|
6987
|
+
showFillRatio: false
|
|
6988
|
+
}
|
|
6989
|
+
},
|
|
6990
|
+
{
|
|
6991
|
+
...getDefaultConfig("longform-3-4"),
|
|
6992
|
+
id: "tpl_longform_vintage_news",
|
|
6993
|
+
name: "复古报刊多图 (3:4)",
|
|
6994
|
+
slot: "newspic_content",
|
|
6995
|
+
algorithm: {
|
|
6996
|
+
...getDefaultConfig("longform-3-4").algorithm,
|
|
6997
|
+
baseFontSize: 38,
|
|
6998
|
+
scaleRatio: 1.25,
|
|
6999
|
+
verticalAlign: "top",
|
|
7000
|
+
textAlign: "left"
|
|
7001
|
+
},
|
|
7002
|
+
theme: {
|
|
7003
|
+
bgColor: "#f7f4ee",
|
|
7004
|
+
textColor: "#262320",
|
|
7005
|
+
headingColor: "#141210",
|
|
7006
|
+
accentColor: "#991b1b",
|
|
7007
|
+
quoteColor: "#57534e",
|
|
7008
|
+
captionColor: "#78716c",
|
|
7009
|
+
watermarkColor: "#a8a29e",
|
|
7010
|
+
cardBg: "#eee9df"
|
|
7011
|
+
},
|
|
7012
|
+
typography: {
|
|
7013
|
+
headingFont: "SourceHanSerifSC, serif",
|
|
7014
|
+
bodyFont: "SourceHanSerifSC, serif"
|
|
7015
|
+
},
|
|
7016
|
+
footerText: "WEEKLY REPORT · 观察与沉淀",
|
|
7017
|
+
watermarkText: "VOL. 42 // THE CHRONICLE",
|
|
7018
|
+
chrome: {
|
|
7019
|
+
showHeader: true,
|
|
7020
|
+
showWatermark: true,
|
|
7021
|
+
showPageNumber: true,
|
|
7022
|
+
showFooter: true,
|
|
7023
|
+
showFooterDivider: true,
|
|
7024
|
+
showFooterText: true,
|
|
7025
|
+
showFillRatio: false
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
];
|
|
7029
|
+
function seedDefaultTemplates() {
|
|
7030
|
+
if (!existsSync3(TEMPLATES_DIR))
|
|
7031
|
+
return;
|
|
7032
|
+
for (const preset of BUILTIN_PRESETS) {
|
|
7019
7033
|
const filePath = join3(TEMPLATES_DIR, `${preset.id}.json`);
|
|
7020
|
-
|
|
7034
|
+
if (!existsSync3(filePath)) {
|
|
7035
|
+
try {
|
|
7036
|
+
writeFileSync(filePath, JSON.stringify(preset, null, 2), "utf-8");
|
|
7037
|
+
} catch {}
|
|
7038
|
+
}
|
|
7021
7039
|
}
|
|
7022
7040
|
}
|
|
7023
|
-
var DEFAULT_SETTINGS_PATH = join3(
|
|
7041
|
+
var DEFAULT_SETTINGS_PATH = join3(resolveDataDir(), "default-template.json");
|
|
7024
7042
|
var BUILTIN_SLOT_DEFAULTS = {
|
|
7025
7043
|
article_cover: "tpl_wechat_cover_minimal",
|
|
7026
7044
|
newspic_cover: "tpl_newspic_poster_cover",
|
|
@@ -7090,7 +7108,15 @@ function setDefaultTemplateId(id) {
|
|
|
7090
7108
|
writeFileSync(DEFAULT_SETTINGS_PATH, JSON.stringify(current, null, 2), "utf-8");
|
|
7091
7109
|
}
|
|
7092
7110
|
function findTemplateByKeyOrName(key) {
|
|
7093
|
-
|
|
7111
|
+
const lowerKey = key.trim().toLowerCase();
|
|
7112
|
+
const builtin = BUILTIN_PRESETS.find((p) => p.id?.toLowerCase() === lowerKey || p.name.toLowerCase() === lowerKey);
|
|
7113
|
+
if (builtin)
|
|
7114
|
+
return builtin;
|
|
7115
|
+
const builtinPartial = BUILTIN_PRESETS.find((p) => p.name.toLowerCase().includes(lowerKey));
|
|
7116
|
+
if (builtinPartial)
|
|
7117
|
+
return builtinPartial;
|
|
7118
|
+
if (!existsSync3(TEMPLATES_DIR))
|
|
7119
|
+
return null;
|
|
7094
7120
|
const directPath = join3(TEMPLATES_DIR, `${key}.json`);
|
|
7095
7121
|
if (existsSync3(directPath)) {
|
|
7096
7122
|
try {
|
|
@@ -7098,7 +7124,7 @@ function findTemplateByKeyOrName(key) {
|
|
|
7098
7124
|
} catch {}
|
|
7099
7125
|
}
|
|
7100
7126
|
const files = readdirSync2(TEMPLATES_DIR).filter((f) => f.endsWith(".json"));
|
|
7101
|
-
const
|
|
7127
|
+
const templates = [];
|
|
7102
7128
|
for (const file of files) {
|
|
7103
7129
|
try {
|
|
7104
7130
|
const content = readFileSync2(join3(TEMPLATES_DIR, file), "utf-8");
|
|
@@ -7106,18 +7132,10 @@ function findTemplateByKeyOrName(key) {
|
|
|
7106
7132
|
if (tpl.id?.toLowerCase() === lowerKey || tpl.name.toLowerCase() === lowerKey) {
|
|
7107
7133
|
return tpl;
|
|
7108
7134
|
}
|
|
7135
|
+
templates.push(tpl);
|
|
7109
7136
|
} catch {}
|
|
7110
7137
|
}
|
|
7111
|
-
|
|
7112
|
-
try {
|
|
7113
|
-
const content = readFileSync2(join3(TEMPLATES_DIR, file), "utf-8");
|
|
7114
|
-
const tpl = JSON.parse(content);
|
|
7115
|
-
if (tpl.name.toLowerCase().includes(lowerKey)) {
|
|
7116
|
-
return tpl;
|
|
7117
|
-
}
|
|
7118
|
-
} catch {}
|
|
7119
|
-
}
|
|
7120
|
-
return null;
|
|
7138
|
+
return templates.find((tpl) => tpl.name.toLowerCase().includes(lowerKey)) ?? null;
|
|
7121
7139
|
}
|
|
7122
7140
|
var templatesRouter = new Hono3;
|
|
7123
7141
|
templatesRouter.get("/", (c) => {
|
|
@@ -7409,7 +7427,7 @@ function mapBodyImages(bodyImages, pageSpecs) {
|
|
|
7409
7427
|
});
|
|
7410
7428
|
}
|
|
7411
7429
|
var REMOTE_STUDIO_URL = process.env.TYPESET_STUDIO_URL?.replace(/\/$/, "");
|
|
7412
|
-
async function renderRemoteBatch(studioUrl, body, outputDir,
|
|
7430
|
+
async function renderRemoteBatch(studioUrl, body, outputDir, route, kind) {
|
|
7413
7431
|
const res = await fetch(`${studioUrl}/api/render/export`, {
|
|
7414
7432
|
method: "POST",
|
|
7415
7433
|
headers: { "Content-Type": "application/json" },
|
|
@@ -7421,12 +7439,11 @@ async function renderRemoteBatch(studioUrl, body, outputDir, filenamePrefix, rou
|
|
|
7421
7439
|
const data = await res.json();
|
|
7422
7440
|
const assets = [];
|
|
7423
7441
|
const pagesSummary = [];
|
|
7424
|
-
for (
|
|
7425
|
-
const pageItem = data.pages[i];
|
|
7442
|
+
for (const pageItem of data.pages) {
|
|
7426
7443
|
const pageNum = pageItem.pageNumber;
|
|
7427
7444
|
const base64Data = pageItem.dataUrl.replace(/^data:image\/png;base64,/, "");
|
|
7428
7445
|
const buffer = Buffer.from(base64Data, "base64");
|
|
7429
|
-
const filename = kind === "cover" ? "cover.png" :
|
|
7446
|
+
const filename = kind === "cover" ? "cover.png" : `article-${String(pageNum).padStart(2, "0")}.png`;
|
|
7430
7447
|
const filePath = join4(outputDir, filename);
|
|
7431
7448
|
await writeFile(filePath, buffer);
|
|
7432
7449
|
assets.push({
|
|
@@ -7443,9 +7460,33 @@ async function renderRemoteBatch(studioUrl, body, outputDir, filenamePrefix, rou
|
|
|
7443
7460
|
}
|
|
7444
7461
|
return { assets, pageCount: data.pageCount, pages: pagesSummary };
|
|
7445
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
|
+
}
|
|
7446
7487
|
var typesetStudioAdapter = {
|
|
7447
7488
|
name: "typeset-studio",
|
|
7448
|
-
version: "0.2.
|
|
7489
|
+
version: "0.2.2",
|
|
7449
7490
|
async doctor() {
|
|
7450
7491
|
const checks = [];
|
|
7451
7492
|
if (REMOTE_STUDIO_URL) {
|
|
@@ -7479,7 +7520,7 @@ var typesetStudioAdapter = {
|
|
|
7479
7520
|
} catch (err) {
|
|
7480
7521
|
checks.push({
|
|
7481
7522
|
name: "@napi-rs/canvas",
|
|
7482
|
-
ok:
|
|
7523
|
+
ok: Boolean(REMOTE_STUDIO_URL),
|
|
7483
7524
|
message: REMOTE_STUDIO_URL ? "@napi-rs/canvas not installed locally (using Cloud Typeset Studio microservice)" : `@napi-rs/canvas initialization failed: ${err}`
|
|
7484
7525
|
});
|
|
7485
7526
|
}
|
|
@@ -7500,201 +7541,41 @@ var typesetStudioAdapter = {
|
|
|
7500
7541
|
return checks;
|
|
7501
7542
|
},
|
|
7502
7543
|
async render(input) {
|
|
7503
|
-
const { route, outputDir,
|
|
7544
|
+
const { route, outputDir, bodyText, state } = input;
|
|
7504
7545
|
await mkdir(outputDir, { recursive: true });
|
|
7546
|
+
if (route === "blog")
|
|
7547
|
+
return { assets: [], pageCount: 0, pages: [] };
|
|
7505
7548
|
const account = state.route.account || "default";
|
|
7506
|
-
const
|
|
7507
|
-
const
|
|
7508
|
-
const watermarkText = account ? `@${account}` : undefined;
|
|
7509
|
-
const effectiveTitle = cleanTitle(title || state.metadata?.title || "");
|
|
7510
|
-
if (REMOTE_STUDIO_URL) {
|
|
7511
|
-
if (route === "wechat-article") {
|
|
7512
|
-
const coverImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7513
|
-
return await renderRemoteBatch(REMOTE_STUDIO_URL, {
|
|
7514
|
-
slot: "article_cover",
|
|
7515
|
-
route: "wechat-article",
|
|
7516
|
-
account,
|
|
7517
|
-
title: effectiveTitle,
|
|
7518
|
-
markdown: `# ${effectiveTitle}`,
|
|
7519
|
-
template: input.template,
|
|
7520
|
-
images: coverImages2,
|
|
7521
|
-
highlightWords,
|
|
7522
|
-
config: { footerText, watermarkText }
|
|
7523
|
-
}, outputDir, "cover", "wechat-article", "cover");
|
|
7524
|
-
}
|
|
7525
|
-
if (route === "wechat-newspic") {
|
|
7526
|
-
const newspicSpec2 = state.intent?.newspic_render;
|
|
7527
|
-
const paginationMode2 = input.template === "longform-3-4" ? "multi" : input.template === "poster-3-4" ? "single" : newspicSpec2?.pagination_mode || "multi";
|
|
7528
|
-
const isMultiPage2 = paginationMode2 === "multi";
|
|
7529
|
-
const coverImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7530
|
-
const coverRes = await renderRemoteBatch(REMOTE_STUDIO_URL, {
|
|
7531
|
-
slot: "newspic_cover",
|
|
7532
|
-
route: "wechat-newspic",
|
|
7533
|
-
kind: "cover",
|
|
7534
|
-
account,
|
|
7535
|
-
title: effectiveTitle,
|
|
7536
|
-
markdown: `# ${effectiveTitle}`,
|
|
7537
|
-
template: input.template,
|
|
7538
|
-
images: coverImages2,
|
|
7539
|
-
highlightWords,
|
|
7540
|
-
config: { footerText, watermarkText }
|
|
7541
|
-
}, outputDir, "cover", "wechat-newspic", "cover");
|
|
7542
|
-
if (!isMultiPage2) {
|
|
7543
|
-
return coverRes;
|
|
7544
|
-
}
|
|
7545
|
-
const contentImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7546
|
-
const contentRes = await renderRemoteBatch(REMOTE_STUDIO_URL, {
|
|
7547
|
-
slot: "newspic_content",
|
|
7548
|
-
route: "wechat-newspic",
|
|
7549
|
-
kind: "content",
|
|
7550
|
-
account,
|
|
7551
|
-
title: effectiveTitle,
|
|
7552
|
-
markdown: bodyText || `# ${effectiveTitle}`,
|
|
7553
|
-
template: input.template,
|
|
7554
|
-
images: contentImages2,
|
|
7555
|
-
highlightWords,
|
|
7556
|
-
config: { footerText, watermarkText }
|
|
7557
|
-
}, outputDir, "article", "wechat-newspic", "page");
|
|
7558
|
-
return {
|
|
7559
|
-
assets: [...coverRes.assets, ...contentRes.assets],
|
|
7560
|
-
pageCount: contentRes.pageCount,
|
|
7561
|
-
pages: contentRes.pages
|
|
7562
|
-
};
|
|
7563
|
-
}
|
|
7564
|
-
}
|
|
7565
|
-
if (route === "wechat-article") {
|
|
7566
|
-
const coverImages2 = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7567
|
-
const resolved = resolveTypesetResult({
|
|
7568
|
-
slot: "article_cover",
|
|
7569
|
-
route: "wechat-article",
|
|
7570
|
-
account,
|
|
7571
|
-
title: effectiveTitle,
|
|
7572
|
-
markdown: `# ${effectiveTitle}`,
|
|
7573
|
-
template: input.template,
|
|
7574
|
-
images: coverImages2,
|
|
7575
|
-
highlightWords,
|
|
7576
|
-
config: {
|
|
7577
|
-
footerText: footerText ?? undefined,
|
|
7578
|
-
watermarkText: watermarkText ?? undefined
|
|
7579
|
-
}
|
|
7580
|
-
});
|
|
7581
|
-
const pngOutputs = await renderTypesetResultToPngs(resolved.result);
|
|
7582
|
-
const coverBuffer2 = pngOutputs[0]?.buffer;
|
|
7583
|
-
if (!coverBuffer2) {
|
|
7584
|
-
throw new Error("Typeset Studio failed to render cover buffer");
|
|
7585
|
-
}
|
|
7586
|
-
const coverPath2 = join4(outputDir, "cover.png");
|
|
7587
|
-
await writeFile(coverPath2, coverBuffer2);
|
|
7588
|
-
const asset = {
|
|
7589
|
-
kind: "cover",
|
|
7590
|
-
route: "wechat-article",
|
|
7591
|
-
path: coverPath2
|
|
7592
|
-
};
|
|
7593
|
-
return {
|
|
7594
|
-
assets: [asset],
|
|
7595
|
-
pageCount: 1,
|
|
7596
|
-
pages: [
|
|
7597
|
-
{
|
|
7598
|
-
page: 1,
|
|
7599
|
-
imageCount: resolved.result.pages[0]?.images.length || 0,
|
|
7600
|
-
imageSources: (resolved.result.pages[0]?.images || []).map((img) => img.src)
|
|
7601
|
-
}
|
|
7602
|
-
]
|
|
7603
|
-
};
|
|
7604
|
-
}
|
|
7605
|
-
if (route === "blog") {
|
|
7606
|
-
return {
|
|
7607
|
-
assets: [],
|
|
7608
|
-
pageCount: 0,
|
|
7609
|
-
pages: []
|
|
7610
|
-
};
|
|
7611
|
-
}
|
|
7612
|
-
const newspicSpec = state.intent?.newspic_render;
|
|
7613
|
-
const paginationMode = input.template === "longform-3-4" ? "multi" : input.template === "poster-3-4" ? "single" : newspicSpec?.pagination_mode || "multi";
|
|
7614
|
-
const isMultiPage = paginationMode === "multi";
|
|
7615
|
-
const assets = [];
|
|
7616
|
-
const pagesSummary = [];
|
|
7617
|
-
const coverImages = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7618
|
-
const resolvedCover = resolveTypesetResult({
|
|
7619
|
-
slot: "newspic_cover",
|
|
7620
|
-
route: "wechat-newspic",
|
|
7621
|
-
kind: "cover",
|
|
7549
|
+
const title = cleanTitle(input.title || state.metadata?.title || "");
|
|
7550
|
+
const common = {
|
|
7622
7551
|
account,
|
|
7623
|
-
title
|
|
7624
|
-
markdown: `# ${effectiveTitle}`,
|
|
7552
|
+
title,
|
|
7625
7553
|
template: input.template,
|
|
7626
|
-
images:
|
|
7627
|
-
highlightWords,
|
|
7628
|
-
config: {
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
const
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7644
|
-
|
|
7645
|
-
if (!isMultiPage) {
|
|
7646
|
-
return {
|
|
7647
|
-
assets,
|
|
7648
|
-
pageCount: 1,
|
|
7649
|
-
pages: [
|
|
7650
|
-
{
|
|
7651
|
-
page: 1,
|
|
7652
|
-
imageCount: resolvedCover.result.pages[0]?.images.length || 0,
|
|
7653
|
-
imageSources: (resolvedCover.result.pages[0]?.images || []).map((img) => img.src)
|
|
7654
|
-
}
|
|
7655
|
-
]
|
|
7656
|
-
};
|
|
7657
|
-
}
|
|
7658
|
-
const contentImages = mapBodyImages(input.bodyImages, input.pageSpecs);
|
|
7659
|
-
const resolvedContent = resolveTypesetResult({
|
|
7660
|
-
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,
|
|
7661
7573
|
route: "wechat-newspic",
|
|
7662
7574
|
kind: "content",
|
|
7663
|
-
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
images: contentImages,
|
|
7668
|
-
highlightWords,
|
|
7669
|
-
config: {
|
|
7670
|
-
footerText: footerText ?? undefined,
|
|
7671
|
-
watermarkText: watermarkText ?? undefined
|
|
7672
|
-
}
|
|
7673
|
-
});
|
|
7674
|
-
const contentPngs = await renderTypesetResultToPngs(resolvedContent.result);
|
|
7675
|
-
for (let i = 0;i < contentPngs.length; i++) {
|
|
7676
|
-
const pageNum = i + 1;
|
|
7677
|
-
const fileName = `article-${String(pageNum).padStart(2, "0")}.png`;
|
|
7678
|
-
const pagePath = join4(outputDir, fileName);
|
|
7679
|
-
await writeFile(pagePath, contentPngs[i].buffer);
|
|
7680
|
-
assets.push({
|
|
7681
|
-
kind: "page",
|
|
7682
|
-
route: "wechat-newspic",
|
|
7683
|
-
index: pageNum,
|
|
7684
|
-
path: pagePath
|
|
7685
|
-
});
|
|
7686
|
-
const flowPage = resolvedContent.result.pages[i];
|
|
7687
|
-
pagesSummary.push({
|
|
7688
|
-
page: pageNum,
|
|
7689
|
-
imageCount: flowPage?.images.length || 0,
|
|
7690
|
-
imageSources: (flowPage?.images || []).map((img) => img.src)
|
|
7691
|
-
});
|
|
7692
|
-
}
|
|
7693
|
-
return {
|
|
7694
|
-
assets,
|
|
7695
|
-
pageCount: contentPngs.length,
|
|
7696
|
-
pages: pagesSummary
|
|
7697
|
-
};
|
|
7575
|
+
slot: "newspic_content",
|
|
7576
|
+
markdown: bodyText || `# ${title}`
|
|
7577
|
+
}, "page");
|
|
7578
|
+
return { ...content, assets: [...cover.assets, ...content.assets] };
|
|
7698
7579
|
}
|
|
7699
7580
|
};
|
|
7700
7581
|
var plugin = typesetStudioAdapter;
|