import type { Template } from "./types"; /** * Built-in presets. Each is just data — selecting one sets the crop aspect, the * target output size, and sensible encoder defaults. Add rows freely. */ export const TEMPLATES: Template[] = [ { id: "original", name: "Original", group: "General", description: "Keep the source aspect ratio and full resolution.", aspect: null, output: null, encoder: { format: "jpeg", quality: 92 }, }, { id: "og-image", name: "OG image", group: "Social", description: "Open Graph cover for link previews. 1200×630.", aspect: 1200 / 630, output: { width: 1200, height: 630 }, encoder: { format: "jpeg", quality: 85 }, }, { id: "twitter-card", name: "Twitter / X card", group: "Social", description: "Summary card with large image. 1200×675.", aspect: 1200 / 675, output: { width: 1200, height: 675 }, encoder: { format: "jpeg", quality: 85 }, }, { id: "instagram-square", name: "Instagram square", group: "Social", description: "Square feed post. 1080×1080.", aspect: 1, output: { width: 1080, height: 1080 }, encoder: { format: "jpeg", quality: 90 }, }, { id: "instagram-portrait", name: "Instagram portrait", group: "Social", description: "Tall 4:5 feed post. 1080×1350.", aspect: 1080 / 1350, output: { width: 1080, height: 1350 }, encoder: { format: "jpeg", quality: 90 }, }, { id: "story", name: "Story / Reel", group: "Social", description: "Full-screen 9:16 vertical. 1080×1920.", aspect: 1080 / 1920, output: { width: 1080, height: 1920 }, encoder: { format: "jpeg", quality: 88 }, }, { id: "youtube-thumb", name: "YouTube thumb", group: "Social", description: "Video thumbnail. 1280×720.", aspect: 1280 / 720, output: { width: 1280, height: 720 }, encoder: { format: "jpeg", quality: 88 }, }, { id: "linkedin-cover", name: "LinkedIn cover", group: "Social", description: "Profile banner. 1584×396.", aspect: 1584 / 396, output: { width: 1584, height: 396 }, encoder: { format: "jpeg", quality: 85 }, }, { id: "web-banner", name: "Web banner", group: "General", description: "Full-HD hero / header. 1920×1080 WebP.", aspect: 1920 / 1080, output: { width: 1920, height: 1080 }, encoder: { format: "webp", quality: 82 }, }, { id: "avatar", name: "Avatar", group: "Profile", description: "Square profile picture. 512×512, lossless.", aspect: 1, output: { width: 512, height: 512 }, encoder: { format: "webp", quality: 92 }, }, { id: "favicon", name: "Favicon", group: "Profile", description: "Square site icon. 256×256 PNG.", aspect: 1, output: { width: 256, height: 256 }, encoder: { format: "png", quality: 100 }, }, { id: "passport-us", name: "US passport", group: "Identity", description: "2×2 inch at 300 DPI. 600×600. Position the face manually for now.", aspect: 1, output: { width: 600, height: 600 }, encoder: { format: "jpeg", quality: 95 }, // Reserved for phase-2 face-aware auto-framing (MediaPipe / @imgly/background-removal). framing: { faceHeightRatio: [0.5, 0.69], eyeLineFromTop: 0.43 }, }, ]; export const DEFAULT_TEMPLATE = TEMPLATES[0]; export function getTemplate(id: string): Template { return TEMPLATES.find((t) => t.id === id) ?? DEFAULT_TEMPLATE; } /** Templates grouped for the picker UI, preserving declaration order. */ export function groupedTemplates(): { group: string; items: Template[] }[] { const groups: { group: string; items: Template[] }[] = []; for (const t of TEMPLATES) { let bucket = groups.find((g) => g.group === t.group); if (!bucket) { bucket = { group: t.group, items: [] }; groups.push(bucket); } bucket.items.push(t); } return groups; }