Add support for iOS app icons and PWA favicons
Updates `client/index.html` and `client/public/manifest.json` to include proper iOS icon links and specifies PNG format for favicons. Enhances the `/api/favicon` endpoint to generate PNG favicons using `sharp` for various sizes and includes the "go4" text for larger icons. Also adds `@types/compression` to `package.json` and `package-lock.json`. Replit-Commit-Author: Agent Replit-Commit-Session-Id: ab9cd02a-d0b2-4288-9ceb-1964d0059648 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/ab9cd02a-d0b2-4288-9ceb-1964d0059648/mbX83Mu
This commit is contained in:
+52
-19
@@ -13,6 +13,7 @@ import multer from "multer";
|
||||
import { randomUUID } from "crypto";
|
||||
import path from "path";
|
||||
import session from "express-session";
|
||||
import sharp from "sharp";
|
||||
|
||||
// Extend express session
|
||||
declare module "express-session" {
|
||||
@@ -690,32 +691,64 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
});
|
||||
|
||||
// Favicon generation endpoint
|
||||
app.get('/api/favicon', (req, res) => {
|
||||
app.get('/api/favicon', async (req, res) => {
|
||||
try {
|
||||
const size = req.query.size ? parseInt(req.query.size as string) : 32;
|
||||
const format = req.query.format as string || 'svg';
|
||||
const padding = Math.max(2, size * 0.1);
|
||||
const logoSize = size - (padding * 2);
|
||||
const cornerRadius = Math.max(2, size * 0.15);
|
||||
|
||||
// Generate SVG favicon with just the logo
|
||||
const svg = `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366f1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Logo background -->
|
||||
<rect x="${padding}" y="${padding}" width="${logoSize}" height="${logoSize}" rx="${cornerRadius}" fill="url(#logoGradient)"/>
|
||||
|
||||
<!-- Play triangle -->
|
||||
<polygon points="${padding + logoSize * 0.35},${padding + logoSize * 0.25} ${padding + logoSize * 0.35},${padding + logoSize * 0.75} ${padding + logoSize * 0.65},${padding + logoSize * 0.5}" fill="white"/>
|
||||
</svg>`;
|
||||
if (format === 'png') {
|
||||
// Ustvarimo PNG ikono za iOS PWA
|
||||
// Ustvarimo SVG za pretvorbo v PNG
|
||||
const svg = `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366f1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- iOS stil ozadje z rounded corners -->
|
||||
<rect x="0" y="0" width="${size}" height="${size}" rx="${cornerRadius * 1.5}" fill="url(#logoGradient)"/>
|
||||
|
||||
<!-- Bel play triangle v sredini -->
|
||||
<polygon points="${padding + logoSize * 0.35},${padding + logoSize * 0.25} ${padding + logoSize * 0.35},${padding + logoSize * 0.75} ${padding + logoSize * 0.65},${padding + logoSize * 0.5}" fill="white" opacity="0.95"/>
|
||||
|
||||
<!-- go4 besedilo pod trikotnikom -->
|
||||
${size >= 120 ? `<text x="${size/2}" y="${size * 0.8}" font-family="Arial, sans-serif" font-size="${size * 0.12}" font-weight="bold" fill="white" text-anchor="middle" opacity="0.9">go4</text>` : ''}
|
||||
</svg>`;
|
||||
|
||||
res.setHeader('Content-Type', 'image/svg+xml');
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400'); // Cache for 24 hours
|
||||
res.send(svg);
|
||||
// Pretvorimo SVG v PNG z Sharp
|
||||
const pngBuffer = await sharp(Buffer.from(svg))
|
||||
.png({ quality: 100, compressionLevel: 0 })
|
||||
.toBuffer();
|
||||
|
||||
res.setHeader('Content-Type', 'image/png');
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400'); // Cache for 24 hours
|
||||
res.send(pngBuffer);
|
||||
} else {
|
||||
// Originalni SVG favicon
|
||||
const svg = `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="logoGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#6366f1;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8b5cf6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Logo background -->
|
||||
<rect x="${padding}" y="${padding}" width="${logoSize}" height="${logoSize}" rx="${cornerRadius}" fill="url(#logoGradient)"/>
|
||||
|
||||
<!-- Play triangle -->
|
||||
<polygon points="${padding + logoSize * 0.35},${padding + logoSize * 0.25} ${padding + logoSize * 0.35},${padding + logoSize * 0.75} ${padding + logoSize * 0.65},${padding + logoSize * 0.5}" fill="white"/>
|
||||
</svg>`;
|
||||
|
||||
res.setHeader('Content-Type', 'image/svg+xml');
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400'); // Cache for 24 hours
|
||||
res.send(svg);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error generating favicon:', error);
|
||||
res.status(500).send('Error generating favicon');
|
||||
|
||||
Reference in New Issue
Block a user