app
Adom Chip Thumbnailer
Public Made by Adomby adom
The chip-icon factory: from a STEP file it renders the complete family of chip icons, shaded 3D orientations, transparent icons, name-lasered variants, and 11 vector outline styles, in a live web app
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
//! Footprint thumbnail: shell to local kicad-cli, then apply the
//! footprint theme (dark canvas + PCB grid behind kicad-cli's native
//! KiCad-palette geometry).
//!
//! kicad-cli's footprint SVG export expects a `.pretty/` directory and
//! `--footprint <name>` flag, *not* a single .kicad_mod file. We stage
//! a temp `.pretty/` symlink-free directory.
use crate::library::ChipPaths;
use crate::theme;
use anyhow::{anyhow, Context, Result};
use std::path::Path;
use std::process::Command;
pub fn render(chip: &ChipPaths) -> Result<()> {
// Prefer the ds2sf-rendered authoritative footprint SVG when present
// (validate.rs writes <MPN>-footprint.authoritative.svg via service-kicad).
// Adom containers have no local kicad-cli, so this is the path that
// actually works; the kicad-cli branch below is a dev-only fallback.
let authoritative = chip
.dir
.join(format!("{}-footprint.authoritative.svg", chip.mpn));
if authoritative.is_file() {
let raw = std::fs::read_to_string(&authoritative)
.with_context(|| format!("reading {}", authoritative.display()))?;
// Apply the Adom footprint theme (dark canvas + PCB grid behind the
// KiCad-palette pads), same as the kicad-cli path did — otherwise the
// tile is a raw light render that clashes with the symbol/3D tiles.
let themed = theme::theme_footprint_svg(&raw);
std::fs::write(chip.footprint_thumb(), themed)
.with_context(|| format!("writing {}", chip.footprint_thumb().display()))?;
return Ok(());
}
let kicad_mod = chip
.kicad_mod
.as_ref()
.ok_or_else(|| anyhow!("no .kicad_mod in {}", chip.dir.display()))?;
let pretty = tempdir(&format!("chip-thumb-fp-{}-pretty", chip.mpn))?;
let staged_mod = pretty.join(format!("{}.kicad_mod", chip.mpn));
std::fs::copy(kicad_mod, &staged_mod)
.with_context(|| format!("staging {} → {}", kicad_mod.display(), staged_mod.display()))?;
let out_dir = tempdir(&format!("chip-thumb-fp-{}-out", chip.mpn))?;
let status = Command::new("kicad-cli")
.arg("fp")
.arg("export")
.arg("svg")
.arg("--footprint")
.arg(&chip.mpn)
.arg("--output")
.arg(&out_dir)
.arg(&pretty)
.status()
.context("running kicad-cli — install KiCad to PATH if missing")?;
if !status.success() {
return Err(anyhow!("kicad-cli fp export svg failed for {}", chip.mpn));
}
let exported = pick_first_svg(&out_dir)
.ok_or_else(|| anyhow!("kicad-cli produced no .svg in {}", out_dir.display()))?;
let raw = std::fs::read_to_string(&exported)
.with_context(|| format!("reading {}", exported.display()))?;
let themed = theme::theme_footprint_svg(&raw);
let out = chip.footprint_thumb();
std::fs::write(&out, themed)
.with_context(|| format!("writing {}", out.display()))?;
let _ = std::fs::remove_dir_all(&pretty);
let _ = std::fs::remove_dir_all(&out_dir);
Ok(())
}
fn pick_first_svg(dir: &Path) -> Option<std::path::PathBuf> {
if let Ok(read) = std::fs::read_dir(dir) {
for entry in read.flatten() {
let p = entry.path();
if p.extension().and_then(|e| e.to_str()) == Some("svg") {
return Some(p);
}
}
}
None
}
fn tempdir(prefix: &str) -> Result<std::path::PathBuf> {
let base = std::env::temp_dir().join(format!("{prefix}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&base);
std::fs::create_dir_all(&base)?;
Ok(base)
}