mdsvr 2.2.13 → 2.2.16
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 +3 -3
- package/dist/generators/feed.d.ts.map +1 -1
- package/dist/generators/feed.js +4 -2
- package/dist/generators/feed.js.map +1 -1
- package/dist/generators/search-index.d.ts.map +1 -1
- package/dist/generators/search-index.js +10 -1
- package/dist/generators/search-index.js.map +1 -1
- package/dist/generators/sitemap.d.ts.map +1 -1
- package/dist/generators/sitemap.js +3 -1
- package/dist/generators/sitemap.js.map +1 -1
- package/dist/generators/static-export.d.ts.map +1 -1
- package/dist/generators/static-export.js +182 -21
- package/dist/generators/static-export.js.map +1 -1
- package/dist/og/generator.d.ts +25 -0
- package/dist/og/generator.d.ts.map +1 -0
- package/dist/og/generator.js +234 -0
- package/dist/og/generator.js.map +1 -0
- package/dist/og/index.d.ts +4 -0
- package/dist/og/index.d.ts.map +1 -0
- package/dist/og/index.js +4 -0
- package/dist/og/index.js.map +1 -0
- package/dist/og/template.d.ts +12 -0
- package/dist/og/template.d.ts.map +1 -0
- package/dist/og/template.js +183 -0
- package/dist/og/template.js.map +1 -0
- package/dist/og/types.d.ts +54 -0
- package/dist/og/types.d.ts.map +1 -0
- package/dist/og/types.js +3 -0
- package/dist/og/types.js.map +1 -0
- package/dist/renderer/markdown.d.ts +16 -0
- package/dist/renderer/markdown.d.ts.map +1 -1
- package/dist/renderer/markdown.js +56 -0
- package/dist/renderer/markdown.js.map +1 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +10 -3
- package/dist/router.js.map +1 -1
- package/dist/settings/defaults.d.ts +1 -0
- package/dist/settings/defaults.d.ts.map +1 -1
- package/dist/settings/defaults.js +1 -0
- package/dist/settings/defaults.js.map +1 -1
- package/dist/settings/index.d.ts +1 -1
- package/dist/settings/index.d.ts.map +1 -1
- package/dist/settings/index.js +1 -1
- package/dist/settings/index.js.map +1 -1
- package/dist/settings/schema.d.ts +260 -0
- package/dist/settings/schema.d.ts.map +1 -1
- package/dist/settings/schema.js +26 -1
- package/dist/settings/schema.js.map +1 -1
- package/dist/template/index.d.ts +1 -0
- package/dist/template/index.d.ts.map +1 -1
- package/dist/template/index.js +50 -11
- package/dist/template/index.js.map +1 -1
- package/dist/template/search.d.ts +1 -1
- package/dist/template/search.d.ts.map +1 -1
- package/dist/template/search.js +5 -2
- package/dist/template/search.js.map +1 -1
- package/dist/template/seo.d.ts.map +1 -1
- package/dist/template/seo.js +4 -2
- package/dist/template/seo.js.map +1 -1
- package/dist/template/sidebar.d.ts +2 -1
- package/dist/template/sidebar.d.ts.map +1 -1
- package/dist/template/sidebar.js +71 -14
- package/dist/template/sidebar.js.map +1 -1
- package/package.json +15 -8
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
// OG Image generator using Satori and Resvg
|
|
2
|
+
import satori from "satori";
|
|
3
|
+
import { Resvg } from "@resvg/resvg-js";
|
|
4
|
+
import sharp from "sharp";
|
|
5
|
+
import { promises as fs } from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import https from "node:https";
|
|
8
|
+
import { generateOgTemplate, OG_WIDTH, OG_HEIGHT } from "./template.js";
|
|
9
|
+
// Font CDN URLs - using jsDelivr for reliable font delivery
|
|
10
|
+
const FONT_URLS = {
|
|
11
|
+
Inter: "https://cdn.jsdelivr.net/npm/@fontsource/inter@5.0.8/files/inter-latin-400-normal.woff",
|
|
12
|
+
"Inter-400": "https://cdn.jsdelivr.net/npm/@fontsource/inter@5.0.8/files/inter-latin-400-normal.woff",
|
|
13
|
+
"Inter-600": "https://cdn.jsdelivr.net/npm/@fontsource/inter@5.0.8/files/inter-latin-600-normal.woff",
|
|
14
|
+
"Inter-700": "https://cdn.jsdelivr.net/npm/@fontsource/inter@5.0.8/files/inter-latin-700-normal.woff",
|
|
15
|
+
"Inter-800": "https://cdn.jsdelivr.net/npm/@fontsource/inter@5.0.8/files/inter-latin-800-normal.woff",
|
|
16
|
+
};
|
|
17
|
+
// Font data cache
|
|
18
|
+
const fontCache = new Map();
|
|
19
|
+
/**
|
|
20
|
+
* Download font from URL
|
|
21
|
+
*/
|
|
22
|
+
function downloadFont(url) {
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
https
|
|
25
|
+
.get(url, { timeout: 30000 }, (res) => {
|
|
26
|
+
if (res.statusCode !== 200) {
|
|
27
|
+
reject(new Error(`Failed to download font: ${res.statusCode}`));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const chunks = [];
|
|
31
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
32
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
33
|
+
res.on("error", reject);
|
|
34
|
+
})
|
|
35
|
+
.on("error", reject);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Load font data (with caching and CDN fallback)
|
|
40
|
+
*/
|
|
41
|
+
async function loadFont(fontFamily, weight = 400) {
|
|
42
|
+
const cacheKey = `${fontFamily}-${weight}`;
|
|
43
|
+
// Check cache first
|
|
44
|
+
if (fontCache.has(cacheKey)) {
|
|
45
|
+
return fontCache.get(cacheKey);
|
|
46
|
+
}
|
|
47
|
+
try {
|
|
48
|
+
// Try to load from CDN for Inter
|
|
49
|
+
if (fontFamily.toLowerCase() === "inter") {
|
|
50
|
+
const fontUrl = FONT_URLS[`Inter-${weight}`] || FONT_URLS["Inter"];
|
|
51
|
+
if (fontUrl) {
|
|
52
|
+
try {
|
|
53
|
+
const data = await downloadFont(fontUrl);
|
|
54
|
+
fontCache.set(cacheKey, data);
|
|
55
|
+
return data;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Fallback to next method
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Try to load from local paths
|
|
63
|
+
const possiblePaths = [
|
|
64
|
+
path.join(process.cwd(), "assets", "fonts", `${fontFamily}-${weight}.woff2`),
|
|
65
|
+
path.join(process.cwd(), "assets", "fonts", `${fontFamily}-${weight}.woff`),
|
|
66
|
+
path.join(process.cwd(), "assets", "fonts", `${fontFamily}.ttf`),
|
|
67
|
+
path.join(process.cwd(), "src", "assets", "fonts", `${fontFamily}-${weight}.woff2`),
|
|
68
|
+
path.join(process.cwd(), "src", "assets", "fonts", `${fontFamily}.ttf`),
|
|
69
|
+
];
|
|
70
|
+
for (const fontPath of possiblePaths) {
|
|
71
|
+
try {
|
|
72
|
+
const data = await fs.readFile(fontPath);
|
|
73
|
+
fontCache.set(cacheKey, data);
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Continue to next path
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Generate OG image using Satori and Resvg
|
|
88
|
+
*/
|
|
89
|
+
export async function generateOgImage(data, options) {
|
|
90
|
+
const { outputPath, format = "jpg", quality = 85, width = OG_WIDTH, height = OG_HEIGHT, } = options;
|
|
91
|
+
try {
|
|
92
|
+
// Ensure output directory exists
|
|
93
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
94
|
+
// Generate template
|
|
95
|
+
const template = generateOgTemplate(data);
|
|
96
|
+
// Load fonts - we need specific weights for the template
|
|
97
|
+
const fontFamily = data.fontFamily || "Inter";
|
|
98
|
+
const fonts = [];
|
|
99
|
+
// Load different font weights needed for the template
|
|
100
|
+
const weights = [400, 600, 700, 800];
|
|
101
|
+
for (const weight of weights) {
|
|
102
|
+
const fontData = await loadFont(fontFamily, weight);
|
|
103
|
+
if (fontData) {
|
|
104
|
+
fonts.push({
|
|
105
|
+
name: fontFamily,
|
|
106
|
+
data: fontData,
|
|
107
|
+
weight,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// If no fonts were loaded, we can't generate the image
|
|
112
|
+
if (fonts.length === 0) {
|
|
113
|
+
throw new Error(`No fonts are loaded for ${fontFamily}. Unable to generate OG image.`);
|
|
114
|
+
}
|
|
115
|
+
// Generate SVG using Satori
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
117
|
+
const satoriOptions = {
|
|
118
|
+
width,
|
|
119
|
+
height,
|
|
120
|
+
};
|
|
121
|
+
if (fonts.length > 0) {
|
|
122
|
+
satoriOptions.fonts = fonts;
|
|
123
|
+
}
|
|
124
|
+
const svg = await satori(template, satoriOptions);
|
|
125
|
+
// Convert SVG to raster image using Resvg
|
|
126
|
+
const resvg = new Resvg(svg, {
|
|
127
|
+
fitTo: {
|
|
128
|
+
mode: "width",
|
|
129
|
+
value: width,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
const pngData = resvg.render();
|
|
133
|
+
const pngBuffer = pngData.asPng();
|
|
134
|
+
// Convert to JPG if needed using sharp
|
|
135
|
+
let finalBuffer;
|
|
136
|
+
const outputExt = path.extname(outputPath).toLowerCase();
|
|
137
|
+
if (outputExt === ".jpg" || outputExt === ".jpeg") {
|
|
138
|
+
finalBuffer = await sharp(pngBuffer)
|
|
139
|
+
.jpeg({ quality: options.quality || 85, progressive: true })
|
|
140
|
+
.toBuffer();
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
finalBuffer = pngBuffer;
|
|
144
|
+
}
|
|
145
|
+
await fs.writeFile(outputPath, finalBuffer);
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
outputPath,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
153
|
+
console.error(`[mdsvr] OG image generation failed: ${errorMessage}`);
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: errorMessage,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Batch generate OG images for multiple pages
|
|
162
|
+
*/
|
|
163
|
+
export async function batchGenerateOgImages(items, format = "jpg", silent = false) {
|
|
164
|
+
let generated = 0;
|
|
165
|
+
let failed = 0;
|
|
166
|
+
for (const item of items) {
|
|
167
|
+
const result = await generateOgImage(item.data, {
|
|
168
|
+
outputPath: item.outputPath,
|
|
169
|
+
format,
|
|
170
|
+
});
|
|
171
|
+
if (result.success) {
|
|
172
|
+
generated++;
|
|
173
|
+
if (!silent) {
|
|
174
|
+
console.log(` ✓ OG image: ${item.urlPath} → ${item.outputPath}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
failed++;
|
|
179
|
+
if (!silent) {
|
|
180
|
+
console.log(` ✗ OG image failed: ${item.urlPath} (${result.error})`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { generated, failed };
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Get OG image output path for a URL path
|
|
188
|
+
*/
|
|
189
|
+
export function getOgImagePath(urlPath, outputDir, format = "jpg", basePath = "") {
|
|
190
|
+
// Normalize URL path and strip basePath if present
|
|
191
|
+
let normalized = urlPath.replace(/\/$/, "");
|
|
192
|
+
// Remove basePath prefix from urlPath if it exists
|
|
193
|
+
// This ensures the file structure matches the URL structure
|
|
194
|
+
const normalizedBase = basePath.endsWith("/")
|
|
195
|
+
? basePath.slice(0, -1)
|
|
196
|
+
: basePath;
|
|
197
|
+
if (normalizedBase && normalized.startsWith(normalizedBase)) {
|
|
198
|
+
normalized = normalized.slice(normalizedBase.length);
|
|
199
|
+
}
|
|
200
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
201
|
+
if (segments.length === 0) {
|
|
202
|
+
// Root path
|
|
203
|
+
return path.join(outputDir, `index.${format}`);
|
|
204
|
+
}
|
|
205
|
+
// Create path: /og/docs/guides → /og/docs/guides.jpg
|
|
206
|
+
// For directories ending with index, use index.jpg
|
|
207
|
+
if (urlPath.endsWith("/")) {
|
|
208
|
+
return path.join(outputDir, ...segments, `index.${format}`);
|
|
209
|
+
}
|
|
210
|
+
// For specific files: /guides/setup → /og/guides/setup.jpg
|
|
211
|
+
return path.join(outputDir, ...segments, `index.${format}`);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get OG image URL for a page
|
|
215
|
+
*/
|
|
216
|
+
export function getOgImageUrl(urlPath, basePath, format = "jpg") {
|
|
217
|
+
const normalizedBase = basePath.endsWith("/")
|
|
218
|
+
? basePath.slice(0, -1)
|
|
219
|
+
: basePath;
|
|
220
|
+
// Normalize URL path and strip basePath if present
|
|
221
|
+
let normalized = urlPath.replace(/\/$/, "");
|
|
222
|
+
// Remove basePath prefix from urlPath if it exists
|
|
223
|
+
// urlPath already includes basePath (e.g., /docs/3.-features/mdx)
|
|
224
|
+
// We need to strip it to avoid double basePath
|
|
225
|
+
if (normalizedBase && normalized.startsWith(normalizedBase)) {
|
|
226
|
+
normalized = normalized.slice(normalizedBase.length);
|
|
227
|
+
}
|
|
228
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
229
|
+
if (segments.length === 0) {
|
|
230
|
+
return `${normalizedBase}/public/og/index.${format}`;
|
|
231
|
+
}
|
|
232
|
+
return `${normalizedBase}/public/og/${segments.join("/")}/index.${format}`;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/og/generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { generateOgImage, batchGenerateOgImages, getOgImagePath, getOgImageUrl } from "./generator.js";
|
|
2
|
+
export { generateOgTemplate, generateMinimalOgTemplate, OG_WIDTH, OG_HEIGHT } from "./template.js";
|
|
3
|
+
export type { OgImageData, OgGenerationOptions, OgGenerationResult, OgSettings, } from "./types.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/og/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACvG,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AACnG,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,GACX,MAAM,YAAY,CAAC"}
|
package/dist/og/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// OG Image generation module exports
|
|
2
|
+
export { generateOgImage, batchGenerateOgImages, getOgImagePath, getOgImageUrl } from "./generator.js";
|
|
3
|
+
export { generateOgTemplate, generateMinimalOgTemplate, OG_WIDTH, OG_HEIGHT } from "./template.js";
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/og/index.ts"],"names":[],"mappings":"AAAA,qCAAqC;AAErC,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACvG,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { OgImageData } from "./types.js";
|
|
2
|
+
export declare const OG_WIDTH = 1200;
|
|
3
|
+
export declare const OG_HEIGHT = 630;
|
|
4
|
+
/**
|
|
5
|
+
* Generate the OG image JSX structure for Satori
|
|
6
|
+
*/
|
|
7
|
+
export declare function generateOgTemplate(data: OgImageData): JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* Alternative minimal template
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateMinimalOgTemplate(data: OgImageData): JSX.Element;
|
|
12
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../../src/og/template.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,SAAS,MAAM,CAAC;AA+B7B;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,WAAW,GAAG,GAAG,CAAC,OAAO,CAkMjE;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,GAAG,CAAC,OAAO,CAsExE"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// OG Image dimensions
|
|
3
|
+
export const OG_WIDTH = 1200;
|
|
4
|
+
export const OG_HEIGHT = 630;
|
|
5
|
+
// Font fallback stack
|
|
6
|
+
const FONT_STACK = [
|
|
7
|
+
"Inter",
|
|
8
|
+
"system-ui",
|
|
9
|
+
"-apple-system",
|
|
10
|
+
"BlinkMacSystemFont",
|
|
11
|
+
"Segoe UI",
|
|
12
|
+
"Roboto",
|
|
13
|
+
"sans-serif",
|
|
14
|
+
];
|
|
15
|
+
// Monospace font for URL
|
|
16
|
+
const MONO_FONT = [
|
|
17
|
+
"JetBrains Mono",
|
|
18
|
+
"Fira Code",
|
|
19
|
+
"SF Mono",
|
|
20
|
+
"Monaco",
|
|
21
|
+
"Consolas",
|
|
22
|
+
"monospace",
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Truncate text to a maximum length
|
|
26
|
+
*/
|
|
27
|
+
function truncate(text, maxLength) {
|
|
28
|
+
if (text.length <= maxLength)
|
|
29
|
+
return text;
|
|
30
|
+
return text.slice(0, maxLength - 3).trim() + "...";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate the OG image JSX structure for Satori
|
|
34
|
+
*/
|
|
35
|
+
export function generateOgTemplate(data) {
|
|
36
|
+
const { title, description, siteName, urlPath, accentColor = "#0969da", fontFamily = "Inter", backgroundColor = "#0a0a0f", textColor = "#ffffff", } = data;
|
|
37
|
+
const truncatedTitle = truncate(title, 120);
|
|
38
|
+
const truncatedDesc = description ? truncate(description, 200) : "";
|
|
39
|
+
// Create font family string
|
|
40
|
+
const fonts = [fontFamily, ...FONT_STACK.filter((f) => f !== fontFamily)];
|
|
41
|
+
const fontFamilyString = fonts.join(", ");
|
|
42
|
+
const monoFontString = MONO_FONT.join(", ");
|
|
43
|
+
return (_jsxs("div", { style: {
|
|
44
|
+
width: OG_WIDTH,
|
|
45
|
+
height: OG_HEIGHT,
|
|
46
|
+
display: "flex",
|
|
47
|
+
flexDirection: "column",
|
|
48
|
+
background: backgroundColor,
|
|
49
|
+
fontFamily: fontFamilyString,
|
|
50
|
+
color: textColor,
|
|
51
|
+
padding: "60px 80px",
|
|
52
|
+
position: "relative",
|
|
53
|
+
overflow: "hidden",
|
|
54
|
+
}, children: [_jsx("div", { style: {
|
|
55
|
+
position: "absolute",
|
|
56
|
+
top: -100,
|
|
57
|
+
right: -100,
|
|
58
|
+
width: 600,
|
|
59
|
+
height: 600,
|
|
60
|
+
background: `radial-gradient(circle, ${accentColor}20 0%, transparent 70%)`,
|
|
61
|
+
borderRadius: "50%",
|
|
62
|
+
} }), _jsx("div", { style: {
|
|
63
|
+
position: "absolute",
|
|
64
|
+
bottom: -150,
|
|
65
|
+
left: -100,
|
|
66
|
+
width: 500,
|
|
67
|
+
height: 500,
|
|
68
|
+
background: `radial-gradient(circle, ${accentColor}15 0%, transparent 70%)`,
|
|
69
|
+
borderRadius: "50%",
|
|
70
|
+
} }), _jsx("div", { style: {
|
|
71
|
+
position: "absolute",
|
|
72
|
+
top: 0,
|
|
73
|
+
left: 0,
|
|
74
|
+
right: 0,
|
|
75
|
+
height: 6,
|
|
76
|
+
background: `linear-gradient(90deg, ${accentColor} 0%, ${accentColor}80 50%, ${accentColor}40 100%)`,
|
|
77
|
+
} }), _jsxs("div", { style: {
|
|
78
|
+
display: "flex",
|
|
79
|
+
flexDirection: "column",
|
|
80
|
+
flex: 1,
|
|
81
|
+
justifyContent: "center",
|
|
82
|
+
}, children: [_jsx("div", { style: {
|
|
83
|
+
fontSize: 24,
|
|
84
|
+
fontWeight: 600,
|
|
85
|
+
color: accentColor,
|
|
86
|
+
textTransform: "uppercase",
|
|
87
|
+
letterSpacing: "0.1em",
|
|
88
|
+
marginBottom: 20,
|
|
89
|
+
}, children: siteName }), _jsx("div", { style: {
|
|
90
|
+
fontSize: 64,
|
|
91
|
+
fontWeight: 800,
|
|
92
|
+
lineHeight: 1.1,
|
|
93
|
+
marginBottom: description ? 24 : 0,
|
|
94
|
+
color: textColor,
|
|
95
|
+
}, children: truncatedTitle }), truncatedDesc && (_jsx("div", { style: {
|
|
96
|
+
fontSize: 28,
|
|
97
|
+
fontWeight: 400,
|
|
98
|
+
lineHeight: 1.4,
|
|
99
|
+
color: `${textColor}cc`,
|
|
100
|
+
maxWidth: 900,
|
|
101
|
+
}, children: truncatedDesc }))] }), urlPath && (_jsxs("div", { style: {
|
|
102
|
+
display: "flex",
|
|
103
|
+
alignItems: "center",
|
|
104
|
+
gap: 12,
|
|
105
|
+
marginTop: "auto",
|
|
106
|
+
paddingTop: 40,
|
|
107
|
+
}, children: [_jsx("div", { style: {
|
|
108
|
+
width: 8,
|
|
109
|
+
height: 8,
|
|
110
|
+
background: accentColor,
|
|
111
|
+
borderRadius: "50%",
|
|
112
|
+
} }), _jsx("div", { style: {
|
|
113
|
+
fontFamily: monoFontString,
|
|
114
|
+
fontSize: 18,
|
|
115
|
+
color: `${textColor}99`,
|
|
116
|
+
letterSpacing: "0.02em",
|
|
117
|
+
}, children: urlPath })] })), _jsxs("div", { style: {
|
|
118
|
+
position: "absolute",
|
|
119
|
+
bottom: 40,
|
|
120
|
+
right: 80,
|
|
121
|
+
display: "flex",
|
|
122
|
+
alignItems: "center",
|
|
123
|
+
gap: 8,
|
|
124
|
+
}, children: [_jsx("div", { style: {
|
|
125
|
+
width: 40,
|
|
126
|
+
height: 4,
|
|
127
|
+
background: accentColor,
|
|
128
|
+
borderRadius: 2,
|
|
129
|
+
} }), _jsx("div", { style: {
|
|
130
|
+
width: 20,
|
|
131
|
+
height: 4,
|
|
132
|
+
background: `${accentColor}60`,
|
|
133
|
+
borderRadius: 2,
|
|
134
|
+
} }), _jsx("div", { style: {
|
|
135
|
+
width: 10,
|
|
136
|
+
height: 4,
|
|
137
|
+
background: `${accentColor}30`,
|
|
138
|
+
borderRadius: 2,
|
|
139
|
+
} })] })] }));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Alternative minimal template
|
|
143
|
+
*/
|
|
144
|
+
export function generateMinimalOgTemplate(data) {
|
|
145
|
+
const { title, siteName, accentColor = "#0969da", backgroundColor = "#ffffff", textColor = "#1a1a1a", } = data;
|
|
146
|
+
const fonts = [data.fontFamily || "Inter", ...FONT_STACK];
|
|
147
|
+
return (_jsxs("div", { style: {
|
|
148
|
+
width: OG_WIDTH,
|
|
149
|
+
height: OG_HEIGHT,
|
|
150
|
+
display: "flex",
|
|
151
|
+
flexDirection: "column",
|
|
152
|
+
background: backgroundColor,
|
|
153
|
+
fontFamily: fonts.join(", "),
|
|
154
|
+
padding: "80px",
|
|
155
|
+
position: "relative",
|
|
156
|
+
}, children: [_jsx("div", { style: {
|
|
157
|
+
position: "absolute",
|
|
158
|
+
left: 0,
|
|
159
|
+
top: 0,
|
|
160
|
+
bottom: 0,
|
|
161
|
+
width: 12,
|
|
162
|
+
background: accentColor,
|
|
163
|
+
} }), _jsxs("div", { style: {
|
|
164
|
+
display: "flex",
|
|
165
|
+
flexDirection: "column",
|
|
166
|
+
justifyContent: "center",
|
|
167
|
+
flex: 1,
|
|
168
|
+
paddingLeft: 40,
|
|
169
|
+
}, children: [_jsx("div", { style: {
|
|
170
|
+
fontSize: 20,
|
|
171
|
+
fontWeight: 500,
|
|
172
|
+
color: accentColor,
|
|
173
|
+
textTransform: "uppercase",
|
|
174
|
+
letterSpacing: "0.15em",
|
|
175
|
+
marginBottom: 24,
|
|
176
|
+
}, children: siteName }), _jsx("div", { style: {
|
|
177
|
+
fontSize: 72,
|
|
178
|
+
fontWeight: 700,
|
|
179
|
+
lineHeight: 1.15,
|
|
180
|
+
color: textColor,
|
|
181
|
+
}, children: truncate(title, 100) })] })] }));
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../../src/og/template.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface OgImageData {
|
|
2
|
+
/** Page title (from frontmatter or extracted from H1) */
|
|
3
|
+
title: string;
|
|
4
|
+
/** Page description (from frontmatter or extracted from content) */
|
|
5
|
+
description?: string;
|
|
6
|
+
/** Site name */
|
|
7
|
+
siteName: string;
|
|
8
|
+
/** URL path */
|
|
9
|
+
urlPath?: string;
|
|
10
|
+
/** Accent color from settings */
|
|
11
|
+
accentColor?: string;
|
|
12
|
+
/** Font family */
|
|
13
|
+
fontFamily?: string;
|
|
14
|
+
/** Background color */
|
|
15
|
+
backgroundColor?: string;
|
|
16
|
+
/** Text color */
|
|
17
|
+
textColor?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface OgGenerationOptions {
|
|
20
|
+
/** Output file path */
|
|
21
|
+
outputPath: string;
|
|
22
|
+
/** Image format */
|
|
23
|
+
format: "jpg" | "png";
|
|
24
|
+
/** Image quality (0-100) for JPG */
|
|
25
|
+
quality?: number;
|
|
26
|
+
/** Width of the image */
|
|
27
|
+
width?: number;
|
|
28
|
+
/** Height of the image */
|
|
29
|
+
height?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface OgSettings {
|
|
32
|
+
/** Enable OG image generation */
|
|
33
|
+
enabled: boolean;
|
|
34
|
+
/** Template name (default: "default") */
|
|
35
|
+
template: string;
|
|
36
|
+
/** Image format */
|
|
37
|
+
imageFormat: "jpg" | "png";
|
|
38
|
+
/** Generate on serve mode */
|
|
39
|
+
generateOnServe: boolean;
|
|
40
|
+
/** Font family */
|
|
41
|
+
fontFamily: string;
|
|
42
|
+
/** Color scheme */
|
|
43
|
+
colors: {
|
|
44
|
+
background: string;
|
|
45
|
+
text: string;
|
|
46
|
+
accent: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export interface OgGenerationResult {
|
|
50
|
+
success: boolean;
|
|
51
|
+
outputPath?: string;
|
|
52
|
+
error?: string;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/og/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,uBAAuB;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,MAAM,EAAE,KAAK,GAAG,KAAK,CAAC;IACtB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC;IAC3B,6BAA6B;IAC7B,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
|
package/dist/og/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/og/types.ts"],"names":[],"mappings":"AAAA,sBAAsB"}
|
|
@@ -11,5 +11,21 @@ export interface MarkdownResult {
|
|
|
11
11
|
toc: TocItem[];
|
|
12
12
|
}
|
|
13
13
|
export declare function renderMarkdown(content: string, settings: Settings): MarkdownResult;
|
|
14
|
+
/**
|
|
15
|
+
* Extract the first H1 heading from markdown content (for SEO title fallback)
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractFirstHeading(content: string): string | null;
|
|
18
|
+
/**
|
|
19
|
+
* Extract the first paragraph text from markdown content (for SEO description fallback)
|
|
20
|
+
* Skips frontmatter, headings, code blocks, and HTML comments
|
|
21
|
+
*/
|
|
22
|
+
export declare function extractFirstParagraph(content: string): string | null;
|
|
23
|
+
/**
|
|
24
|
+
* Extract SEO data from markdown content
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractSeoData(content: string): {
|
|
27
|
+
title: string | null;
|
|
28
|
+
description: string | null;
|
|
29
|
+
};
|
|
14
30
|
export declare function renderMarkdownSimple(content: string): string;
|
|
15
31
|
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/renderer/markdown.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,GAAG,EAAE,OAAO,EAAE,CAAC;CAChB;AAwDD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAahB;
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/renderer/markdown.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,GAAG,EAAE,OAAO,EAAE,CAAC;CAChB;AAwDD,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,GACjB,cAAc,CAahB;AAgBD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOlE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAwCpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAC/C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAKA;AAGD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkD5D"}
|
|
@@ -73,6 +73,62 @@ function extractToc(html) {
|
|
|
73
73
|
}
|
|
74
74
|
return toc;
|
|
75
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Extract the first H1 heading from markdown content (for SEO title fallback)
|
|
78
|
+
*/
|
|
79
|
+
export function extractFirstHeading(content) {
|
|
80
|
+
// Match markdown H1 heading (# Heading text)
|
|
81
|
+
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
82
|
+
if (h1Match) {
|
|
83
|
+
return h1Match[1].trim();
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Extract the first paragraph text from markdown content (for SEO description fallback)
|
|
89
|
+
* Skips frontmatter, headings, code blocks, and HTML comments
|
|
90
|
+
*/
|
|
91
|
+
export function extractFirstParagraph(content) {
|
|
92
|
+
// Remove frontmatter
|
|
93
|
+
const withoutFrontmatter = content.replace(/^---[\s\S]*?---/, "");
|
|
94
|
+
// Remove code blocks
|
|
95
|
+
const withoutCodeBlocks = withoutFrontmatter.replace(/```[\s\S]*?```/g, "");
|
|
96
|
+
// Remove HTML comments
|
|
97
|
+
const withoutComments = withoutCodeBlocks.replace(/<!--[\s\S]*?-->/g, "");
|
|
98
|
+
// Remove headings
|
|
99
|
+
const withoutHeadings = withoutComments.replace(/^#{1,6}\s+.+$/gm, "");
|
|
100
|
+
// Remove horizontal rules
|
|
101
|
+
const withoutRules = withoutHeadings.replace(/^[\s]*[-_*]{3,}[\s]*$/gm, "");
|
|
102
|
+
// Find first non-empty paragraph (text followed by blank line or end of string)
|
|
103
|
+
// Match text that isn't a list item, blockquote, or other special markdown
|
|
104
|
+
const paragraphRegex = /^([^\s#\-\*\>\|\!\`].+)$/m;
|
|
105
|
+
const match = withoutRules.match(paragraphRegex);
|
|
106
|
+
if (match) {
|
|
107
|
+
// Clean up the text: remove markdown formatting and trim
|
|
108
|
+
let text = match[1]
|
|
109
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // Remove links, keep text
|
|
110
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1") // Remove bold
|
|
111
|
+
.replace(/\*([^*]+)\*/g, "$1") // Remove italic
|
|
112
|
+
.replace(/`([^`]+)`/g, "$1") // Remove inline code
|
|
113
|
+
.replace(/\s+/g, " ") // Normalize whitespace
|
|
114
|
+
.trim();
|
|
115
|
+
// Limit to reasonable length for description
|
|
116
|
+
if (text.length > 300) {
|
|
117
|
+
text = text.slice(0, 297).trim() + "...";
|
|
118
|
+
}
|
|
119
|
+
return text || null;
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Extract SEO data from markdown content
|
|
125
|
+
*/
|
|
126
|
+
export function extractSeoData(content) {
|
|
127
|
+
return {
|
|
128
|
+
title: extractFirstHeading(content),
|
|
129
|
+
description: extractFirstParagraph(content),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
76
132
|
// For V1 compatibility - simple render without frontmatter extraction
|
|
77
133
|
export function renderMarkdownSimple(content) {
|
|
78
134
|
const md = new MarkdownIt({
|