--- import '../styles/global.css'; import Header from '../components/Header.astro'; import Footer from '../components/Footer.astro'; import SubscriptionPopup from '../components/SubscriptionPopup.astro'; import BackToTop from '../components/interactive/BackToTop.svelte'; import { api, DEFAULT_SITE_SETTINGS } from '../lib/api/client'; import { getI18n, LOCALE_COOKIE_NAME, SUPPORTED_LOCALES } from '../lib/i18n'; import type { SiteSettings } from '../lib/types'; interface Props { title?: string; description?: string; siteSettings?: SiteSettings; canonical?: string; noindex?: boolean; ogImage?: string; ogType?: string; twitterCard?: 'summary' | 'summary_large_image'; jsonLd?: Record | Array>; } const props = Astro.props; const { locale, messages } = getI18n(Astro); let siteSettings = props.siteSettings ?? DEFAULT_SITE_SETTINGS; if (!props.siteSettings) { try { siteSettings = await api.getSiteSettings(); } catch (error) { console.error('Failed to load site settings:', error); } } const title = props.title || siteSettings.siteTitle; const description = props.description || siteSettings.siteDescription; const siteUrl = siteSettings.siteUrl.replace(/\/$/, ''); const defaultCanonical = `${siteUrl}${Astro.url.pathname}`; const canonical = props.canonical ? /^https?:\/\//i.test(props.canonical) ? props.canonical : `${siteUrl}${props.canonical.startsWith('/') ? props.canonical : `/${props.canonical}`}` : defaultCanonical; const resolvedOgImage = props.ogImage || siteSettings.seo.defaultOgImage; const ogImage = resolvedOgImage ? /^https?:\/\//i.test(resolvedOgImage) ? resolvedOgImage : `${siteUrl}${resolvedOgImage.startsWith('/') ? resolvedOgImage : `/${resolvedOgImage}`}` : undefined; const ogType = props.ogType || 'website'; const twitterCard = props.twitterCard || (ogImage ? 'summary_large_image' : 'summary'); const defaultJsonLdObjects = [ { '@context': 'https://schema.org', '@type': 'WebSite', name: siteSettings.siteName, alternateName: siteSettings.siteShortName, url: siteUrl, description, inLanguage: locale, potentialAction: { '@type': 'SearchAction', target: `${siteUrl}/search?q={search_term_string}`, 'query-input': 'required name=search_term_string', }, }, { '@context': 'https://schema.org', '@type': 'Person', name: siteSettings.ownerName, url: siteUrl, image: siteSettings.ownerAvatarUrl || undefined, jobTitle: siteSettings.ownerTitle, description: siteSettings.ownerBio || description, sameAs: [ siteSettings.social.github, siteSettings.social.twitter, ].filter(Boolean), }, ].filter((item) => item.name || item.url); const mergedJsonLdObjects = [ ...defaultJsonLdObjects, ...(props.jsonLd === undefined ? [] : Array.isArray(props.jsonLd) ? props.jsonLd : [props.jsonLd]), ]; const jsonLd = mergedJsonLdObjects.length ? JSON.stringify(mergedJsonLdObjects) : undefined; const i18nPayload = JSON.stringify({ locale, messages }); --- {siteSettings.seo.defaultTwitterHandle && ( )} {siteSettings.seo.defaultTwitterHandle && ( )} {ogImage && } {ogImage && } {title} {jsonLd && }