From e86544064cf37e7cdb7cac302cfb40fdb728de6d Mon Sep 17 00:00:00 2001 From: Jacky Zhao Date: Sun, 16 Mar 2025 15:12:40 -0700 Subject: [PATCH] fix: parse parallelization chunk arg, inline b64 for og image --- quartz/plugins/emitters/ogImage.tsx | 24 +++++++++++-- quartz/processors/parse.ts | 4 +-- quartz/util/log.ts | 2 +- quartz/util/og.tsx | 54 +++++++++++++---------------- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/quartz/plugins/emitters/ogImage.tsx b/quartz/plugins/emitters/ogImage.tsx index f31cc4b..0b78695 100644 --- a/quartz/plugins/emitters/ogImage.tsx +++ b/quartz/plugins/emitters/ogImage.tsx @@ -1,7 +1,7 @@ import { QuartzEmitterPlugin } from "../types" import { i18n } from "../../i18n" import { unescapeHTML } from "../../util/escape" -import { FullSlug, getFileExtension } from "../../util/path" +import { FullSlug, getFileExtension, joinSegments, QUARTZ } from "../../util/path" import { ImageOptions, SocialImageOptions, defaultImage, getSatoriFonts } from "../../util/og" import sharp from "sharp" import satori, { SatoriOptions } from "satori" @@ -10,6 +10,8 @@ import { Readable } from "stream" import { write } from "./helpers" import { BuildCtx } from "../../util/ctx" import { QuartzPluginData } from "../vfile" +import fs from "node:fs/promises" +import chalk from "chalk" const defaultOptions: SocialImageOptions = { colorScheme: "lightMode", @@ -28,7 +30,25 @@ async function generateSocialImage( userOpts: SocialImageOptions, ): Promise { const { width, height } = userOpts - const imageComponent = userOpts.imageStructure(cfg, userOpts, title, description, fonts, fileData) + const iconPath = joinSegments(QUARTZ, "static", "icon.png") + let iconBase64: string | undefined = undefined + try { + const iconData = await fs.readFile(iconPath) + iconBase64 = `data:image/png;base64,${iconData.toString("base64")}` + } catch (err) { + console.warn(chalk.yellow(`Warning: Could not find icon at ${iconPath}`)) + } + + const imageComponent = userOpts.imageStructure({ + cfg, + userOpts, + title, + description, + fonts, + fileData, + iconBase64, + }) + const svg = await satori(imageComponent, { width, height, diff --git a/quartz/processors/parse.ts b/quartz/processors/parse.ts index 3a0d15a..04efdbe 100644 --- a/quartz/processors/parse.ts +++ b/quartz/processors/parse.ts @@ -172,7 +172,7 @@ export async function parseMarkdown(ctx: BuildCtx, fps: FilePath[]): Promise { - console.error(`${err}`.replace(/^error:\s*/i, "")) + console.error(err) process.exit(1) } @@ -201,7 +201,7 @@ export async function parseMarkdown(ctx: BuildCtx, fps: FilePath[]): Promise[] = [] processedFiles = 0 - for (const [mdChunk, _] of mdResults) { + for (const mdChunk of mdResults) { markdownToHtmlPromises.push(pool.exec("processHtml", [serializableCtx, mdChunk])) } const results: ProcessedContent[][] = await Promise.all( diff --git a/quartz/util/log.ts b/quartz/util/log.ts index 2d53dd3..cfd8c3f 100644 --- a/quartz/util/log.ts +++ b/quartz/util/log.ts @@ -35,7 +35,7 @@ export class QuartzLogger { const truncated = truncate(output, columns) process.stdout.write(truncated) this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerChars.length - }, 20) + }, 50) } } diff --git a/quartz/util/og.tsx b/quartz/util/og.tsx index 4901a53..41f885b 100644 --- a/quartz/util/og.tsx +++ b/quartz/util/og.tsx @@ -13,6 +13,7 @@ import chalk from "chalk" const defaultHeaderWeight = [700] const defaultBodyWeight = [400] + export async function getSatoriFonts(headerFont: FontSpecification, bodyFont: FontSpecification) { // Get all weights for header and body fonts const headerWeights: FontWeight[] = ( @@ -134,21 +135,12 @@ export type SocialImageOptions = { excludeRoot: boolean /** * JSX to use for generating image. See satori docs for more info (https://github.com/vercel/satori) - * @param cfg global quartz config - * @param userOpts options that can be set by user - * @param title title of current page - * @param description description of current page - * @param fonts global font that can be used for styling - * @param fileData full fileData of current page - * @returns prepared jsx to be used for generating image */ imageStructure: ( - cfg: GlobalConfiguration, - userOpts: UserOpts, - title: string, - description: string, - fonts: SatoriOptions["fonts"], - fileData: QuartzPluginData, + options: ImageOptions & { + userOpts: UserOpts + iconBase64?: string + }, ) => JSXInternal.Element } @@ -178,17 +170,17 @@ export type ImageOptions = { } // This is the default template for generated social image. -export const defaultImage: SocialImageOptions["imageStructure"] = ( - cfg: GlobalConfiguration, - { colorScheme }: UserOpts, - title: string, - description: string, - _fonts: SatoriOptions["fonts"], - fileData: QuartzPluginData, -) => { +export const defaultImage: SocialImageOptions["imageStructure"] = ({ + cfg, + userOpts, + title, + description, + fileData, + iconBase64, +}) => { + const { colorScheme } = userOpts const fontBreakPoint = 32 const useSmallerFont = title.length > fontBreakPoint - const iconPath = `https://${cfg.baseUrl}/static/icon.png` // Format date if available const rawDate = getDate(cfg, fileData) @@ -226,14 +218,16 @@ export const defaultImage: SocialImageOptions["imageStructure"] = ( marginBottom: "0.5rem", }} > - + {iconBase64 && ( + + )}