app
Adom Project Manager
Public Made by Adomby adom
Adom Project Manager — the central hub for navigating wiki boards, molecules and libraries into Hydrogen: browse your wiki boards/projects (Personal / Organization / Public), view symbol + footprint + 3D and the per-EDA footprint layer stackup, explore the board and schematic in INTERACTIVE cross-probing viewers (click a net, hover a part for stock and price), add components to your KiCad/Altium libraries, and deliver self-contained KiCad projects. Built on adom-lbr. (Formerly Adom Project Manag
← Commit history
v0.3.6: fix issue #139 — board 3D render now (1) stages project-local models (${KIPRJMOD}/3d/, plus a 3d/ folder for custom refs) so vendor STEPs resolve, and (2) includes DNP footprints (KiCad omits them by default, so a mis-flagged part rendered blank)
1 file changed
+35−5
src/main.rs+35−5@@ -1494,7 +1494,8 @@ fn render_board_glb(slug: &str) -> Option<Vec<u8>> { // machine-absolute Unix path (/home/…), a Windows path (C:\Users\…), OR a // project-relative path (<Board>.3dshapes/…). Handling all three means the 3D // renders regardless of how the source .kicad_pcb wrote its paths.- let mut models: std::collections::HashSet<String> = std::collections::HashSet::new();+ let mut models: std::collections::HashSet<String> = std::collections::HashSet::new(); // custom refs → 3d/<basename>+ let mut proj_models: std::collections::HashSet<String> = std::collections::HashSet::new(); // ${KIPRJMOD}/<rel> → fetched from the page at <rel> let mut out = String::with_capacity(pcb.len()); let mut rest = pcb.as_str(); while let Some(pos) = rest.find("(model \"") {@@ -1504,7 +1505,13 @@ fn render_board_glb(slug: &str) -> Option<Vec<u8>> { Some(end) => { let path = &after[..end]; if path.starts_with("${") {- // standard KiCad 3D model dir — the render service already has it+ // ${KIPRJMOD}/<rel> is PROJECT-LOCAL (a vendor STEP in the page's+ // 3d/ etc.), not a stock model — the file lives in the page repo, so+ // stage it. Other ${KICAD*_3DMODEL_DIR} refs are stock and resolve on+ // the render service.+ if let Some(rel) = path.replace('\\', "/").strip_prefix("${KIPRJMOD}/") {+ proj_models.insert(rel.to_string());+ } out.push_str(path); } else { let base = path.rsplit(['/', '\\']).next().unwrap_or(path);@@ -1517,19 +1524,42 @@ fn render_board_glb(slug: &str) -> Option<Vec<u8>> { } } out.push_str(rest);+ // Show DNP (Do-Not-Populate) parts in the 3D view. KiCad's 3D export omits DNP+ // footprints by default, so a part left/mis-flagged DNP (e.g. a main IC) renders+ // blank with no body — surprising in a design-browsing tool. Drop the `dnp` attr+ // token so every placed footprint gets its model.+ let out = out.replace(" dnp)", ")").replace(" dnp ", " "); // assemble the bundle: board.kicad_pcb (rewritten) + 3d/<custom models> let key = slug.replace('/', "_"); let bundle = format!("{SCRATCH}/glb-{key}"); let _ = std::fs::remove_dir_all(&bundle); std::fs::create_dir_all(format!("{bundle}/3d")).ok()?; std::fs::write(format!("{bundle}/board.kicad_pcb"), &out).ok()?;- if let Some(sd) = &shapes_dir {- for base in &models {- if let Some(mb) = wiki_bytes(slug, &format!("{sd}/{base}")) {+ // custom refs (absolute/relative) were rewritten to ${KIPRJMOD}/3d/<base>; find the+ // model on the page — it may live in a `.3dshapes` folder, a `3d/` folder, or at the+ // referenced relative path — and stage it under 3d/<base>.+ for base in &models {+ let candidates: Vec<String> = shapes_dir.iter().map(|sd| format!("{sd}/{base}"))+ .chain([format!("3d/{base}"), base.clone()])+ .collect();+ for cand in candidates {+ if let Some(mb) = wiki_bytes(slug, &cand) { let _ = std::fs::write(format!("{bundle}/3d/{base}"), mb);+ break; } } }+ // project-local ${KIPRJMOD}/<rel> models: fetch each from the page repo at <rel> and+ // stage it at the same relative path so ${KIPRJMOD} resolves inside the bundle.+ for rel in &proj_models {+ if let Some(mb) = wiki_bytes(slug, rel) {+ let dest = format!("{bundle}/{rel}");+ if let Some(parent) = std::path::Path::new(&dest).parent() {+ let _ = std::fs::create_dir_all(parent);+ }+ let _ = std::fs::write(&dest, mb);+ }+ } let targz = format!("{SCRATCH}/glb-{key}.tar.gz"); if !Command::new("tar").args(["-czf", &targz, "-C", &bundle, "."]).status().map(|s| s.success()).unwrap_or(false) { return None;