import fs from "fs"; import path from "path"; export type SliderPhoto = { src: string; alt: string }; function slugToAlt(filename: string): string { const base = filename.replace(/\.[^.]+$/, ""); const words = base.replace(/[-_]+/g, " ").trim(); return words || "Slide"; } export function parseManifest(data: unknown): SliderPhoto[] { if (!Array.isArray(data)) return []; const out: SliderPhoto[] = []; for (const item of data) { if (typeof item !== "object" || item === null || !("src" in item)) continue; const src = (item as { src: unknown }).src; if (typeof src !== "string" || !src.startsWith("/")) continue; const altRaw = (item as { alt?: unknown }).alt; const alt = typeof altRaw === "string" && altRaw.trim() ? altRaw.trim() : slugToAlt(src.split("/").pop() ?? ""); out.push({ src, alt }); } return out; } const IMAGE_EXT = /\.(jpe?g|png|webp|gif|avif)$/i; function scanSliderDirectory(dir: string): SliderPhoto[] { let names: string[] = []; try { names = fs.readdirSync(dir); } catch { return []; } return names .filter((f) => IMAGE_EXT.test(f)) .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" })) .map((f) => ({ src: `/slider/${f}`, alt: slugToAlt(f), })); } /** * Reads `public/slider/manifest.json` when present (full control: order + alt). * Otherwise scans `public/slider` for image files (drop-in updates, no code edits). */ export function readSliderPhotos(): SliderPhoto[] { const dir = path.join(process.cwd(), "public", "slider"); if (!fs.existsSync(dir)) return []; const manifestPath = path.join(dir, "manifest.json"); if (fs.existsSync(manifestPath)) { try { const raw = JSON.parse(fs.readFileSync(manifestPath, "utf8")) as unknown; return parseManifest(raw); } catch { return []; } } return scanSliderDirectory(dir); }