--- import type { CollectionEntry } from 'astro:content'; import { formatDate, processMarkdown, isValidDate } from '../utils/markdown'; import { siteConfig, getPostCardAspectRatio } from '../config'; import { normalizeStatus, getStatusDisplayText, hasStatusStyling, getStatusClasses } from '../utils/status'; import { optimizeContentImagePath, getOptimizedFormat } from '../utils/images'; import ImageWrapper from './ImageWrapper.astro'; import Icon from './Icon.astro'; interface Props { project: CollectionEntry<'projects'>; featured?: boolean; eager?: boolean; context?: 'featured' | 'recent' | 'projects' | 'tags' | 'home'; showCategories?: boolean; } const { project, featured = false, eager = false, context = 'projects', showCategories = true } = Astro.props; // Get excerpt from markdown processing const { excerpt } = processMarkdown(project.body || ''); const { title, description: rawDescription, date, image: rawImage, imageAlt } = project.data; // Process Obsidian bracket syntax in image field (copy from PostCard) const image = (() => { if (!rawImage) return rawImage; // Handle case where rawImage is an array (unquoted YAML syntax) let imageValue = rawImage; if (Array.isArray(rawImage)) { // If it's an array, take the first element imageValue = rawImage[0]; } // Ensure imageValue is a string before calling string methods if (typeof imageValue !== 'string') { console.warn('ProjectCard: imageValue is not a string:', imageValue); return imageValue; } // Check if it's Obsidian double bracket syntax if (imageValue.startsWith('[[') && imageValue.endsWith(']]')) { // Extract the content inside the double brackets return imageValue.slice(2, -2); } return imageValue; })(); const description = rawDescription || excerpt; const categories = project.data.categories || []; const status = normalizeStatus(project.data.status); const repositoryUrl = project.data.repositoryUrl; const projectUrl = project.data.projectUrl || project.data.demoUrl || project.data.demoURL; // Determine content availability for layout const hasDescription = description && description.trim().length > 0; const hasCategories = showCategories && categories.length > 0; const hasHoverContent = hasDescription || hasCategories; // Determine if we should show the cover image // Projects should always show images when available (not controlled by postOptions) const shouldShowCoverImage = true; ---