Skip to content
This repository was archived by the owner on Nov 6, 2024. It is now read-only.

Commit 1694e92

Browse files
authored
Generate preview images for each page (#59)
* Generate preview images for each page * remove book icon
1 parent 8f34d2b commit 1694e92

File tree

4 files changed

+491
-11
lines changed

4 files changed

+491
-11
lines changed

docusaurus.config.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Config } from "@docusaurus/types";
2+
import { docOgRenderer } from "./src/renderer/image-renderers";
23

34
const baseUrl = process.env.EMBEDDED ? "/docsite/" : "/";
45

@@ -47,7 +48,16 @@ const config: Config = {
4748
filename: "sitemap.xml",
4849
},
4950
],
50-
],
51+
!process.env.EMBEDDED && [
52+
"@waveterm/docusaurus-og",
53+
{
54+
path: "./preview-images", // relative to the build directory
55+
imageRenderers: {
56+
"docusaurus-plugin-content-docs": docOgRenderer,
57+
},
58+
},
59+
],
60+
].filter((v) => v),
5161
themes: [
5262
["classic", { customCss: "src/css/custom.css" }],
5363
!process.env.EMBEDDED && "@docusaurus/theme-search-algolia",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@docusaurus/theme-classic": "^3.5.2",
2424
"@docusaurus/theme-search-algolia": "^3.5.2",
2525
"@mdx-js/react": "^3.0.0",
26+
"@waveterm/docusaurus-og": "https://github.com/wavetermdev/docusaurus-og",
2627
"clsx": "^2.1.1",
2728
"prism-react-renderer": "^2.3.0",
2829
"react": "^18.0.0",

src/renderer/image-renderers.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import type { DocsPageData, ImageGeneratorOptions, ImageRenderer } from "@waveterm/docusaurus-og";
2+
import { readFileSync } from "fs";
3+
import { join } from "path";
4+
import React, { ReactNode } from "react";
5+
6+
const waveLogo = join(__dirname, "../../static/img/logo/wave-dark.png");
7+
const waveLogoBase64 = `data:image/png;base64,${readFileSync(waveLogo).toString("base64")}`;
8+
9+
const titleElement = ({ children }) =>
10+
React.createElement(
11+
"label",
12+
{
13+
style: {
14+
fontSize: 72,
15+
fontWeight: 800,
16+
letterSpacing: 1,
17+
margin: "25px 225px 10px 0px",
18+
color: "#e3e3e3",
19+
wordBreak: "break-word",
20+
},
21+
},
22+
children
23+
);
24+
25+
const waveLogoElement = React.createElement("img", {
26+
src: waveLogoBase64,
27+
style: {
28+
position: "absolute",
29+
top: 20,
30+
right: 20,
31+
width: 300,
32+
},
33+
});
34+
35+
const headerElement = (header: string, svg: ReactNode) =>
36+
React.createElement(
37+
"div",
38+
{
39+
style: {
40+
display: "flex",
41+
alignItems: "center",
42+
position: "absolute",
43+
top: 20,
44+
left: 40,
45+
},
46+
},
47+
svg,
48+
React.createElement(
49+
"label",
50+
{
51+
style: {
52+
fontSize: 50,
53+
fontWeight: 600,
54+
letterSpacing: 1,
55+
color: "#e3e3e3",
56+
},
57+
},
58+
header
59+
)
60+
);
61+
62+
const rootDivStyle: React.CSSProperties = {
63+
display: "flex",
64+
flexDirection: "column",
65+
height: "100%",
66+
width: "100%",
67+
padding: "10px 40px",
68+
justifyContent: "center",
69+
fontFamily: "Roboto",
70+
fontSize: 32,
71+
fontWeight: 400,
72+
backgroundColor: "#1b1b1d",
73+
color: "#e3e3e3",
74+
borderBottom: "2rem solid #58c142",
75+
};
76+
77+
export const docOgRenderer: ImageRenderer<DocsPageData> = async (data, context) => {
78+
const element = React.createElement(
79+
"div",
80+
{ style: rootDivStyle },
81+
React.createElement(titleElement, null, data.metadata.title),
82+
React.createElement("div", null, data.metadata.description.replace("&mdash;", "-")),
83+
waveLogoElement,
84+
headerElement(context.siteConfig.title, null)
85+
);
86+
87+
return [element, await imageGeneratorOptions()];
88+
};
89+
90+
const imageGeneratorOptions = async (): Promise<ImageGeneratorOptions> => {
91+
return {
92+
width: 1200,
93+
height: 600,
94+
fonts: [
95+
{
96+
name: "Roboto",
97+
data: await getTtfFont("Roboto", ["ital", "wght"], [0, 400]),
98+
weight: 400,
99+
style: "normal",
100+
},
101+
],
102+
};
103+
};
104+
105+
function docSectionPath(slug: string, title: string) {
106+
let section = slug.split("/")[1].toString();
107+
108+
// Override some sections by slug
109+
switch (section) {
110+
case "api":
111+
section = "REST APIs";
112+
break;
113+
}
114+
115+
section = section.charAt(0).toUpperCase() + section.slice(1);
116+
117+
return `${title} / ${section}`;
118+
}
119+
120+
async function getTtfFont(family: string, axes: string[], value: number[]): Promise<ArrayBuffer> {
121+
const familyParam = axes.join(",") + "@" + value.join(",");
122+
123+
// Get css style sheet with user agent Mozilla/5.0 Firefox/1.0 to ensure TTF is returned
124+
const cssCall = await fetch(`https://fonts.googleapis.com/css2?family=${family}:${familyParam}&display=swap`, {
125+
headers: {
126+
"User-Agent": "Mozilla/5.0 Firefox/1.0",
127+
},
128+
});
129+
130+
const css = await cssCall.text();
131+
const ttfUrl = css.match(/url\(([^)]+)\)/)?.[1];
132+
133+
return await fetch(ttfUrl).then((res) => res.arrayBuffer());
134+
}

0 commit comments

Comments
 (0)