From ec00a40aefca73596ab76e3ebe3a8e1129b43688 Mon Sep 17 00:00:00 2001
From: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 27 Jan 2026 18:27:17 +0000
Subject: [PATCH] chore(deps): bump the production-dependencies group with 4 updates (#2289)
---
quartz/util/og.tsx | 165 ++++++++++++++++++++++++++++++++----------------------
1 files changed, 98 insertions(+), 67 deletions(-)
diff --git a/quartz/util/og.tsx b/quartz/util/og.tsx
index c8ad663..2afd606 100644
--- a/quartz/util/og.tsx
+++ b/quartz/util/og.tsx
@@ -3,14 +3,17 @@
import { GlobalConfiguration } from "../cfg"
import { QuartzPluginData } from "../plugins/vfile"
import { JSXInternal } from "preact/src/jsx"
-import { FontSpecification, ThemeKey } from "./theme"
+import { FontSpecification, getFontSpecificationName, ThemeKey } from "./theme"
import path from "path"
import { QUARTZ } from "./path"
-import { formatDate } from "../components/Date"
-import { getDate } from "../components/Date"
+import { formatDate, getDate } from "../components/Date"
+import readingTime from "reading-time"
+import { i18n } from "../i18n"
+import { styleText } from "util"
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[] = (
@@ -25,29 +28,38 @@
const headerFontName = typeof headerFont === "string" ? headerFont : headerFont.name
const bodyFontName = typeof bodyFont === "string" ? bodyFont : bodyFont.name
- // Fetch fonts for all weights
- const headerFontPromises = headerWeights.map((weight) => fetchTtf(headerFontName, weight))
- const bodyFontPromises = bodyWeights.map((weight) => fetchTtf(bodyFontName, weight))
+ // Fetch fonts for all weights and convert to satori format in one go
+ const headerFontPromises = headerWeights.map(async (weight) => {
+ const data = await fetchTtf(headerFontName, weight)
+ if (!data) return null
+ return {
+ name: headerFontName,
+ data,
+ weight,
+ style: "normal" as const,
+ }
+ })
- const [headerFontData, bodyFontData] = await Promise.all([
+ const bodyFontPromises = bodyWeights.map(async (weight) => {
+ const data = await fetchTtf(bodyFontName, weight)
+ if (!data) return null
+ return {
+ name: bodyFontName,
+ data,
+ weight,
+ style: "normal" as const,
+ }
+ })
+
+ const [headerFonts, bodyFonts] = await Promise.all([
Promise.all(headerFontPromises),
Promise.all(bodyFontPromises),
])
- // Convert fonts to satori font format and return
+ // Filter out any failed fetches and combine header and body fonts
const fonts: SatoriOptions["fonts"] = [
- ...headerFontData.map((data, idx) => ({
- name: headerFontName,
- data,
- weight: headerWeights[idx],
- style: "normal" as const,
- })),
- ...bodyFontData.map((data, idx) => ({
- name: bodyFontName,
- data,
- weight: bodyWeights[idx],
- style: "normal" as const,
- })),
+ ...headerFonts.filter((font): font is NonNullable<typeof font> => font !== null),
+ ...bodyFonts.filter((font): font is NonNullable<typeof font> => font !== null),
]
return fonts
@@ -60,10 +72,11 @@
* @returns `.ttf` file of google font
*/
export async function fetchTtf(
- fontName: string,
+ rawFontName: string,
weight: FontWeight,
-): Promise<Buffer<ArrayBufferLike>> {
- const cacheKey = `${fontName.replaceAll(" ", "-")}-${weight}`
+): Promise<Buffer<ArrayBufferLike> | undefined> {
+ const fontName = rawFontName.replaceAll(" ", "+")
+ const cacheKey = `${fontName}-${weight}`
const cacheDir = path.join(QUARTZ, ".quartz-cache", "fonts")
const cachePath = path.join(cacheDir, cacheKey)
@@ -86,20 +99,20 @@
const match = urlRegex.exec(css)
if (!match) {
- throw new Error("Could not fetch font")
+ console.log(
+ styleText(
+ "yellow",
+ `\nWarning: Failed to fetch font ${rawFontName} with weight ${weight}, got ${cssResponse.statusText}`,
+ ),
+ )
+ return
}
// fontData is an ArrayBuffer containing the .ttf file data
const fontResponse = await fetch(match[1])
const fontData = Buffer.from(await fontResponse.arrayBuffer())
-
- try {
- await fs.mkdir(cacheDir, { recursive: true })
- await fs.writeFile(cachePath, fontData)
- } catch (error) {
- console.warn(`Failed to cache font: ${error}`)
- // Continue even if caching fails
- }
+ await fs.mkdir(cacheDir, { recursive: true })
+ await fs.writeFile(cachePath, fontData)
return fontData
}
@@ -123,21 +136,12 @@
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
}
@@ -167,24 +171,32 @@
}
// 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)
const date = rawDate ? formatDate(rawDate, cfg.locale) : null
+ // Calculate reading time
+ const { minutes } = readingTime(fileData.text ?? "")
+ const readingTimeText = i18n(cfg.locale).components.contentMeta.readingTime({
+ minutes: Math.ceil(minutes),
+ })
+
// Get tags if available
const tags = fileData.frontmatter?.tags ?? []
+ const bodyFont = getFontSpecificationName(cfg.theme.typography.body)
+ const headerFont = getFontSpecificationName(cfg.theme.typography.header)
return (
<div
@@ -195,7 +207,7 @@
width: "100%",
backgroundColor: cfg.theme.colors[colorScheme].light,
padding: "2.5rem",
- fontFamily: fonts[1].name,
+ fontFamily: bodyFont,
}}
>
{/* Header Section */}
@@ -207,20 +219,22 @@
marginBottom: "0.5rem",
}}
>
- <img
- src={iconPath}
- width={56}
- height={56}
- style={{
- borderRadius: "50%",
- }}
- />
+ {iconBase64 && (
+ <img
+ src={iconBase64}
+ width={56}
+ height={56}
+ style={{
+ borderRadius: "50%",
+ }}
+ />
+ )}
<div
style={{
display: "flex",
fontSize: 32,
color: cfg.theme.colors[colorScheme].gray,
- fontFamily: fonts[1].name,
+ fontFamily: bodyFont,
}}
>
{cfg.baseUrl}
@@ -239,7 +253,7 @@
style={{
margin: 0,
fontSize: useSmallerFont ? 64 : 72,
- fontFamily: fonts[0].name,
+ fontFamily: headerFont,
fontWeight: 700,
color: cfg.theme.colors[colorScheme].dark,
lineHeight: 1.2,
@@ -247,6 +261,7 @@
WebkitBoxOrient: "vertical",
WebkitLineClamp: 2,
overflow: "hidden",
+ textOverflow: "ellipsis",
}}
>
{title}
@@ -268,8 +283,9 @@
margin: 0,
display: "-webkit-box",
WebkitBoxOrient: "vertical",
- WebkitLineClamp: 4,
+ WebkitLineClamp: 5,
overflow: "hidden",
+ textOverflow: "ellipsis",
}}
>
{description}
@@ -287,11 +303,12 @@
borderTop: `1px solid ${cfg.theme.colors[colorScheme].lightgray}`,
}}
>
- {/* Left side - Date */}
+ {/* Left side - Date and Reading Time */}
<div
style={{
display: "flex",
alignItems: "center",
+ gap: "2rem",
color: cfg.theme.colors[colorScheme].gray,
fontSize: 28,
}}
@@ -314,6 +331,20 @@
{date}
</div>
)}
+ <div style={{ display: "flex", alignItems: "center" }}>
+ <svg
+ style={{ marginRight: "0.5rem" }}
+ width="28"
+ height="28"
+ viewBox="0 0 24 24"
+ fill="none"
+ stroke="currentColor"
+ >
+ <circle cx="12" cy="12" r="10"></circle>
+ <polyline points="12 6 12 12 16 14"></polyline>
+ </svg>
+ {readingTimeText}
+ </div>
</div>
{/* Right side - Tags */}
--
Gitblit v1.10.0