feat: add Favicon Generator app with ImageMagick WASM support
This commit is contained in:
86
lib/favicon/faviconService.ts
Normal file
86
lib/favicon/faviconService.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { resizeImage } from '../media/converters/imagemagickService';
|
||||
import type { FaviconIcon, FaviconSet, FaviconOptions } from '@/types/favicon';
|
||||
|
||||
export const ICON_CONFIGS = [
|
||||
{ name: 'favicon-16x16.png', width: 16, height: 16 },
|
||||
{ name: 'favicon-32x32.png', width: 32, height: 32 },
|
||||
{ name: 'apple-touch-icon.png', width: 180, height: 180 },
|
||||
{ name: 'android-chrome-192x192.png', width: 192, height: 192 },
|
||||
{ name: 'android-chrome-512x512.png', width: 512, height: 512 },
|
||||
{ name: 'favicon.ico', width: 48, height: 48 }, // Simple ICO fallback
|
||||
];
|
||||
|
||||
export async function generateFaviconSet(
|
||||
file: File,
|
||||
options: FaviconOptions,
|
||||
onProgress?: (progress: number) => void
|
||||
): Promise<FaviconSet> {
|
||||
const icons: FaviconIcon[] = [];
|
||||
const totalSteps = ICON_CONFIGS.length;
|
||||
|
||||
for (let i = 0; i < ICON_CONFIGS.length; i++) {
|
||||
const config = ICON_CONFIGS[i];
|
||||
const format = config.name.endsWith('.ico') ? 'ico' : 'png';
|
||||
|
||||
const result = await resizeImage(file, config.width, config.height, format);
|
||||
|
||||
if (result.success && result.blob) {
|
||||
icons.push({
|
||||
name: config.name,
|
||||
width: config.width,
|
||||
height: config.height,
|
||||
size: result.blob.size,
|
||||
blob: result.blob,
|
||||
previewUrl: URL.createObjectURL(result.blob),
|
||||
});
|
||||
}
|
||||
|
||||
if (onProgress) {
|
||||
onProgress(Math.round(((i + 1) / totalSteps) * 100));
|
||||
}
|
||||
}
|
||||
|
||||
const manifest = generateManifest(options);
|
||||
const htmlCode = generateHtmlCode(options);
|
||||
|
||||
return {
|
||||
icons,
|
||||
manifest,
|
||||
htmlCode,
|
||||
};
|
||||
}
|
||||
|
||||
function generateManifest(options: FaviconOptions): string {
|
||||
const manifest = {
|
||||
name: options.name,
|
||||
short_name: options.shortName,
|
||||
icons: [
|
||||
{
|
||||
src: '/android-chrome-192x192.png',
|
||||
sizes: '192x192',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
src: '/android-chrome-512x512.png',
|
||||
sizes: '512x512',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
theme_color: options.themeColor,
|
||||
background_color: options.backgroundColor,
|
||||
display: 'standalone',
|
||||
};
|
||||
|
||||
return JSON.stringify(manifest, null, 2);
|
||||
}
|
||||
|
||||
function generateHtmlCode(options: FaviconOptions): string {
|
||||
return `
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<meta name="msapplication-TileColor" content="${options.backgroundColor}">
|
||||
<meta name="theme-color" content="${options.themeColor}">
|
||||
`.trim();
|
||||
}
|
||||
Reference in New Issue
Block a user