'use client'; import * as React from 'react'; import { Download, Loader2, Code2, Globe, Layout, FileImage } from 'lucide-react'; import { FaviconFileUpload } from './FaviconFileUpload'; import { CodeSnippet } from './CodeSnippet'; import { generateFaviconSet } from '@/lib/favicon/faviconService'; import { downloadBlobsAsZip } from '@/lib/media/utils/fileUtils'; import type { FaviconSet, FaviconOptions } from '@/types/favicon'; import { toast } from 'sonner'; import { cn } from '@/lib/utils/cn'; type Tab = 'icons' | 'html' | 'manifest'; type MobileTab = 'setup' | 'results'; const TABS: { value: Tab; label: string; icon: React.ReactNode }[] = [ { value: 'icons', label: 'Icons', icon: }, { value: 'html', label: 'HTML', icon: }, { value: 'manifest', label: 'Manifest', icon: }, ]; const actionBtn = 'flex items-center justify-center gap-1.5 px-3 py-1.5 text-xs glass rounded-md border border-border/30 text-muted-foreground hover:text-primary hover:border-primary/30 hover:bg-primary/10 transition-all disabled:opacity-40 disabled:cursor-not-allowed'; const inputCls = 'w-full bg-transparent border border-border/40 rounded-lg px-3 py-2 text-xs font-mono outline-none focus:border-primary/50 transition-colors text-foreground/80 placeholder:text-muted-foreground/30'; export function FaviconGenerator() { const [sourceFile, setSourceFile] = React.useState(null); const [options, setOptions] = React.useState({ name: 'My App', shortName: 'App', backgroundColor: '#ffffff', themeColor: '#3b82f6', }); const [isGenerating, setIsGenerating] = React.useState(false); const [progress, setProgress] = React.useState(0); const [result, setResult] = React.useState(null); const [tab, setTab] = React.useState('icons'); const [mobileTab, setMobileTab] = React.useState('setup'); const handleGenerate = async () => { if (!sourceFile) { toast.error('Please upload a source image'); return; } setIsGenerating(true); setProgress(0); try { const resultSet = await generateFaviconSet(sourceFile, options, (p) => setProgress(p)); setResult(resultSet); setMobileTab('results'); toast.success('Favicon set generated!'); } catch (error) { console.error(error); toast.error('Failed to generate favicons'); } finally { setIsGenerating(false); } }; const handleDownloadAll = async () => { if (!result) return; const files = result.icons.map((icon) => ({ blob: icon.blob!, filename: icon.name })); const manifestBlob = new Blob([result.manifest], { type: 'application/json' }); files.push({ blob: manifestBlob, filename: 'site.webmanifest' }); await downloadBlobsAsZip(files, 'favicons.zip'); toast.success('Downloading favicons ZIP…'); }; const handleReset = () => { setSourceFile(null); setResult(null); setProgress(0); setMobileTab('setup'); }; return (
{/* ── Mobile tab switcher ─────────────────────────────── */}
{(['setup', 'results'] as MobileTab[]).map((t) => ( ))}
{/* ── Main layout ─────────────────────────────────────── */}
{/* Left: Setup */}
{/* Upload zone */}
Source Image setSourceFile(null)} disabled={isGenerating} />
{/* App config */}
App Details
setOptions({ ...options, name: e.target.value })} placeholder="My Awesome App" className={inputCls} />
setOptions({ ...options, shortName: e.target.value })} placeholder="App" className={inputCls} />

Used for mobile home screen labels

setOptions({ ...options, backgroundColor: e.target.value })} className="w-8 h-8 rounded-lg cursor-pointer border border-border/40 bg-transparent shrink-0 p-0.5" /> setOptions({ ...options, backgroundColor: e.target.value })} className={cn(inputCls, 'py-1')} />
setOptions({ ...options, themeColor: e.target.value })} className="w-8 h-8 rounded-lg cursor-pointer border border-border/40 bg-transparent shrink-0 p-0.5" /> setOptions({ ...options, themeColor: e.target.value })} className={cn(inputCls, 'py-1')} />
{/* Action buttons */}
{result && ( )}
{/* Right: Results */}
{/* Tab bar + download button */}
{TABS.map(({ value, label, icon }) => ( ))}
{result && ( )}
{/* Scrollable content */}
{isGenerating ? (
Processing… {progress}%
) : result ? ( <> {tab === 'icons' && (
{result.icons.map((icon) => (
{icon.previewUrl ? ( {icon.name} ) : ( )}

{icon.name}

{icon.width}×{icon.height} · {(icon.size / 1024).toFixed(1)} KB

))}
)} {tab === 'html' && (

Place generated files in your site root or update the href paths.

)} {tab === 'manifest' && ( )} ) : (

No assets yet

Upload an image and generate favicons

)}
); }