char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aleksander Grygierui: Linting & Formatting scripts (#26819)92d1bb0c9

char/aliceai-t5-moe
4.2 KiB139 linesraw
1#!/usr/bin/env node
2
3/**
4 * Apply circular mask to pwa-*.png icons.
5 * Uses the maskable icon as source (white bg, full logo) to avoid
6 * the small-colormap pwa icons looking bad when cropped to a circle.
7 *
8 * Usage: node scripts/make-icons-circular.js [--padding-pct <0-50>] [--scale-pct <50-100>]
9 *
10 * - padding-pct: percentage of icon size kept as padding around the circle (default: 25)
11 * - scale-pct: scale down the source image before cropping (default: 85)
12 *
13 * maskable-icon and apple-touch-icon are left untouched.
14 */
15
16import fs from 'fs';
17import path from 'path';
18import sharp from 'sharp';
19import { fileURLToPath } from 'url';
20
21const __filename = fileURLToPath(import.meta.url);
22const __dirname = path.dirname(__filename);
23const STATIC_DIR = path.resolve(__dirname, '..', 'static');
24const paddingPct = process.argv.reduce((acc, arg, i, args) => {
25	if (arg === '--padding-pct' && args[i + 1]) return parseFloat(args[i + 1]);
26
27	return acc;
28}, 0);
29// Scale down the source image before cropping to circle
30const scalePct = process.argv.reduce((acc, arg, i, args) => {
31	if (arg === '--scale-pct' && args[i + 1]) return parseFloat(args[i + 1]);
32
33	return acc;
34}, 85); // default 85% - icon fills 85% of the circular area
35// Source for circular icons: the maskable icon (white bg, full logo)
36const sourceIcon = 'maskable-icon-512x512.png';
37const targetIcons = ['pwa-64x64.png', 'pwa-192x192.png', 'pwa-512x512.png'];
38// maskable-icon and apple-touch-icon stay square
39const untouchedIcons = ['maskable-icon-512x512.png', 'apple-touch-icon-180x180.png'];
40
41async function makeCircle(targetFilename) {
42	const targetPath = path.join(STATIC_DIR, targetFilename);
43	const sourcePath = path.join(STATIC_DIR, sourceIcon);
44
45	if (!fs.existsSync(sourcePath)) {
46		console.log(`⏭️  ${sourceIcon} not found, skipping`);
47
48		return;
49	}
50
51	if (!fs.existsSync(targetPath)) {
52		console.log(`⏭️  ${targetFilename} not found, skipping`);
53
54		return;
55	}
56
57	const metadata = await sharp(targetPath).metadata();
58	const size = Math.max(metadata.width, metadata.height);
59	const radius = Math.floor((size * (1 - paddingPct / 100)) / 2);
60	const center = Math.floor(size / 2);
61	// Build circular mask as RGBA buffer: white opaque circle on transparent bg
62	const maskBuf = Buffer.alloc(size * size * 4, 0);
63
64	for (let y = 0; y < size; y++) {
65		for (let x = 0; x < size; x++) {
66			const dx = x - center;
67			const dy = y - center;
68			const dist = Math.sqrt(dx * dx + dy * dy);
69
70			if (dist < radius) {
71				const i = (y * size + x) * 4;
72
73				maskBuf[i] = 255;
74				maskBuf[i + 1] = 255;
75				maskBuf[i + 2] = 255;
76				maskBuf[i + 3] = 255;
77			}
78		}
79	}
80
81	const tmpMask = path.join(STATIC_DIR, '.mask-tmp.png');
82
83	await sharp(maskBuf, {
84		raw: { channels: 4, height: size, width: size }
85	})
86		.png()
87		.toFile(tmpMask);
88
89	// Step 1: Scale source relative to circle diameter (not full icon), composite centered onto white canvas of full size
90	const circleDiameter = Math.floor(size * (1 - paddingPct / 100));
91	const scaledSize = Math.floor((circleDiameter * scalePct) / 100);
92	const offset = Math.floor((size - scaledSize) / 2);
93	const scaledBuf = await sharp(sourcePath)
94		.resize(scaledSize, scaledSize, {
95			background: { alpha: 1, b: 255, g: 255, r: 255 },
96			fit: 'cover'
97		})
98		.ensureAlpha()
99		.png()
100		.toBuffer();
101	// Step 2: Composite scaled image onto white background, then apply circular mask
102	const output = await sharp({
103		create: {
104			background: { alpha: 1, b: 255, g: 255, r: 255 },
105			channels: 4,
106			height: size,
107			width: size
108		}
109	})
110		.composite([
111			{ input: scaledBuf, left: offset, top: offset },
112			{ blend: 'dest-in', input: tmpMask, left: 0, top: 0 }
113		])
114		.png()
115		.toBuffer();
116
117	fs.writeFileSync(targetPath, output);
118	fs.unlinkSync(tmpMask);
119
120	console.log(
121		`✓ ${targetFilename} → circle from ${sourceIcon}, ${paddingPct}% padding (size=${size}, r=${radius}, scale=${scalePct}%, circleDiameter=${circleDiameter})`
122	);
123}
124
125async function main() {
126	console.log(`Circular mask: ${paddingPct}% padding, ${scalePct}% scale, source=${sourceIcon}\n`);
127	for (const icon of targetIcons) {
128		await makeCircle(icon);
129	}
130
131	console.log('\nUnchanged:');
132	for (const icon of untouchedIcons) {
133		const fp = path.join(STATIC_DIR, icon);
134
135		console.log(`  ${icon} (${fs.existsSync(fp) ? fs.statSync(fp).size + ' bytes' : 'missing'})`);
136	}
137}
138
139main();