Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5dc2a9fd37 | |||
| 57e5431e2a | |||
| a13b033b7d | |||
| 93714d2683 | |||
| b0fae48e65 | |||
| 60391b5a3f | |||
| 6d4e080d17 | |||
| 2a4c605a77 | |||
| da9d1954d6 | |||
| 4fc00098f2 | |||
| 2df7aece5c | |||
| d65be95e95 | |||
| e83d3c0933 | |||
| f69d1977ad |
@@ -184,7 +184,6 @@ export default async function Layout(props: {
|
||||
data-scroll-behavior="smooth"
|
||||
>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://img.infra.mintel.me" />
|
||||
<link rel="icon" href="/favicon.ico" sizes="any" />
|
||||
<link rel="apple-touch-icon" href="/apple-icon.png" sizes="180x180" />
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ interface HeroVideoProps {
|
||||
badge?: string;
|
||||
}
|
||||
|
||||
import placeholders from '@/lib/placeholders.json';
|
||||
|
||||
export function HeroVideo(props: HeroVideoProps) {
|
||||
const { data } = props;
|
||||
const title = props.title || data?.title || 'DIE EXPERTEN FÜR KABELNETZBAU';
|
||||
@@ -84,6 +86,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
src={posterSrc}
|
||||
alt={posterAlt}
|
||||
fill
|
||||
unoptimized
|
||||
className="object-cover z-[1] pointer-events-none filter contrast-125 saturate-110 brightness-90"
|
||||
sizes="100vw"
|
||||
priority
|
||||
@@ -93,7 +96,7 @@ export function HeroVideo(props: HeroVideoProps) {
|
||||
{videoUrl && (
|
||||
<video
|
||||
key={`${pathname}-${videoUrl}`}
|
||||
className="absolute inset-0 w-full h-full object-cover z-[2] pointer-events-none filter contrast-125 saturate-110 brightness-90"
|
||||
className={`absolute inset-0 w-full h-full object-cover z-[2] pointer-events-none filter contrast-125 saturate-110 brightness-90 transition-opacity duration-1000 ${videoSrcLoaded ? 'opacity-100' : 'opacity-0'}`}
|
||||
src={videoSrcLoaded ? videoUrl : undefined}
|
||||
autoPlay
|
||||
muted
|
||||
|
||||
@@ -107,17 +107,27 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="container relative z-10">
|
||||
<div className="absolute right-0 top-0 bottom-0 w-16 bg-gradient-to-l from-neutral-dark to-transparent z-20 md:hidden pointer-events-none" />
|
||||
<div
|
||||
ref={containerRef}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseUp={onMouseUp}
|
||||
onMouseMove={onMouseMove}
|
||||
className={`select-none flex gap-6 overflow-x-auto pb-8 pt-4 px-6 md:px-[max(2rem,calc((100vw_-_1280px)_/_2))] lg:px-[max(2.5rem,calc((100vw_-_1280px)_/_2))] [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden ${isDragging ? 'cursor-grabbing snap-none' : 'cursor-grab snap-x snap-mandatory'}`}
|
||||
>
|
||||
{references.map((ref, i) => {
|
||||
|
||||
{/* Pure CSS Full-Bleed Alignment Container */}
|
||||
<div style={{ '--offset': 'calc(50vw - 50%)' } as React.CSSProperties}>
|
||||
<div
|
||||
ref={containerRef}
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onMouseUp={onMouseUp}
|
||||
onMouseMove={onMouseMove}
|
||||
style={{
|
||||
marginLeft: 'calc(-1 * var(--offset))',
|
||||
marginRight: 'calc(-1 * var(--offset))',
|
||||
paddingLeft: 'var(--offset)',
|
||||
paddingRight: 'var(--offset)',
|
||||
scrollPaddingLeft: 'var(--offset)',
|
||||
}}
|
||||
className={`select-none flex overflow-x-auto pb-8 pt-4 [scrollbar-width:none] [-ms-overflow-style:none] [&::-webkit-scrollbar]:hidden ${isDragging ? 'cursor-grabbing snap-none' : 'cursor-grab snap-x snap-mandatory'}`}
|
||||
>
|
||||
{references.map((ref, i) => {
|
||||
const imgSrc = ref.image
|
||||
? (typeof ref.image === 'string' ? ref.image : ref.image.url)
|
||||
: fallbacks[i % fallbacks.length];
|
||||
@@ -129,7 +139,7 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
transition={{ delay: i * 0.1, duration: 1.0, ease: [0.16, 1, 0.3, 1] }}
|
||||
className="flex-shrink-0 w-[320px] md:w-[480px] snap-start group pointer-events-auto"
|
||||
className="flex-shrink-0 w-[320px] md:w-[480px] snap-start group pointer-events-auto mr-6"
|
||||
>
|
||||
<Link
|
||||
href={`/${locale}/referenzen#${ref.slug}`}
|
||||
@@ -164,6 +174,7 @@ export function ReferencesSlider(props: ReferencesSliderProps) {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="container relative z-10 mt-4 flex justify-center">
|
||||
|
||||
@@ -84,19 +84,19 @@ export function ScaleOfImpact() {
|
||||
{/* Static Data Grid (No jumping text) */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-y-4 gap-x-2 md:gap-12 w-full max-w-5xl mt-4 md:mt-12">
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">200<span className="text-primary">+</span></div>
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">200<span className="text-emerald-400">+</span></div>
|
||||
<div className="text-xs font-bold uppercase tracking-widest text-white/50">{locale === 'en' ? 'Projects' : 'Projekte'}</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">974<span className="text-primary text-xl md:text-2xl ml-1">km</span></div>
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">974<span className="text-emerald-400 text-xl md:text-2xl ml-1">km</span></div>
|
||||
<div className="text-xs font-bold uppercase tracking-widest text-white/50">{locale === 'en' ? 'Cable Laying' : 'Kabelverlegung'}</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">300<span className="text-primary text-xl md:text-2xl ml-1">km</span></div>
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">300<span className="text-emerald-400 text-xl md:text-2xl ml-1">km</span></div>
|
||||
<div className="text-xs font-bold uppercase tracking-widest text-white/50">{locale === 'en' ? 'Open Trenching' : 'Offener Tiefbau'}</div>
|
||||
</div>
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">160<span className="text-primary text-xl md:text-2xl ml-1">km</span></div>
|
||||
<div className="font-mono text-3xl md:text-5xl font-black text-white tracking-tighter mb-2">160<span className="text-emerald-400 text-xl md:text-2xl ml-1">km</span></div>
|
||||
<div className="text-xs font-bold uppercase tracking-widest text-white/50">{locale === 'en' ? 'HDD Drilling' : 'Spülbohrung'}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -73,7 +73,7 @@ export function SubCompanyTiles(props: SubCompanyTilesProps) {
|
||||
|
||||
{(badge || title) && (
|
||||
<div className="text-center md:text-left max-w-3xl mx-auto md:mx-0 mb-16">
|
||||
{badge && <h2 className="text-primary font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>}
|
||||
{badge && <h2 className="text-emerald-400 font-bold tracking-wider uppercase text-sm mb-3">{badge}</h2>}
|
||||
{title && <h3 className="font-heading text-3xl md:text-5xl font-extrabold text-white">{title}</h3>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -129,7 +129,7 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
|
||||
{item.icon}
|
||||
</motion.div>
|
||||
<motion.span
|
||||
animate={{ opacity: isActive ? 1 : 0.7, y: isActive ? 0 : 2 }}
|
||||
animate={{ y: isActive ? 0 : 2 }}
|
||||
className="text-[9px] font-bold mt-1 tracking-wide"
|
||||
>
|
||||
{item.label}
|
||||
@@ -144,7 +144,7 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
|
||||
key={item.label}
|
||||
onClick={() => setIsFlyoutOpen(!isFlyoutOpen)}
|
||||
className={`relative flex flex-col items-center justify-center flex-1 h-[62px] transition-colors duration-300 select-none w-full focus:outline-none ${
|
||||
isActive ? 'text-primary' : 'text-neutral-500 hover:text-neutral-900'
|
||||
isActive ? 'text-primary' : 'text-neutral-600 hover:text-neutral-900'
|
||||
}`}
|
||||
style={{ WebkitTapHighlightColor: 'transparent', touchAction: 'manipulation' }}
|
||||
>
|
||||
@@ -159,7 +159,7 @@ export function MobileBottomNav({ navLinks, currentLocale }: MobileBottomNavProp
|
||||
href={mappedUrl}
|
||||
onClick={() => setIsFlyoutOpen(false)}
|
||||
className={`relative flex flex-col items-center justify-center flex-1 h-[62px] transition-colors duration-300 select-none ${
|
||||
isActive ? 'text-primary' : 'text-neutral-500 hover:text-neutral-900'
|
||||
isActive ? 'text-primary' : 'text-neutral-600 hover:text-neutral-900'
|
||||
}`}
|
||||
style={{ WebkitTapHighlightColor: 'transparent', touchAction: 'manipulation' }}
|
||||
>
|
||||
|
||||
7
lib/placeholders.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"/assets/photos/DJI_0048.JPG": "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAADwAQCdASoKAAcABUB8JYwCdH8AGDNIbaAA92kbyv6vgOVMTIAAAA==",
|
||||
"/assets/videos/web/hero-bahnkreuzung-poster.jpg": "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoKAAYABUB8JaQAA3AA/vDUoAA=",
|
||||
"/assets/videos/web/hero-bohrung-poster.jpg": "data:image/webp;base64,UklGRjIAAABXRUJQVlA4ICYAAACwAQCdASoKAAYABUB8JZACdAEOO2gAAN5Rr3co8j7CDYNKNCAAAA==",
|
||||
"/assets/videos/web/hero-kabelpflug-poster.jpg": "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoKAAYABUB8JaQAA3AA/vDUoAA=",
|
||||
"/assets/videos/web/hero-messe-poster.jpg": "data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoKAAYABUB8JaQAA3AA/vDUoAA="
|
||||
}
|
||||
@@ -15,6 +15,7 @@ const nextConfig = {
|
||||
pagesBufferLength: 2,
|
||||
},
|
||||
experimental: {
|
||||
optimizeCss: true,
|
||||
staleTimes: {
|
||||
dynamic: 0,
|
||||
static: 30,
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
"prepare": "husky",
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"version": "2.2.34",
|
||||
"version": "2.2.41",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"@parcel/watcher",
|
||||
|
||||
BIN
public/assets/photos/2025-09-23_12.11.43.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 11 MiB After Width: | Height: | Size: 1007 KiB |
BIN
public/assets/photos/2025-09-23_12.12.37.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 777 KiB |
BIN
public/assets/photos/DJI_0037.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 9.3 MiB After Width: | Height: | Size: 522 KiB |
BIN
public/assets/photos/DJI_0048.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 7.8 MiB After Width: | Height: | Size: 412 KiB |
BIN
public/assets/photos/DJI_0243.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 8.6 MiB After Width: | Height: | Size: 488 KiB |
BIN
public/assets/photos/DJI_0301.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 9.8 MiB After Width: | Height: | Size: 574 KiB |
BIN
public/assets/photos/DSC00373.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 304 KiB |
BIN
public/assets/photos/DSC00375.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 304 KiB |
BIN
public/assets/photos/DSC00388.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 342 KiB |
BIN
public/assets/photos/DSC00400.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.2 MiB After Width: | Height: | Size: 322 KiB |
BIN
public/assets/photos/DSC00430.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.5 MiB After Width: | Height: | Size: 540 KiB |
BIN
public/assets/photos/DSC00440.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 358 KiB |
BIN
public/assets/photos/DSC00452.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 6.5 MiB After Width: | Height: | Size: 618 KiB |
BIN
public/assets/photos/DSC02028.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 314 KiB |
BIN
public/assets/photos/DSC02057.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.7 MiB After Width: | Height: | Size: 345 KiB |
BIN
public/assets/photos/DSC02069.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.0 MiB After Width: | Height: | Size: 597 KiB |
BIN
public/assets/photos/DSC02083.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 7.2 MiB After Width: | Height: | Size: 493 KiB |
BIN
public/assets/photos/DSC02089.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.8 MiB After Width: | Height: | Size: 371 KiB |
BIN
public/assets/photos/DSC02114.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 240 KiB |
BIN
public/assets/photos/DSC02135.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.0 MiB After Width: | Height: | Size: 325 KiB |
BIN
public/assets/photos/DSC02150.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 8.7 MiB After Width: | Height: | Size: 629 KiB |
BIN
public/assets/photos/DSC02152.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.4 MiB After Width: | Height: | Size: 659 KiB |
BIN
public/assets/photos/DSC02156.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.3 MiB After Width: | Height: | Size: 664 KiB |
BIN
public/assets/photos/DSC02164.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 9.2 MiB After Width: | Height: | Size: 648 KiB |
BIN
public/assets/photos/DSCF8687.JPG
Executable file → Normal file
|
Before Width: | Height: | Size: 8.2 MiB After Width: | Height: | Size: 442 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-100.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 218 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-12.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 345 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-14.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.5 MiB After Width: | Height: | Size: 355 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-15.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 336 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-2.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 346 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-24.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 174 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-27.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 120 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-28.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 277 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-3.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 181 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-45.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.0 MiB After Width: | Height: | Size: 276 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-47.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.9 MiB After Width: | Height: | Size: 305 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-54.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 194 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-6.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 216 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-60.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 220 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie-86.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 108 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(219_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 376 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(225_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 250 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(227_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.4 MiB After Width: | Height: | Size: 256 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(235_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 326 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(247_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 5.2 MiB After Width: | Height: | Size: 408 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(249_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 339 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(24_von_99)_groß2.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 11 MiB After Width: | Height: | Size: 188 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(250_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 353 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(257_von_80).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 159 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(26_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.7 MiB After Width: | Height: | Size: 130 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(2_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 197 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(38_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 171 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(66_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 206 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(6_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.1 MiB After Width: | Height: | Size: 290 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(71_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.2 MiB After Width: | Height: | Size: 189 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(80_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.0 MiB After Width: | Height: | Size: 185 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(83_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.6 MiB After Width: | Height: | Size: 285 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(95_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.7 MiB After Width: | Height: | Size: 218 KiB |
BIN
public/assets/photos/Etib_E-tib_Tiefbau_Guben_Netzanbindung_Energie_Vermessung_(98_von_99).jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.3 MiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 623 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 774 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 601 KiB |
BIN
public/assets/photos/Screenshot_2025-09-30_085237.png
Executable file → Normal file
|
Before Width: | Height: | Size: 5.3 MiB After Width: | Height: | Size: 455 KiB |
BIN
public/assets/photos/Screenshot_2025-09-30_085339.png
Executable file → Normal file
|
Before Width: | Height: | Size: 5.2 MiB After Width: | Height: | Size: 474 KiB |
BIN
public/assets/photos/Screenshot_2025-09-30_085736.png
Executable file → Normal file
|
Before Width: | Height: | Size: 6.0 MiB After Width: | Height: | Size: 722 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-256.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 139 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-27.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 658 KiB After Width: | Height: | Size: 238 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-294.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 3.9 MiB After Width: | Height: | Size: 233 KiB |
BIN
public/assets/photos/etib_ingenieure_tiefbau_solar_pv_planung_vermessung_gis-298.jpg
Executable file → Normal file
|
Before Width: | Height: | Size: 4.6 MiB After Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 324 KiB |
|
Before Width: | Height: | Size: 3.8 MiB After Width: | Height: | Size: 390 KiB |
44
scripts/generate-placeholders.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
const PUBLIC_DIR = path.join(process.cwd(), 'public');
|
||||
const IMAGES_TO_PROCESS = [
|
||||
'/assets/photos/DJI_0048.JPG',
|
||||
'/assets/videos/web/hero-bahnkreuzung-poster.jpg',
|
||||
'/assets/videos/web/hero-bohrung-poster.jpg',
|
||||
'/assets/videos/web/hero-kabelpflug-poster.jpg',
|
||||
'/assets/videos/web/hero-messe-poster.jpg',
|
||||
];
|
||||
|
||||
async function generatePlaceholders() {
|
||||
const placeholders: Record<string, string> = {};
|
||||
|
||||
for (const imagePath of IMAGES_TO_PROCESS) {
|
||||
const fullPath = path.join(PUBLIC_DIR, imagePath);
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
console.warn(`File not found: ${fullPath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const buffer = await sharp(fullPath)
|
||||
.resize(10, 10, { fit: 'inside' })
|
||||
.blur(2) // Adding slight blur
|
||||
.webp({ quality: 20 })
|
||||
.toBuffer();
|
||||
|
||||
const base64 = `data:image/webp;base64,${buffer.toString('base64')}`;
|
||||
placeholders[imagePath] = base64;
|
||||
console.log(`Generated placeholder for ${imagePath}`);
|
||||
} catch (e) {
|
||||
console.error(`Failed to generate placeholder for ${imagePath}`, e);
|
||||
}
|
||||
}
|
||||
|
||||
const outputPath = path.join(process.cwd(), 'lib', 'placeholders.json');
|
||||
fs.writeFileSync(outputPath, JSON.stringify(placeholders, null, 2));
|
||||
console.log(`Saved placeholders to ${outputPath}`);
|
||||
}
|
||||
|
||||
generatePlaceholders();
|
||||
41
scripts/optimize-images.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import sharp from 'sharp';
|
||||
|
||||
const PHOTOS_DIR = path.join(process.cwd(), 'public', 'assets', 'photos');
|
||||
|
||||
async function optimizeImages() {
|
||||
const files = fs.readdirSync(PHOTOS_DIR);
|
||||
let optimized = 0;
|
||||
|
||||
for (const file of files) {
|
||||
if (!file.match(/\.(jpg|jpeg|png)$/i)) continue;
|
||||
|
||||
const filePath = path.join(PHOTOS_DIR, file);
|
||||
const metadata = await sharp(filePath).metadata();
|
||||
|
||||
// If image is larger than 1920px wide, or is a massive file
|
||||
if ((metadata.width && metadata.width > 1920) || fs.statSync(filePath).size > 1024 * 1024) {
|
||||
console.log(`Optimizing ${file} (Width: ${metadata.width}, Size: ${Math.round(fs.statSync(filePath).size / 1024)}KB)`);
|
||||
|
||||
const tmpPath = `${filePath}.tmp`;
|
||||
try {
|
||||
await sharp(filePath)
|
||||
.resize({ width: 1920, withoutEnlargement: true })
|
||||
.jpeg({ quality: 80, progressive: true })
|
||||
.toFile(tmpPath);
|
||||
|
||||
fs.renameSync(tmpPath, filePath);
|
||||
console.log(`✅ Optimized ${file} (New Size: ${Math.round(fs.statSync(filePath).size / 1024)}KB)`);
|
||||
optimized++;
|
||||
} catch (err) {
|
||||
console.error(`Failed to optimize ${file}`, err);
|
||||
if (fs.existsSync(tmpPath)) fs.unlinkSync(tmpPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Finished optimizing ${optimized} images.`);
|
||||
}
|
||||
|
||||
optimizeImages();
|
||||