a139372ce2
- Added support for extracting focal points and alt text from CMS image fields. - Introduced responsive image handling in TopBanner component, allowing for AVIF/WebP sources and LQIP placeholders. - Updated image transformation functions to accommodate focal point parameters for better cropping. - Refactored image URL resolution logic across various components and routes to utilize new image field structure.
95 lines
3.1 KiB
TypeScript
95 lines
3.1 KiB
TypeScript
import type { PageServerLoad } from './$types';
|
|
import { getPageBySlug } from '$lib/cms';
|
|
import {
|
|
extractCmsImageField,
|
|
resolveContentImages,
|
|
resolveImageUrls,
|
|
} from '$lib/rusty-image';
|
|
import { buildResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
|
|
import {
|
|
resolvePostOverviewBlocks,
|
|
resolveSearchableTextBlocks,
|
|
resolveCalendarBlocks,
|
|
} from '$lib/blog-utils';
|
|
import { PAGE_RESOLVE } from '$lib/constants';
|
|
import { error } from '@sveltejs/kit';
|
|
|
|
const BANNER_WIDTHS = [640, 960, 1280, 1600, 1920, 2400];
|
|
|
|
function focalToCss(focal: { x: number; y: number } | undefined): string | null {
|
|
if (!focal) return null;
|
|
return `${(focal.x * 100).toFixed(2)}% ${(focal.y * 100).toFixed(2)}%`;
|
|
}
|
|
|
|
export const load: PageServerLoad = async ({ params, locals }) => {
|
|
const { slug } = params;
|
|
const translations = locals.translations ?? {};
|
|
|
|
const page = await getPageBySlug(slug, {
|
|
locale: 'de',
|
|
resolve: PAGE_RESOLVE,
|
|
});
|
|
|
|
if (!page) {
|
|
error(404, 'Seite nicht gefunden');
|
|
}
|
|
|
|
await resolveContentImages(page);
|
|
const tagsMap = await resolvePostOverviewBlocks(page);
|
|
await resolveSearchableTextBlocks(page, tagsMap);
|
|
await resolveCalendarBlocks(page);
|
|
|
|
// Resolve top banner if present
|
|
let topBannerResolvedImages: string[] = [];
|
|
let topBannerResponsive: ResponsiveImage | null = null;
|
|
let topBannerFocalCss: string | null = null;
|
|
const bannerRaw = (page as { topFullwidthBanner?: unknown }).topFullwidthBanner;
|
|
if (bannerRaw && typeof bannerRaw === 'object' && 'image' in bannerRaw) {
|
|
const bannerImages = Array.isArray((bannerRaw as { image?: unknown }).image)
|
|
? ((bannerRaw as { image?: unknown[] }).image ?? [])
|
|
: (bannerRaw as { image?: unknown }).image != null
|
|
? [(bannerRaw as { image?: unknown }).image]
|
|
: [];
|
|
if (bannerImages.length > 0) {
|
|
const primary = extractCmsImageField(bannerImages[0]);
|
|
if (primary?.url) {
|
|
topBannerFocalCss = focalToCss(primary.focal);
|
|
topBannerResponsive = await buildResponsiveImage(primary.url, {
|
|
widths: BANNER_WIDTHS,
|
|
fit: 'contain',
|
|
formats: ['avif', 'webp', 'jpeg'],
|
|
quality: 78,
|
|
focal: primary.focal,
|
|
});
|
|
}
|
|
topBannerResolvedImages = await resolveImageUrls(bannerImages as string[], {
|
|
width: 2000,
|
|
height: 750,
|
|
fit: 'cover',
|
|
format: 'webp',
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
page,
|
|
translations,
|
|
seoTitle: page.seoTitle ?? page.headline ?? page.linkName ?? slug,
|
|
seoDescription: page.seoDescription ?? page.subheadline ?? '',
|
|
breadcrumbItems: [
|
|
{ href: '/', label: 'Start' },
|
|
{ label: page.headline ?? page.linkName ?? page.slug ?? page._slug ?? slug },
|
|
],
|
|
topBanner: bannerRaw
|
|
? {
|
|
banner: bannerRaw as import('$lib/cms').FullwidthBannerEntry | null,
|
|
resolvedImages: topBannerResolvedImages,
|
|
responsive: topBannerResponsive,
|
|
focalCss: topBannerFocalCss,
|
|
headline: page.headline ?? undefined,
|
|
subheadline: page.subheadline ?? undefined,
|
|
}
|
|
: null,
|
|
};
|
|
};
|