From a476a6c784d16c26d5d60b97714a94c585139d42 Mon Sep 17 00:00:00 2001 From: Ben Schlegel Date: Mon, 25 Sep 2023 13:48:43 +0200 Subject: [PATCH] refactor: move default image to own component --- quartz/cfg.ts | 2 +- quartz/components/Head.tsx | 140 ++++------------------------------- quartz/util/defaultImage.tsx | 82 +++++++++++++++++++- quartz/util/imageHelper.ts | 56 +++++++++++++- 4 files changed, 149 insertions(+), 131 deletions(-) diff --git a/quartz/cfg.ts b/quartz/cfg.ts index b77e3ccd1..c6eea8e68 100644 --- a/quartz/cfg.ts +++ b/quartz/cfg.ts @@ -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 = diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx index 8a369ade8..f4a1aa962 100644 --- a/quartz/components/Head.tsx +++ b/quartz/components/Head.tsx @@ -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 - /** - * `GlobalConfiguration` of quartz (used for theme/typography) - */ - cfg: GlobalConfiguration - imageHtml?: ( - cfg: GlobalConfiguration, - title: string, - description: string, - ) => Parameters["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( -
-
-

- {title} -

-

- {description} -

-
-
-
, - { - 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() diff --git a/quartz/util/defaultImage.tsx b/quartz/util/defaultImage.tsx index f55d8d7c8..f633876ad 100644 --- a/quartz/util/defaultImage.tsx +++ b/quartz/util/defaultImage.tsx @@ -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) => ( -
-) +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 ( +
+
+

+ {title} +

+

+ {description} +

+
+
+
+ ) +} diff --git a/quartz/util/imageHelper.ts b/quartz/util/imageHelper.ts index 71119240a..dbb5f68af 100644 --- a/quartz/util/imageHelper.ts +++ b/quartz/util/imageHelper.ts @@ -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 { } }) } + +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 + /** + * `GlobalConfiguration` of quartz (used for theme/typography) + */ + cfg: GlobalConfiguration + imageHtml?: ( + cfg: GlobalConfiguration, + title: string, + description: string, + fonts: SatoriOptions["fonts"], + ) => JSXInternal.Element +}