feat(brand): mavi/beyaz tema, yer tutucu logo, ikon üretimi ve PWA manifest
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Uygulama ikonlarını logodan üretir: npm run icons
|
||||
*
|
||||
* Kaynak: public/brand/icon.png (kare, en az 512×512).
|
||||
* Çıktılar (commit edilir):
|
||||
* src/app/favicon.ico 16/32/48 px (PNG gömülü ICO)
|
||||
* src/app/icon.png 192 px → <link rel="icon">
|
||||
* src/app/apple-icon.png 180 px, saydamsız → iOS ana ekran
|
||||
* public/icons/icon-192.png PWA manifest
|
||||
* public/icons/icon-512.png PWA manifest
|
||||
* public/icons/maskable-512.png PWA manifest (purpose: maskable, %80 güvenli alan)
|
||||
*
|
||||
* Logoyu değiştirdikten sonra bu betiği yeniden çalıştırın.
|
||||
*/
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import sharp from "sharp";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
const SOURCE = path.join(ROOT, "public/brand/icon.png");
|
||||
/** Saydamlık kabul etmeyen ikonlarda arka plan: marka mavisi (--primary, açık tema). */
|
||||
const BRAND_BG = "#105bbe";
|
||||
|
||||
async function png(size: number, opts: { flatten?: boolean; padding?: number } = {}) {
|
||||
const inner = Math.round(size * (1 - (opts.padding ?? 0) * 2));
|
||||
const resized = await sharp(SOURCE).resize(inner, inner, { fit: "contain" }).png().toBuffer();
|
||||
let img = sharp({
|
||||
create: {
|
||||
width: size,
|
||||
height: size,
|
||||
channels: 4,
|
||||
background: opts.flatten || opts.padding ? BRAND_BG : { r: 0, g: 0, b: 0, alpha: 0 },
|
||||
},
|
||||
}).composite([{ input: resized, gravity: "center" }]);
|
||||
if (opts.flatten) img = img.flatten({ background: BRAND_BG });
|
||||
return img.png({ compressionLevel: 9 }).toBuffer();
|
||||
}
|
||||
|
||||
/** PNG görüntülerinden ICO dosyası oluşturur (Vista+ tüm tarayıcılar PNG gömülü ICO destekler). */
|
||||
function toIco(images: { size: number; data: Buffer }[]): Buffer {
|
||||
const header = Buffer.alloc(6);
|
||||
header.writeUInt16LE(0, 0); // reserved
|
||||
header.writeUInt16LE(1, 2); // type: icon
|
||||
header.writeUInt16LE(images.length, 4);
|
||||
const entries: Buffer[] = [];
|
||||
let offset = 6 + images.length * 16;
|
||||
for (const { size, data } of images) {
|
||||
const e = Buffer.alloc(16);
|
||||
e.writeUInt8(size >= 256 ? 0 : size, 0);
|
||||
e.writeUInt8(size >= 256 ? 0 : size, 1);
|
||||
e.writeUInt8(0, 2); // palette
|
||||
e.writeUInt8(0, 3); // reserved
|
||||
e.writeUInt16LE(1, 4); // color planes
|
||||
e.writeUInt16LE(32, 6); // bpp
|
||||
e.writeUInt32LE(data.length, 8);
|
||||
e.writeUInt32LE(offset, 12);
|
||||
offset += data.length;
|
||||
entries.push(e);
|
||||
}
|
||||
return Buffer.concat([header, ...entries, ...images.map((i) => i.data)]);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const meta = await sharp(SOURCE).metadata();
|
||||
if (!meta.width || !meta.height || meta.width !== meta.height || meta.width < 512) {
|
||||
throw new Error(`${SOURCE} kare ve en az 512×512 olmalı (şu an ${meta.width}×${meta.height}).`);
|
||||
}
|
||||
await mkdir(path.join(ROOT, "public/icons"), { recursive: true });
|
||||
|
||||
const out: [string, Buffer][] = [
|
||||
["src/app/icon.png", await png(192)],
|
||||
["src/app/apple-icon.png", await png(180, { flatten: true })],
|
||||
["public/icons/icon-192.png", await png(192)],
|
||||
["public/icons/icon-512.png", await png(512)],
|
||||
["public/icons/maskable-512.png", await png(512, { padding: 0.1 })],
|
||||
[
|
||||
"src/app/favicon.ico",
|
||||
toIco(await Promise.all([16, 32, 48].map(async (size) => ({ size, data: await png(size) })))),
|
||||
],
|
||||
];
|
||||
for (const [file, data] of out) {
|
||||
await writeFile(path.join(ROOT, file), data);
|
||||
console.log(`✓ ${file} (${data.length} bayt)`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err: unknown) => {
|
||||
console.error(err instanceof Error ? err.message : err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user