feat(image): enhance image handling with new properties and responsive support
Deploy / verify (push) Successful in 41s
Deploy / deploy (push) Successful in 1m2s

- Updated PostEntry type to include _rawImageUrl and _imageFocal for improved image management.
- Refactored image handling in various components to utilize the new properties, ensuring better focal point support and responsive image rendering.
- Introduced warmResponsiveImage function for pre-fetching image variants to optimize loading performance.
- Replaced CmsImage component with RustyImage in multiple blocks for consistent image processing.
This commit is contained in:
Peter Meier
2026-04-18 08:43:17 +02:00
parent 3c6ff773ae
commit a2f2d5bfb0
14 changed files with 297 additions and 162 deletions
+8 -6
View File
@@ -5,7 +5,7 @@ import {
resolveContentImages,
resolveImageUrls,
} from '$lib/rusty-image';
import { buildResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
import { buildResponsiveImage, warmResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
import {
resolvePostOverviewBlocks,
resolveSearchableTextBlocks,
@@ -13,14 +13,15 @@ import {
} from '$lib/blog-utils';
import { DEFAULT_HOME_PAGE_SLUG, PAGE_RESOLVE, SITE_NAME } from '$lib/constants';
const BANNER_WIDTHS = [640, 960, 1280, 1600, 1920, 2400];
const BANNER_WIDTHS = [640, 960, 1280, 1600, 1920];
const BANNER_AR = '16/9';
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 ({ locals }) => {
export const load: PageServerLoad = async ({ locals, fetch }) => {
const translations = locals.translations ?? {};
let homeSlug = DEFAULT_HOME_PAGE_SLUG;
try {
@@ -76,9 +77,9 @@ export const load: PageServerLoad = async ({ locals }) => {
focalCss = focalToCss(primary.focal);
responsive = await buildResponsiveImage(primary.url, {
widths: BANNER_WIDTHS,
fit: 'contain',
formats: ['avif', 'webp', 'jpeg'],
quality: 78,
fit: 'cover',
ar: BANNER_AR,
quality: 72,
focal: primary.focal,
});
}
@@ -89,6 +90,7 @@ export const load: PageServerLoad = async ({ locals }) => {
format: 'webp',
});
}
warmResponsiveImage(responsive, fetch);
topBanner = {
banner: bannerRaw as import('$lib/cms').FullwidthBannerEntry | null,
resolvedImages,
+8 -6
View File
@@ -5,7 +5,7 @@ import {
resolveContentImages,
resolveImageUrls,
} from '$lib/rusty-image';
import { buildResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
import { buildResponsiveImage, warmResponsiveImage, type ResponsiveImage } from '$lib/rusty-image.server';
import {
resolvePostOverviewBlocks,
resolveSearchableTextBlocks,
@@ -14,14 +14,15 @@ import {
import { PAGE_RESOLVE } from '$lib/constants';
import { error } from '@sveltejs/kit';
const BANNER_WIDTHS = [640, 960, 1280, 1600, 1920, 2400];
const BANNER_WIDTHS = [640, 960, 1280, 1600, 1920];
const BANNER_AR = '16/9';
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 }) => {
export const load: PageServerLoad = async ({ params, locals, fetch }) => {
const { slug } = params;
const translations = locals.translations ?? {};
@@ -56,9 +57,9 @@ export const load: PageServerLoad = async ({ params, locals }) => {
topBannerFocalCss = focalToCss(primary.focal);
topBannerResponsive = await buildResponsiveImage(primary.url, {
widths: BANNER_WIDTHS,
fit: 'contain',
formats: ['avif', 'webp', 'jpeg'],
quality: 78,
fit: 'cover',
ar: BANNER_AR,
quality: 72,
focal: primary.focal,
});
}
@@ -68,6 +69,7 @@ export const load: PageServerLoad = async ({ params, locals }) => {
fit: 'cover',
format: 'webp',
});
warmResponsiveImage(topBannerResponsive, fetch);
}
}
+2
View File
@@ -107,6 +107,8 @@ export const load: PageServerLoad = async ({ params, locals, url }) => {
return {
post,
postImageUrl,
rawPostImageUrl: postImageField?.url ?? null,
postImageFocal: postImageField?.focal ?? null,
postDate,
contentHtml,
hasRowContent,
+16 -1
View File
@@ -5,6 +5,7 @@
import EventBadges from '$lib/components/EventBadges.svelte';
import EventMap from '$lib/components/EventMap.svelte';
import Accordion from '$lib/components/Accordion.svelte';
import RustyImage from '$lib/components/RustyImage.svelte';
import { t, T } from '$lib/translations';
import type { PageData } from './$types';
@@ -32,7 +33,21 @@
{/if}
<div class="md:flex gap-2 items-end">
{#if data.postImageUrl}
{#if data.rawPostImageUrl}
<div class="overflow-hidden inline-block my-4 border border-white self-start ring-1 ring-black/50 shadow-lg">
<RustyImage
src={data.rawPostImageUrl}
focal={data.postImageFocal}
width={150}
height={200}
fit="cover"
alt=""
class="block object-cover"
loading="eager"
fetchpriority="high"
/>
</div>
{:else if data.postImageUrl}
<div class="overflow-hidden inline-block my-4 border border-white self-start ring-1 ring-black/50 shadow-lg">
<img
src={data.postImageUrl}