feat(page): inline topImage field, fallback to topFullwidthBanner
Deploy / verify (push) Successful in 1m3s
Deploy / deploy (push) Successful in 1m4s

Reads page.topImage first (new direct image field on the page schema);
falls back to legacy topFullwidthBanner.image during transition. Same
ResponsiveImage build, social-image transform and warm-up as before.
Banner-object still passed through so existing rendering keeps working
when headline/subheadline/variant are wanted; pure-image migration
needs no further code changes.

Schema + 21 pages migrated CMS-side in the same change set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-05-21 15:14:41 +02:00
parent c329658992
commit 055f001174
+20 -14
View File
@@ -67,22 +67,26 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
]); ]);
sanitizeBlocks(page); sanitizeBlocks(page);
// Resolve top banner if present // Top-Bild: bevorzugt neues inline-Feld `topImage`, sonst Legacy via
// topFullwidthBanner-Referenz. Banner-Bild ist auch Social-Image-Quelle.
let topBannerResolvedImages: string[] = []; let topBannerResolvedImages: string[] = [];
let topBannerResponsive: ResponsiveImage | null = null; let topBannerResponsive: ResponsiveImage | null = null;
let topBannerFocalCss: string | null = null; let topBannerFocalCss: string | null = null;
// Banner-Bild ist auch unsere Social-Image-Quelle (1200×630 cover, mit Focal).
let pageSocialImageAbs: string | null = null; let pageSocialImageAbs: string | null = null;
const bannerRaw = (page as { topFullwidthBanner?: unknown }).topFullwidthBanner;
const siteBaseUrl = (env.PUBLIC_SITE_URL || '').replace(/\/$/, ''); const siteBaseUrl = (env.PUBLIC_SITE_URL || '').replace(/\/$/, '');
if (bannerRaw && typeof bannerRaw === 'object' && 'image' in bannerRaw) {
const bannerImages = Array.isArray((bannerRaw as { image?: unknown }).image) const topImageRaw = (page as { topImage?: unknown }).topImage;
? ((bannerRaw as { image?: unknown[] }).image ?? []) const bannerRaw = (page as { topFullwidthBanner?: unknown }).topFullwidthBanner;
: (bannerRaw as { image?: unknown }).image != null const topImageSource: unknown =
? [(bannerRaw as { image?: unknown }).image] topImageRaw ??
: []; (bannerRaw && typeof bannerRaw === 'object' && 'image' in bannerRaw
if (bannerImages.length > 0) { ? (bannerRaw as { image?: unknown }).image
const primary = extractCmsImageField(bannerImages[0]); : undefined);
if (topImageSource) {
const imageArr = Array.isArray(topImageSource) ? topImageSource : [topImageSource];
if (imageArr.length > 0) {
const primary = extractCmsImageField(imageArr[0]);
if (primary?.url) { if (primary?.url) {
topBannerFocalCss = focalToCss(primary.focal); topBannerFocalCss = focalToCss(primary.focal);
topBannerResponsive = await buildResponsiveImage(primary.url, { topBannerResponsive = await buildResponsiveImage(primary.url, {
@@ -101,7 +105,7 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
? `${siteBaseUrl}${socialRel}` ? `${siteBaseUrl}${socialRel}`
: socialRel; : socialRel;
} }
topBannerResolvedImages = await resolveImageUrls(bannerImages as string[], { topBannerResolvedImages = await resolveImageUrls(imageArr as string[], {
width: 2000, width: 2000,
height: 750, height: 750,
fit: 'cover', fit: 'cover',
@@ -125,9 +129,11 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
{ href: '/', label: 'Start' }, { href: '/', label: 'Start' },
{ label: page.headline ?? page.linkName ?? page.slug ?? page._slug ?? slug }, { label: page.headline ?? page.linkName ?? page.slug ?? page._slug ?? slug },
], ],
topBanner: bannerRaw topBanner: topImageSource
? { ? {
banner: bannerRaw as import('$lib/cms').FullwidthBannerEntry | null, banner:
(bannerRaw as import('$lib/cms').FullwidthBannerEntry | null) ??
null,
resolvedImages: topBannerResolvedImages, resolvedImages: topBannerResolvedImages,
responsive: topBannerResponsive, responsive: topBannerResponsive,
focalCss: topBannerFocalCss, focalCss: topBannerFocalCss,