refactor: move default image to own component

This commit is contained in:
Ben Schlegel 2023-09-25 13:48:43 +02:00
parent bb9e857bf7
commit a476a6c784
No known key found for this signature in database
GPG Key ID: 8BDB8891C1575E22
4 changed files with 149 additions and 131 deletions

View File

@ -1,7 +1,7 @@
import { ValidDateType } from "./components/Date"
import { SocialImageOptions } from "./components/Head"
import { QuartzComponent } from "./components/types"
import { PluginTypes } from "./plugins/types"
import { SocialImageOptions } from "./util/imageHelper"
import { Theme } from "./util/theme"
export type Analytics =

View File

@ -3,60 +3,10 @@ import { JSResourceToScriptElement } from "../util/resources"
import { QuartzComponentConstructor, QuartzComponentProps } from "./types"
import satori, { SatoriOptions } from "satori"
import * as fs from "fs"
import { getSatoriFont } from "../util/imageHelper"
import { GlobalConfiguration } from "../cfg"
import { ImageOptions, getSatoriFont } from "../util/imageHelper"
import sharp from "sharp"
export type SocialImageOptions = {
/**
* What color scheme to use for image generation (uses colors from config theme)
*/
colorScheme?: "lightMode" | "darkMode"
}
type ImageOptions = {
/**
* what title to use as header in image
*/
title: string
/**
* what description to use as body in image
*/
description: string
/**
* what fileName to use when writing to disk
*/
fileName: string
/**
* what directory to store image in
*/
fileDir: string
/**
* what file extension to use (should be `webp` unless you also change sharp conversion)
*/
fileExt: string
/**
* What height to generate image with (in px)
*/
imgHeight: number
/**
* What width to generate image with (in px)
*/
imgWidth: number
/**
* header + body font to be used when generating satori image (as promise to work around sync in component)
*/
fontsPromise: Promise<SatoriOptions["fonts"]>
/**
* `GlobalConfiguration` of quartz (used for theme/typography)
*/
cfg: GlobalConfiguration
imageHtml?: (
cfg: GlobalConfiguration,
title: string,
description: string,
) => Parameters<typeof satori>["0"]
}
import { defaultImage } from "../util/defaultImage"
import { JSXInternal } from "preact/src/jsx"
/**
* Generates social image (OG/twitter standard) and saves it as `.web` inside the public folder
@ -66,81 +16,19 @@ async function generateSocialImage(opts: ImageOptions) {
const { cfg, description, fileName, fontsPromise, title, imageHtml } = opts
const fonts = await fontsPromise
// How many characters are allowed before switching to smaller font
const fontBreakPoint = 22
const useSmallerFont = title.length > fontBreakPoint
const defaultImg = defaultImage(cfg, title, description, fonts)
// Get color scheme preference from config (use lightMode by default)
let colorScheme: SocialImageOptions["colorScheme"] = "lightMode"
if (typeof cfg.generateSocialImages !== "boolean" && cfg.generateSocialImages.colorScheme) {
colorScheme = cfg.generateSocialImages.colorScheme
// If imageHtml was passed, use it. otherwise, use default image element
let imageElement: JSXInternal.Element = defaultImg
if (imageHtml) {
imageElement = imageHtml(cfg, title, description, fonts)
}
const svg = await satori(
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center",
height: "100%",
width: "100%",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "100%",
width: "100%",
backgroundColor: cfg.theme.colors[colorScheme].light,
flexDirection: "column",
gap: "2.5rem",
paddingTop: "2rem",
paddingBottom: "2rem",
}}
>
<p
style={{
color: cfg.theme.colors[colorScheme].dark,
fontSize: useSmallerFont ? 70 : 82,
marginLeft: "4rem",
textAlign: "center",
marginRight: "4rem",
fontFamily: fonts[0].name,
}}
>
{title}
</p>
<p
style={{
color: cfg.theme.colors[colorScheme].dark,
fontSize: 44,
marginLeft: "8rem",
marginRight: "8rem",
lineClamp: 3,
fontFamily: fonts[1].name,
}}
>
{description}
</p>
</div>
<div
style={{
height: "100%",
width: "2vw",
position: "absolute",
backgroundColor: cfg.theme.colors[colorScheme].tertiary,
opacity: 0.85,
}}
/>
</div>,
{
width: ogHeight,
height: ogWidth,
fonts: fonts,
},
)
const svg = await satori(imageElement, {
width: ogHeight,
height: ogWidth,
fonts: fonts,
})
// Convert svg directly to webp (with additional compression)
const compressed = await sharp(Buffer.from(svg)).webp({ quality: 40 }).toBuffer()

View File

@ -1,5 +1,81 @@
import { SatoriOptions } from "satori/wasm"
import { GlobalConfiguration } from "../cfg"
import { SocialImageOptions } from "./imageHelper"
export const defaultImage = (cfg: GlobalConfiguration, title: string, description: string) => (
<div />
)
export const defaultImage = (
cfg: GlobalConfiguration,
title: string,
description: string,
fonts: SatoriOptions["fonts"],
) => {
// How many characters are allowed before switching to smaller font
const fontBreakPoint = 22
const useSmallerFont = title.length > fontBreakPoint
// Get color scheme preference from config (use lightMode by default)
let colorScheme: SocialImageOptions["colorScheme"] = "lightMode"
if (typeof cfg.generateSocialImages !== "boolean" && cfg.generateSocialImages.colorScheme) {
colorScheme = cfg.generateSocialImages.colorScheme
}
return (
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-start",
alignItems: "center",
height: "100%",
width: "100%",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
height: "100%",
width: "100%",
backgroundColor: cfg.theme.colors[colorScheme].light,
flexDirection: "column",
gap: "2.5rem",
paddingTop: "2rem",
paddingBottom: "2rem",
}}
>
<p
style={{
color: cfg.theme.colors[colorScheme].dark,
fontSize: useSmallerFont ? 70 : 82,
marginLeft: "4rem",
textAlign: "center",
marginRight: "4rem",
fontFamily: fonts[0].name,
}}
>
{title}
</p>
<p
style={{
color: cfg.theme.colors[colorScheme].dark,
fontSize: 44,
marginLeft: "8rem",
marginRight: "8rem",
lineClamp: 3,
fontFamily: fonts[1].name,
}}
>
{description}
</p>
</div>
<div
style={{
height: "100%",
width: "2vw",
position: "absolute",
backgroundColor: cfg.theme.colors[colorScheme].tertiary,
opacity: 0.85,
}}
/>
</div>
)
}

View File

@ -1,4 +1,6 @@
import { FontWeight, SatoriOptions } from "satori/wasm"
import satori, { FontWeight, SatoriOptions } from "satori/wasm"
import { GlobalConfiguration } from "../cfg"
import { JSXInternal } from "preact/src/jsx"
/**
* Get an array of `FontOptions` (for satori) given google font names
@ -47,3 +49,55 @@ function fetchTtf(fontName: string, weight: FontWeight): Promise<ArrayBuffer> {
}
})
}
export type SocialImageOptions = {
/**
* What color scheme to use for image generation (uses colors from config theme)
*/
colorScheme?: "lightMode" | "darkMode"
}
export type ImageOptions = {
/**
* what title to use as header in image
*/
title: string
/**
* what description to use as body in image
*/
description: string
/**
* what fileName to use when writing to disk
*/
fileName: string
/**
* what directory to store image in
*/
fileDir: string
/**
* what file extension to use (should be `webp` unless you also change sharp conversion)
*/
fileExt: string
/**
* What height to generate image with (in px)
*/
imgHeight: number
/**
* What width to generate image with (in px)
*/
imgWidth: number
/**
* header + body font to be used when generating satori image (as promise to work around sync in component)
*/
fontsPromise: Promise<SatoriOptions["fonts"]>
/**
* `GlobalConfiguration` of quartz (used for theme/typography)
*/
cfg: GlobalConfiguration
imageHtml?: (
cfg: GlobalConfiguration,
title: string,
description: string,
fonts: SatoriOptions["fonts"],
) => JSXInternal.Element
}