diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx index 88e877b74..8a369ade8 100644 --- a/quartz/components/Head.tsx +++ b/quartz/components/Head.tsx @@ -14,21 +14,56 @@ export type SocialImageOptions = { 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"] +} + /** * Generates social image (OG/twitter standard) and saves it as `.web` inside the public folder - * @param title what title to use - * @param description what description to use - * @param fileName what fileName to use when writing to disk - * @param fontsPromise header + body font to be used when generating satori image (as promise to work around sync in component) - * @param cfg `GlobalConfiguration` of quartz + * @param opts options for generating image */ -async function generateSocialImage( - title: string, - description: string, - fileName: string, - fontsPromise: Promise, - cfg: GlobalConfiguration, -) { +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 @@ -126,7 +161,8 @@ export default (() => { fontsPromise = getSatoriFont(cfg.theme.typography.header, cfg.theme.typography.body) } const slug = fileData.filePath - const filePath = slug?.replaceAll("/", "-") + // since "/" is not a valid character in file names, replace with "-" + const fileName = slug?.replaceAll("/", "-") const title = fileData.frontmatter?.title ?? "Untitled" // Get file description (priority: frontmatter > fileData > default) @@ -141,8 +177,20 @@ export default (() => { fs.mkdirSync(imageDir, { recursive: true }) } - // Generate social image (happens async) - generateSocialImage(title, description, filePath as string, fontsPromise, cfg) + if (fileName) { + // Generate social image (happens async) + generateSocialImage({ + title, + description, + fileName, + fileDir: imageDir, + imgHeight: ogHeight, + imgWidth: ogWidth, + fileExt: extension, + fontsPromise, + cfg, + }) + } } const { css, js } = externalResources @@ -157,10 +205,10 @@ export default (() => { const ogImageGeneratedPath = `https://${cfg.baseUrl}/${imageDir.replace( "public/", "", - )}/${filePath}.${extension}` + )}/${fileName}.${extension}` // Use default og image if filePath doesnt exist (for autogenerated paths with no .md file) - const useDefaultOgImage = filePath === undefined || !cfg.generateSocialImages + const useDefaultOgImage = fileName === undefined || !cfg.generateSocialImages // Path to og/social image (priority: frontmatter > generated image (if enabled) > default image) let ogImagePath = useDefaultOgImage ? ogImageDefaultPath : ogImageGeneratedPath @@ -185,11 +233,11 @@ export default (() => { - - + + - - + + {cfg.baseUrl && ( <> diff --git a/quartz/util/defaultImage.tsx b/quartz/util/defaultImage.tsx new file mode 100644 index 000000000..f55d8d7c8 --- /dev/null +++ b/quartz/util/defaultImage.tsx @@ -0,0 +1,5 @@ +import { GlobalConfiguration } from "../cfg" + +export const defaultImage = (cfg: GlobalConfiguration, title: string, description: string) => ( +
+)