121 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
#!/usr/bin/env node
 | 
						||
/*
 | 
						||
 Generates extension and toolbar PNG icons from the base SVG with transparent
 | 
						||
 background and configurable stroke colors. Requires ImageMagick (`magick`).
 | 
						||
 | 
						||
 Theme defaults (aligned with project palette):
 | 
						||
   Primary 2 (deep):  #223544 → add-on icon strokes (default)
 | 
						||
   Primary 1 (light): #5AC3D6 → toolbar icon strokes (default)
 | 
						||
 | 
						||
 Override via environment variables (optional):
 | 
						||
   ICON_COLOR_ADDON   – hex color for add-on icons (48/96/128). Default: #223544
 | 
						||
   ICON_COLOR_TOOLBAR – hex color for toolbar icons (16/32).   Default: #5AC3D6
 | 
						||
 | 
						||
 Usage:
 | 
						||
   node scripts/build-icons.js
 | 
						||
*/
 | 
						||
const fs = require('fs');
 | 
						||
const path = require('path');
 | 
						||
const { spawnSync } = require('child_process');
 | 
						||
 | 
						||
const REPO_ROOT = path.join(__dirname, '..');
 | 
						||
const ICONS_DIR = path.join(REPO_ROOT, 'icons');
 | 
						||
const SRC_SVG = path.join(ICONS_DIR, 'file-search.svg');
 | 
						||
 | 
						||
const ADDON_SIZES = [48, 96, 128];
 | 
						||
const TOOLBAR_SIZES = [16, 32];
 | 
						||
 | 
						||
const COLOR_ADDON = process.env.ICON_COLOR_ADDON || '#223544';
 | 
						||
const COLOR_TOOLBAR = process.env.ICON_COLOR_TOOLBAR || '#5AC3D6';
 | 
						||
 | 
						||
function which(cmd, args = ['--version']) {
 | 
						||
  try {
 | 
						||
    const res = spawnSync(cmd, args, { stdio: 'ignore' });
 | 
						||
    return res && res.status === 0;
 | 
						||
  } catch (_) {
 | 
						||
    return false;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function ensureRasterizer() {
 | 
						||
  // Prefer rsvg-convert (librsvg), then Inkscape, then ImageMagick.
 | 
						||
  if (which('rsvg-convert', ['-v'])) return { kind: 'rsvg', bin: 'rsvg-convert' };
 | 
						||
  // Inkscape 1.x CLI: --pipe reads from stdin
 | 
						||
  if (which('inkscape', ['--version'])) return { kind: 'inkscape', bin: 'inkscape' };
 | 
						||
  if (which('magick', ['-version'])) return { kind: 'magick', bin: 'magick' };
 | 
						||
  if (which('convert', ['-version'])) return { kind: 'magick', bin: 'convert' };
 | 
						||
  return null;
 | 
						||
}
 | 
						||
 | 
						||
function readSvg() {
 | 
						||
  if (!fs.existsSync(SRC_SVG)) {
 | 
						||
    console.error(`Base SVG not found: ${path.relative(REPO_ROOT, SRC_SVG)}`);
 | 
						||
    process.exit(1);
 | 
						||
  }
 | 
						||
  return fs.readFileSync(SRC_SVG, 'utf8');
 | 
						||
}
 | 
						||
 | 
						||
function colorize(svg, color) {
 | 
						||
  // Replace currentColor with explicit hex color and inject explicit stroke attributes on each path
 | 
						||
  let s = svg.replace(/currentColor/gi, color);
 | 
						||
  // Ensure each <path> has explicit stroke and fill to avoid inheritance issues in some renderers
 | 
						||
  s = s.replace(/<path\b([^>]*?)\/>/g, (m, attrs) => {
 | 
						||
    let a = attrs;
 | 
						||
    if (!/\bstroke\s*=/.test(a)) a = ` stroke=\"${color}\"` + a;
 | 
						||
    if (!/\bstroke-width\s*=/.test(a)) a = ` stroke-width=\"1.8\"` + a;
 | 
						||
    if (!/\bstroke-linecap\s*=/.test(a)) a = ` stroke-linecap=\"round\"` + a;
 | 
						||
    if (!/\bstroke-linejoin\s*=/.test(a)) a = ` stroke-linejoin=\"round\"` + a;
 | 
						||
    if (!/\bfill\s*=/.test(a)) a = ` fill=\"none\"` + a;
 | 
						||
    return `<path${a}/>`;
 | 
						||
  });
 | 
						||
  return s;
 | 
						||
}
 | 
						||
 | 
						||
function rasterize(raster, svgString, size, outPath) {
 | 
						||
  let args;
 | 
						||
  let cmd = raster.bin;
 | 
						||
  if (raster.kind === 'rsvg') {
 | 
						||
    // rsvg-convert reads SVG and outputs PNG; specify size and transparent background
 | 
						||
    args = ['-f', 'png', '-w', String(size), '-h', String(size), '--background-color=transparent', '-o', outPath, '-'];
 | 
						||
  } else if (raster.kind === 'inkscape') {
 | 
						||
    // Inkscape 1.x: --export-type=png --export-filename=out --export-width/height, read from stdin via --pipe
 | 
						||
    args = ['--export-type=png', `--export-filename=${outPath}`, `--export-width=${size}`, `--export-height=${size}`, '--pipe'];
 | 
						||
  } else {
 | 
						||
    // ImageMagick
 | 
						||
    args = ['-background', 'none', '-density', '384', 'svg:-', '-resize', `${size}x${size}`, outPath];
 | 
						||
  }
 | 
						||
  const proc = spawnSync(cmd, args, { input: svgString, stdio: ['pipe', 'inherit', 'inherit'] });
 | 
						||
  if (proc.status !== 0) {
 | 
						||
    console.error(`Failed to generate ${outPath} using ${raster.kind} (${cmd}).`);
 | 
						||
    process.exit(proc.status || 1);
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
function main() {
 | 
						||
  const raster = ensureRasterizer();
 | 
						||
  if (!raster) {
 | 
						||
    console.error('No SVG rasterizer found. Install one of: rsvg-convert, inkscape, or ImageMagick (magick/convert).');
 | 
						||
    process.exit(1);
 | 
						||
  }
 | 
						||
  if (!fs.existsSync(ICONS_DIR)) fs.mkdirSync(ICONS_DIR, { recursive: true });
 | 
						||
 | 
						||
  const base = readSvg();
 | 
						||
 | 
						||
  // Add-on icons
 | 
						||
  const addonSvg = colorize(base, COLOR_ADDON);
 | 
						||
  for (const s of ADDON_SIZES) {
 | 
						||
    const out = path.join(ICONS_DIR, `icon-${s}.png`);
 | 
						||
    rasterize(raster, addonSvg, s, out);
 | 
						||
    console.log(`Generated ${path.relative(REPO_ROOT, out)}`);
 | 
						||
  }
 | 
						||
 | 
						||
  // Toolbar icons
 | 
						||
  const toolbarSvg = colorize(base, COLOR_TOOLBAR);
 | 
						||
  for (const s of TOOLBAR_SIZES) {
 | 
						||
    const out = path.join(ICONS_DIR, `icon-${s}.png`);
 | 
						||
    rasterize(raster, toolbarSvg, s, out);
 | 
						||
    console.log(`Generated ${path.relative(REPO_ROOT, out)}`);
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
main();
 |