Dashboard › byk.github.io › Session 1aRIMkpkBlGI
1aRIMkpkBlGIlVuZOExplore this Astro blog codebase (byk.github.io) to understand how social media preview metadata (Open Graph / Twitter cards) is generated for blog posts.
I need a "very thorough" investigation. Specifically find and report:
Where the HTML <head> metadata is generated — the layout/component that emits <meta property="og:*">, <meta name="twitter:*">, <title>, <meta name="description"> tags. Report exact file paths and line numbers.
How the social preview IMAGE (og:image / twitter:image) is generated or referenced. Is there dynamic OG image generation (e.g. satori, @astrojs/og, an endpoint like /og/[slug].png), or static images? Report the exact code path.
The post routing: how /posts/[slug] pages are built. Look at src/pages for the dynamic route file. Report how it resolves posts and passes data to the layout.
The content config (src/content.config.ts) — what frontmatter fields posts have (title, description, image, ogImage, etc.).
Look at the actual frontmatter of the specific post ultimate-setup-labs (find the .mdx file whose slug is ultimate-setup-labs) and report its full frontmatter.
Any base URL / site config (astro.config.*) — the site value, and how absolute URLs for og:image are constructed (relative vs absolute). Social previews commonly break when og:image is a relative URL instead of absolute.
Report all findings with exact file paths and relevant code snippets. Do NOT make any edits — this is read-only exploration.
I'll investigate this Astro blog codebase thoroughly. Let me start by exploring the structure and finding the key files in parallel.
total 180 drwxrwxr-x 1 byk byk 376 Jul 21 00:28 . drwxrwxr-x 1 byk byk 2342 Jul 21 12:18 .. -rw-r--r-- 1 byk byk 2672 Apr 15 14:17 AGENTS.md drwxrwxr-x 1 byk byk 214 Jul 21 00:14 .astro -rw-rw-r-- 1 byk byk 635 Apr 15 10:31 astro.config.mjs -rw-rw-r-- 1 byk byk 7 Mar 18 12:45 CNAME drwxr-xr-x 1 byk byk 162 Jul 21 00:27 dist drwxrwxr-x 1 byk byk 16 Mar 18 12:45 .frontmatter -rw-rw-r-- 1 byk byk 1594 Mar 18 12:45 frontmatter.json drwxrwxr-x 1 byk byk 220 Jul 21 00:28 .git drwxrwxr-x 1 byk byk 40 Jun 29 19:05 .github -rw-rw-r-- 1 byk byk 255 Apr 15 11:32 .gitignore -rw-rw-r-- 1 byk byk 1109 Mar 18 12:45 LICENSE drwxrwxr-x 1 byk byk 304 Jul 21 00:26 node_modules drwxr-xr-x 1 byk byk 112 Apr 17 07:57 .opencode -rw-r--r-- 1 byk byk 775 Jul 21 00:28 package.json -rw-r--r-- 1 byk byk 144591 Jul 21 00:28 pnpm-lock.yaml drwxrwxr-x 1 byk byk 70 Jun 29 19:05 public -rw-rw-r-- 1 byk byk 305 Mar 18 12:45 README.md drwxr-xr-x 1 byk byk 50 ...
/home/byk/Code/byk.github.io/src/pages/standard-site.json.ts /home/byk/Code/byk.github.io/src/pages/rss.xml.js /home/byk/Code/byk.github.io/src/pages/posts/[slug].astro /home/byk/Code/byk.github.io/src/pages/posts/[...page].astro /home/byk/Code/byk.github.io/src/pages/index.astro
/home/byk/Code/byk.github.io/src/layouts/Base.astro
/home/byk/Code/byk.github.io/src/components/posts/ReadingInfo.astro /home/byk/Code/byk.github.io/src/components/posts/PublishDate.astro /home/byk/Code/byk.github.io/src/components/posts/Post.astro /home/byk/Code/byk.github.io/src/components/posts/LatestPosts.astro /home/byk/Code/byk.github.io/src/components/posts/AuthorInfo.astro /home/byk/Code/byk.github.io/src/components/general/Welcome.astro /home/byk/Code/byk.github.io/src/components/general/Tag.astro /home/byk/Code/byk.github.io/src/components/general/SocialIcon.astro /home/byk/Code/byk.github.io/src/components/general/SeoHead.astro /home/byk/Code/byk.github.io/src/components/general/Navigation.astro /home/byk/Code/byk.github.io/src/components/general/ModeSwitch.astro /home/byk/Code/byk.github.io/src/components/general/Header.astro /home/byk/Code/byk.github.io/src/components/general/HamburgerButton.astro /home/byk/Code/byk.github.io/src/components/general/Footer.astro
/home/byk/Code/byk.github.io/astro.config.mjs
<path>/home/byk/Code/byk.github.io/src/content.config.ts</path>
<type>file</type>
<content>
1: import { defineCollection, reference } from "astro:content";
2: import { glob } from "astro/loaders";
3: import { z } from "astro/zod";
4:
5: const postsCollection = defineCollection({
6: loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/posts" }),
7: schema: ({ image }) =>
8: z.object({
9: title: z.string(),
10: intro: z.string(),
11: tag: z.string(),
12: image: image().optional(),
13: author: reference("author"),
14: pubDate: z.date(),
15: type: z.string().optional(),
16: slug: z.string().optional(),
17: }),
18: });
19:
20: const pageCollection = defineCollection({
21: loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/page" }),
22: schema: ({ image }) =>
23: z.object({
24: title: z.string(),
25: intro: z.string(),
26: image: image().optional(),
27: type: ...
The SeoHead.astro component is the key file for metadata. Let me read the core files in parallel.
<path>/home/byk/Code/byk.github.io/src/components/general/SeoHead.astro</path>
<type>file</type>
<content>
1: ---
2: import config from '../../config.mjs';
3:
4: const { postData } = Astro.props;
5:
6: const title = ( postData?.data?.title ? postData.data.title : config.title );
7: const description = postData?.data?.description ? postData.data.description : config.description;
8: const permalink = new URL(Astro.url.pathname, Astro.url);
9: const image = postData?.data?.image ? postData.data.image.src : config.defaultOgImage;
10: const type = postData?.data?.type ? postData.data.type : "website";
11:
12: ---
13:
14: <title>{title}</title>
15: <meta name="title" content={title} />
16: {description && <meta name="description" content={description} />}
17:
18: <!-- Open Graph Tags (Facebook) -->
19: <meta property="og:type" content={type} />
20: <meta property="og:title" content={title} />
21: {permalink && <meta property="og:url" content={permalink} />}
22: {description && <meta ...
<path>/home/byk/Code/byk.github.io/src/layouts/Base.astro</path>
<type>file</type>
<content>
1: ---
2: import { ClientRouter } from 'astro:transitions';
3: import SeoHead from "../components/general/SeoHead.astro"
4: import Header from "../components/general/Header.astro"
5: import Footer from "../components/general/Footer.astro"
6: import { publicationUri, documentUri } from "../lib/standard-site"
7: import '../css/style.css';
8:
9: const { postData } = Astro.props;
10: // standard.site (https://standard.site): link every page to the publication and
11: // each post to its document record so AT Protocol readers can resolve them.
12: const postSlug = postData?.data?.slug;
13: ---
14: <!doctype html>
15: <html lang="en">
16:
17: <head>
18: <script is:inline>
19: (function () {
20: // One-time migration from old storage format
21: if ('darkMode' in localStorage) {
22: localStorage.theme = localStorage.darkMode === 'true' ? ...
<path>/home/byk/Code/byk.github.io/src/pages/posts/[slug].astro</path>
<type>file</type>
<content>
1: ---
2: import { Image } from 'astro:assets'
3:
4: import Base from "../../layouts/Base.astro";
5: import AuthorInfo from "../../components/posts/AuthorInfo.astro";
6: import LatestPosts from "../../components/posts/LatestPosts.astro";
7: import PublishDate from "../../components/posts/PublishDate.astro";
8: import ReadingInfo from "../../components/posts/ReadingInfo.astro";
9: import Tag from "../../components/general/Tag.astro";
10:
11: import { getCollection, getEntry, render } from "astro:content";
12:
13: export async function getStaticPaths() {
14: const blogEntries = await getCollection("posts");
15:
16: return blogEntries.map((entry) => ({
17: params: { slug: entry.data.slug },
18: props: { entry },
19: }));
20: }
21: const { entry } = Astro.props;
22: entry.data.type = "blog";
23:
24: const { Content, remarkPluginFrontmatter } = await render(entry);
25: ...
<path>/home/byk/Code/byk.github.io/astro.config.mjs</path>
<type>file</type>
<content>
1: import { defineConfig } from "astro/config";
2: import tailwindcss from "@tailwindcss/vite";
3: import mdx from "@astrojs/mdx";
4: import icon from "astro-icon";
5: import { wordCount } from "./src/utils/word-count.mjs";
6:
7: // https://astro.build/config
8: export default defineConfig({
9: integrations: [
10: mdx({
11: remarkPlugins: [
12: () =>
13: (tree, { data }) => {
14: const words = wordCount(tree);
15: data.astro.frontmatter.words = words;
16: },
17: ],
18: }),
19: icon(),
20: ],
21: site: "https://byk.im",
22: build: {
23: assetsPrefix: "https://byk.im/",
24: },
25: vite: {
26: plugins: [tailwindcss()],
27: },
28: });
(End of file - total 28 lines)
</content>
<path>/home/byk/Code/byk.github.io/src/components/posts/Post.astro</path>
<type>file</type>
<content>
1: ---
2: const { postData, num } = Astro.props;
3: import { Image } from 'astro:assets'
4: import PublishDate from "./PublishDate.astro";
5: import Tag from "../general/Tag.astro";
6: import ReadingInfo from "../../components/posts/ReadingInfo.astro";
7: // default image if no image added
8: import defaultImage from "../../assets/article.png";
9:
10: import { render } from 'astro:content';
11: const isOdd = num % 2;
12: const { remarkPluginFrontmatter } = await render(postData);
13: ---
14: <article
15: class="
16: group
17: lg:mb-[50px] mb-10 last:mb-0 prose lg:prose-xl max-w-none
18: prose-headings:font-bold prose-headings:text-pacamara-accent
19: prose-p:text-pacamara-primary/70 lg:prose-p:text-[18px] prose-p:transition-all prose-p:duration-300
20: prose-a:font-semibold prose-a:text-pacamara-dark prose-a:hover:text-pacamara-pink ...
I've found some important discrepancies. The SeoHead.astro references fields that don't exist in the content config schema. Let me look at the config.mjs file and the specific post, plus check the actual frontmatter fields used.