Team Ai
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 21d agoView on Hugging Face
0likes1.2kdownloads
favicon-colorize.ts114 linesDownload Raw Back to scripts
1import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';2import { dirname, resolve } from 'node:path';3import { fileURLToPath } from 'node:url';4 5const HERE = dirname(fileURLToPath(import.meta.url));6const PROJECT_ROOT = resolve(HERE, '..');7const DEFAULT_LOGO = resolve(PROJECT_ROOT, 'src/lib/assets/logo.svg');8const DEFAULT_OUT_DIR = resolve(PROJECT_ROOT, 'static');9const DEFAULT_OUT_LIGHT = resolve(DEFAULT_OUT_DIR, 'favicon.svg');10const DEFAULT_OUT_DARK = resolve(DEFAULT_OUT_DIR, 'favicon-dark.svg');11const CURRENT_COLOR = 'currentColor';12 13export interface ColorizedFavicon {14	light: string;15	dark: string;16}17 18export interface WriteThemeFaviconsOptions {19	sourcePath?: string;20	lightOutPath?: string;21	darkOutPath?: string;22	/**23	 * Fraction of the icon (0..1) to leave as an even margin on each side.24	 * Applied by wrapping the inner content in a `<g transform="...">` so the25	 * source `src/lib/assets/logo.svg` is not modified. Pass 0 to disable.26	 */27	padding?: number;28}29 30/**31 * Replace every `currentColor` occurrence in the SVG with the given color.32 * Pure: no filesystem access, so it is straightforward to unit-test.33 */34export function colorizeFaviconSvg(35	svg: string,36	lightColor: string,37	darkColor: string38): ColorizedFavicon {39	return {40		dark: svg.replaceAll(CURRENT_COLOR, darkColor),41		light: svg.replaceAll(CURRENT_COLOR, lightColor)42	};43}44 45/**46 * Shrink the inner SVG content uniformly and re-center it so `padding` (a47 * 0..1 fraction) is reserved as equal margin on each side. Returns the input48 * unchanged for non-positive padding, missing/invalid `viewBox`, or unexpected49 * markup so the caller always gets a renderable SVG.50 */51export function padFaviconSvg(svg: string, padding: number): string {52	if (!(padding > 0) || padding >= 1) return svg;53 54	const viewBoxMatch = svg.match(/viewBox\s*=\s*["']([^"']+)["']/i);55 56	if (!viewBoxMatch) return svg;57 58	const parts = viewBoxMatch[1]59		.trim()60		.split(/[\s,]+/)61		.map(Number);62 63	if (parts.length !== 4 || parts.some((n) => !Number.isFinite(n))) return svg;64 65	const [, , width, height] = parts;66 67	if (width <= 0 || height <= 0) return svg;68 69	const scale = 1 - padding;70	const translateX = (padding * width) / 2;71	const translateY = (padding * height) / 2;72	const openTagStart = svg.search(/<svg\b/i);73 74	if (openTagStart === -1) return svg;75 76	const openTagEnd = svg.indexOf('>', openTagStart);77 78	if (openTagEnd === -1) return svg;79 80	const closeStart = svg.lastIndexOf('</svg');81 82	if (closeStart === -1 || closeStart <= openTagEnd) return svg;83 84	const openTag = svg.slice(0, openTagEnd + 1);85	const inner = svg.slice(openTagEnd + 1, closeStart);86	const closeTag = svg.slice(closeStart);87	const group = `<g transform="translate(${translateX} ${translateY}) scale(${scale})">`;88 89	return `${openTag}${group}${inner}</g>${closeTag}`;90}91 92/**93 * Read `src/lib/assets/logo.svg`, colorize it for both themes, and write94 * the results to the static directory so the PWA asset generator can consume95 * them. Paths can be overridden for tests.96 */97export function writeThemeFavicons(98	lightColor: string,99	darkColor: string,100	{101		darkOutPath = DEFAULT_OUT_DARK,102		lightOutPath = DEFAULT_OUT_LIGHT,103		padding = 0,104		sourcePath = DEFAULT_LOGO105	}: WriteThemeFaviconsOptions = {}106): void {107	const source = readFileSync(sourcePath, 'utf-8');108	const { dark, light } = colorizeFaviconSvg(source, lightColor, darkColor);109 110	mkdirSync(dirname(lightOutPath), { recursive: true });111	writeFileSync(lightOutPath, padFaviconSvg(light, padding));112	writeFileSync(darkOutPath, padFaviconSvg(dark, padding));113}114