From b3b65bf8918641f30d9a160813001bd9970feca8 Mon Sep 17 00:00:00 2001 From: Ricardo Date: Tue, 10 Mar 2026 19:51:50 +0100 Subject: [PATCH] fix(og): strip markdown tables, lists, and non-renderable chars from body text extractBodyText() was too naive - markdown tables (|...|), heading anchors ({#id}), list numbering, and HTML tags leaked into OG image titles for notes without explicit titles. Characters outside Inter font coverage caused Satori to render "NO GLYPH" vertically on the card. Confab-Link: http://localhost:8080/sessions/5565387e-4eb5-4441-89fb-2c6347de8e0c --- lib/og.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/og.js b/lib/og.js index 439a2fb..357a89b 100644 --- a/lib/og.js +++ b/lib/og.js @@ -121,14 +121,35 @@ function truncateTitle(title, max = 120) { function extractBodyText(raw) { const body = raw + // Strip frontmatter .replace(/^---[\s\S]*?---\s*/, "") - .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") - .replace(/[#*_~`>]/g, "") + // Strip images ![alt](url) .replace(/!\[[^\]]*\]\([^)]+\)/g, "") - .replace(/\n+/g, " ") + // Strip markdown tables (lines with pipes) + .replace(/^\|.*\|$/gm, "") + // Strip table separator rows (|---|---|) + .replace(/^\s*[-|: ]+$/gm, "") + // Strip heading anchors {#id} + .replace(/\{#[^}]+\}/g, "") + // Strip HTML tags + .replace(/<[^>]+>/g, "") + // Strip markdown links, keep text + .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") + // Strip heading markers, bold, italic, strikethrough, code, blockquote + .replace(/[#*_~`>]/g, "") + // Strip list bullets (-, *, +) and numbered lists (1.) + .replace(/^\s*[-*+]\s+/gm, "") + .replace(/^\s*\d+\.\s+/gm, "") + // Strip horizontal rules + .replace(/^-{3,}$/gm, "") + // Collapse all whitespace (newlines, tabs, multiple spaces) + .replace(/\s+/g, " ") .trim(); if (!body) return "Untitled"; - return body.length > 120 ? body.slice(0, 120).trim() + "\u2026" : body; + // Strip any non-ASCII-printable characters that could cause NO GLYPH in Satori + const safe = body.replace(/[^\x20-\x7E\u00A0-\u024F\u2010-\u2027\u2030-\u205E]/g, "").trim(); + const text = safe || body; + return text.length > 120 ? text.slice(0, 120).trim() + "\u2026" : text; } function buildCard(title, dateStr, postType, siteName) {