- {brief.data.title}
-
+
- {project.data.title}
-
+
{(project.data.demoURL || project.data.repoURL) && (
diff --git a/src/layouts/PageLayout.astro b/src/layouts/PageLayout.astro index b9a67a7..bb8017c 100644 --- a/src/layouts/PageLayout.astro +++ b/src/layouts/PageLayout.astro @@ -4,6 +4,7 @@ import Header from "@components/Header.astro"; import Footer from "@components/Footer.astro"; import { SITE } from "@consts"; import type { OpenGraphData } from "@lib/opengraph"; +import { stripMarkdown } from "@lib/markdown"; type Props = { title: string; @@ -12,12 +13,13 @@ type Props = { }; const { title, description, ogData } = Astro.props; +const plainTitle = stripMarkdown(title); ---
-
.replace(/`([^`]+)`/g, (_, content) => `${escapeHtml(content)}`)
-
+
// Bold: **text** or __text__ - escape content, then wrap in
.replace(/\*\*([^*]+)\*\*/g, (_, content) => `${escapeHtml(content)}`)
.replace(/__([^_]+)__/g, (_, content) => `${escapeHtml(content)}`)
-
+
// Italic: *text* or _text_ (but not part of bold) - escape content, then wrap in
.replace(/(? `${escapeHtml(content)}`)
.replace(/(? `${escapeHtml(content)}`)
-
+
// Strikethrough: ~~text~~ - escape content, then wrap in
.replace(/~~([^~]+)~~/g, (_, content) => `${escapeHtml(content)}`);
-
+
// Escape any remaining unprocessed text (text outside of markdown patterns)
// This is tricky because we need to avoid escaping the HTML we just created
// For now, we'll leave plain text unescaped since Astro should handle it
-
+
return html;
+}
+
+/**
+ * Strip markdown and HTML from text, returning plain text.
+ * Useful for meta tags, alt text, and other contexts where plain text is needed.
+ */
+export function stripMarkdown(text: string): string {
+ if (!text) return "";
+
+ return text
+ // Remove code: `text`
+ .replace(/`([^`]+)`/g, "$1")
+
+ // Remove bold: **text** or __text__
+ .replace(/\*\*([^*]+)\*\*/g, "$1")
+ .replace(/__([^_]+)__/g, "$1")
+
+ // Remove italic: *text* or _text_
+ .replace(/(?]*>/g, "");
}
\ No newline at end of file
diff --git a/src/lib/opengraph.ts b/src/lib/opengraph.ts
index 2364438..c0acaf8 100644
--- a/src/lib/opengraph.ts
+++ b/src/lib/opengraph.ts
@@ -1,5 +1,6 @@
import type { CollectionEntry } from "astro:content";
import { SITE } from "@consts";
+import { stripMarkdown } from "./markdown";
export interface OpenGraphData {
title: string;
@@ -68,17 +69,17 @@ export function getPostOGData(
url: string,
siteUrl: string
): OpenGraphData {
- const ogTitle = post.data.ogTitle || post.data.title;
+ const ogTitle = stripMarkdown(post.data.ogTitle || post.data.title);
const ogDescription = post.data.ogDescription || post.data.description;
-
+
let ogImage = post.data.ogImage;
if (!ogImage && !post.data.noOgImage) {
ogImage = generateTailgraphURL({
- title: post.data.cardTitle || post.data.title,
- subtitle: post.data.date.toLocaleDateString("en-US", {
- year: "numeric",
- month: "long",
- day: "numeric"
+ title: stripMarkdown(post.data.cardTitle || post.data.title),
+ subtitle: post.data.date.toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric"
}),
author: "plx",
theme: "dark",
@@ -86,7 +87,7 @@ export function getPostOGData(
logo: `${siteUrl}/default-og-image.jpg`
});
}
-
+
return {
title: ogTitle,
description: ogDescription,
@@ -116,21 +117,21 @@ export function getBriefOGData(
url: string,
siteUrl: string
): OpenGraphData {
- const ogTitle = brief.data.ogTitle || brief.data.title;
+ const ogTitle = stripMarkdown(brief.data.ogTitle || brief.data.title);
const ogDescription = brief.data.ogDescription || brief.data.description;
-
+
let ogImage = brief.data.ogImage;
if (!ogImage && !brief.data.noOgImage) {
ogImage = generateTailgraphURL({
- title: brief.data.cardTitle || brief.data.title,
- subtitle: category?.titlePrefix || category?.displayName || "Brief",
+ title: stripMarkdown(brief.data.cardTitle || brief.data.title),
+ subtitle: stripMarkdown(category?.titlePrefix || category?.displayName || "Brief"),
author: "plx",
theme: "dark",
backgroundImage: "gradient",
logo: `${siteUrl}/default-og-image.jpg`
});
}
-
+
return {
title: ogTitle,
description: ogDescription,
@@ -159,13 +160,13 @@ export function getProjectOGData(
url: string,
siteUrl: string
): OpenGraphData {
- const ogTitle = project.data.ogTitle || project.data.title;
+ const ogTitle = stripMarkdown(project.data.ogTitle || project.data.title);
const ogDescription = project.data.ogDescription || project.data.description;
-
+
let ogImage = project.data.ogImage;
if (!ogImage && !project.data.noOgImage) {
ogImage = generateTailgraphURL({
- title: project.data.title,
+ title: stripMarkdown(project.data.title),
subtitle: "Project",
author: "plx",
theme: "dark",
@@ -173,7 +174,7 @@ export function getProjectOGData(
logo: `${siteUrl}/default-og-image.jpg`
});
}
-
+
return {
title: ogTitle,
description: ogDescription,
diff --git a/src/pages/blog/[...slug].astro b/src/pages/blog/[...slug].astro
index 38d5e1c..e048cdd 100644
--- a/src/pages/blog/[...slug].astro
+++ b/src/pages/blog/[...slug].astro
@@ -6,6 +6,7 @@ import FormattedDate from "@components/FormattedDate.astro";
import { readingTime } from "@lib/utils";
import BackToPrev from "@components/BackToPrev.astro";
import { getPostOGData } from "@lib/opengraph";
+import { renderInlineMarkdown } from "@lib/markdown";
export async function getStaticPaths() {
const posts = (await getCollection("blog"))
@@ -22,6 +23,7 @@ const post = Astro.props;
const { Content } = await post.render();
const ogData = getPostOGData(post, Astro.url.toString(), Astro.site?.toString() || "");
+const renderedTitle = renderInlineMarkdown(post.data.title);
---
@@ -41,9 +43,7 @@ const ogData = getPostOGData(post, Astro.url.toString(), Astro.site?.toString()
{readingTime(post.body)}
-
- {post.data.title}
-
+
diff --git a/src/pages/briefs/[...slug].astro b/src/pages/briefs/[...slug].astro
index d17d159..9db35ac 100644
--- a/src/pages/briefs/[...slug].astro
+++ b/src/pages/briefs/[...slug].astro
@@ -27,6 +27,7 @@ const categorySlug = extractCategoryFromSlug(brief.slug);
const category = categorySlug ? getCategory(categorySlug, `src/content/briefs/${categorySlug}`) : null;
const ogData = getBriefOGData(brief, category, Astro.url.toString(), Astro.site?.toString() || "");
const renderedTitlePrefix = category?.titlePrefix ? renderInlineMarkdown(category.titlePrefix) : null;
+const renderedTitle = renderInlineMarkdown(brief.data.title);
---
@@ -49,9 +50,7 @@ const renderedTitlePrefix = category?.titlePrefix ? renderInlineMarkdown(categor
>
)}
-
- {brief.data.title}
-
+
diff --git a/src/pages/projects/[...slug].astro b/src/pages/projects/[...slug].astro
index 295c984..2e74f7c 100644
--- a/src/pages/projects/[...slug].astro
+++ b/src/pages/projects/[...slug].astro
@@ -7,6 +7,7 @@ import { readingTime } from "@lib/utils";
import BackToPrev from "@components/BackToPrev.astro";
import Link from "@components/Link.astro";
import { getProjectOGData } from "@lib/opengraph";
+import { renderInlineMarkdown } from "@lib/markdown";
export async function getStaticPaths() {
const projects = (await getCollection("projects"))
@@ -23,6 +24,7 @@ const project = Astro.props;
const { Content } = await project.render();
const ogData = getProjectOGData(project, Astro.url.toString(), Astro.site?.toString() || "");
+const renderedTitle = renderInlineMarkdown(project.data.title);
---
@@ -42,9 +44,7 @@ const ogData = getProjectOGData(project, Astro.url.toString(), Astro.site?.toStr
{readingTime(project.body)}
-
- {project.data.title}
-
+
{(project.data.demoURL || project.data.repoURL) && (
+