feat: photography features & image optimizations
- Hero carousel: gallery.yaml hero accepts single UUID or array, crossfade component - Full-bleed single albums: subpages with 1 album skip grid, render photos directly - EXIF on hover: camera/lens/focal length overlay on grid items (toggle via exifOnHover) - Customizable grid: columns/gap/aspectRatio/layout (masonry|uniform) via gallery.yaml - Per-subpage grid overrides: each subpage can override global grid config - True masonry: per-image aspect ratios from EXIF dimensions - next/image migration: homepage hero, album covers, grid thumbnails, about portrait - ThumbHash blur placeholders and dominant color backgrounds - Custom next/image loader via image proxy with width/quality params - Lightbox fade-in transition with dominant color background - Password-protected subpages, health check, rate limiting, OG images
This commit is contained in:
30
app/page.tsx
30
app/page.tsx
@@ -1,13 +1,14 @@
|
||||
/**
|
||||
* Homepage — Split hero layout.
|
||||
* Left panel: site title, subtitle, navigation links.
|
||||
* Right panel: hero image from Immich.
|
||||
* Right panel: hero image carousel from Immich.
|
||||
*/
|
||||
|
||||
import Link from 'next/link';
|
||||
import { immich } from '@/lib/immich';
|
||||
import { getConfig } from '@/lib/config';
|
||||
import { imageUrl } from '@/lib/urls';
|
||||
import { imageUrl, assetPlaceholder } from '@/lib/urls';
|
||||
import { HeroCarousel } from '@/components/HeroCarousel';
|
||||
|
||||
// Render at request time — requires live Immich connection
|
||||
export const dynamic = 'force-dynamic';
|
||||
@@ -19,6 +20,18 @@ export default async function HomePage() {
|
||||
immich.getStandaloneAlbums(),
|
||||
]);
|
||||
|
||||
// Fetch ThumbHash for all hero images
|
||||
const heroData = await Promise.all(
|
||||
config.heroImages.map(async (id) => {
|
||||
const asset = await immich.getAssetInfo(id);
|
||||
const ph = asset ? assetPlaceholder(asset) : null;
|
||||
return {
|
||||
src: imageUrl(id, 'preview'),
|
||||
...(ph ? { blurDataURL: ph.blurDataURL } : {}),
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="hero">
|
||||
{/* ── Left Panel ──────────────────────────────── */}
|
||||
@@ -52,18 +65,11 @@ export default async function HomePage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ── Right Panel (Hero Image) ────────────────── */}
|
||||
{/* ── Right Panel (Hero Carousel) ─────────────── */}
|
||||
<div className="hero__right">
|
||||
{config.heroImage ? (
|
||||
<img
|
||||
src={imageUrl(config.heroImage, 'preview')}
|
||||
alt=""
|
||||
className="hero__image"
|
||||
/>
|
||||
) : (
|
||||
<div className="hero__image-placeholder" />
|
||||
)}
|
||||
<HeroCarousel images={heroData} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user