--- import type { Post, Heading } from '@/types'; import { siteConfig } from '@/config'; import { generatePostSEO } from '@/utils/seo'; import { optimizePostImagePath, getOptimizedFormat } from '@/utils/images'; import { processWikilinksInHTML } from '@/utils/internallinks'; import { getCollection } from 'astro:content'; import { shouldShowPost, processPost, calculateReadingTime, generateTOC, getAdjacentPosts, formatDate, formatDateMobile, getReadingTimeMobile, getPostSlug } from '@/utils/markdown'; import BaseLayout from '@/layouts/BaseLayout.astro'; import TableOfContents from '@/components/TableOfContents.astro'; import LinkedMentions from '@/components/LinkedMentions.astro'; import LocalGraph from '@/components/LocalGraph.astro'; import GiscusComments from '@/components/GiscusComments.astro'; import Lightbox from '@/components/Lightbox.astro'; import Icon from '@/components/Icon.astro'; import ImageWrapper from '@/components/ImageWrapper.astro'; import { devConfig } from '@/config/dev'; import { readFileSync } from 'fs'; import { join } from 'path'; export interface Props { post: Post; } const { post } = Astro.props; // Check if we're in development mode const isDev = import.meta.env.DEV; // Calculate current slug const currentSlug = getPostSlug(post); // Generate SEO data const siteUrl = import.meta.env.SITE || siteConfig.site; const postUrl = `${siteUrl.replace(/\/+$/, '')}/posts/${currentSlug}`; const seoData = generatePostSEO(post, postUrl); // Generate structured data for the post const structuredData = { "@context": "https://schema.schema.org", "@type": "BlogPosting", "headline": post.data.title, "description": post.data.description, "author": { "@type": "Person", "name": siteConfig.author }, "publisher": { "@type": "Organization", "name": siteConfig.title, "logo": { "@type": "ImageObject", "url": siteConfig.site + "/favicon.ico" } }, "datePublished": post.data.date.toISOString(), "dateModified": post.data.date.toISOString(), "mainEntityOfPage": { "@type": "WebPage", "@id": Astro.url.href } }; // Get all posts for wikilink resolution const allPosts = await getCollection('posts'); const visiblePosts = allPosts.filter(p => shouldShowPost(p, isDev)).map(p => ({ ...p, slug: getPostSlug(p) })); // Process the post content and get processed data including word count const { Content, headings, wordCount, remarkPluginFrontmatter } = await processPost(post); const readingTime = calculateReadingTime(post.body || ''); // Generate table of contents if enabled const hideTOC = post.data.hideTOC === true; const shouldShowTOC = !hideTOC && siteConfig.tableOfContents.enabled && headings.length > 0; const toc = shouldShowTOC ? await generateTOC(headings) : []; // Get adjacent posts for navigation const { prev: prevPost, next: nextPost } = getAdjacentPosts(visiblePosts, currentSlug); // Check if current post has connections for LocalGraph let hasLocalGraphConnections = false; if (siteConfig.postOptions.graphView.enabled && siteConfig.postOptions.graphView.showInSidebar) { try { // Read the graph data file directly from the file system (works at build time) const graphDataPath = join(process.cwd(), 'public', 'graph', 'graph-data.json'); const graphDataContent = readFileSync(graphDataPath, 'utf-8'); const graphData = JSON.parse(graphDataContent); // Check if current post has any connections (both incoming and outgoing) const matchingConnections = graphData.connections.filter((conn: any) => conn.source === post.id || conn.target === post.id ); hasLocalGraphConnections = matchingConnections.length > 0; } catch (error) { // If we can't read the graph data at build time, don't show the graph hasLocalGraphConnections = false; } } ---