app
Parts Search
Public Made by Adomby adom
One search box across Mouser, DigiKey and JLCPCB: parallel queries, side-by-side product photos, and a Mouser-preferred recommendation that reasons around stock and lead time.
← Commit history
v2.0.18: Module 2.4.0: the panel gutter check now measures against the containing block
8 files changed
+64−94
Cargo.lock+1−1@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-parts-search"-version = "2.0.17"+version = "2.0.18" dependencies = [ "base64", "clap",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-parts-search"-version = "2.0.17"+version = "2.0.18" edition = "2021" description = "Parts Search Electronics component search — CLI, backend HTTP service, and Hydrogen webview app" license = "MIT"
VERSION+1−1@@ -1 +1 @@-2.0.17+2.0.18
install.shpackage.json+2−41@@ -1,6 +1,6 @@ { "slug": "adom-parts-search",- "version": "2.0.18",+ "version": "2.0.4", "type": "app", "description": "One search box across Mouser, DigiKey and JLCPCB. Parallel queries, side-by-side product photos, and a Mouser-preferred recommendation that reasons around stock and lead time. Dock Bar ready: serve, health, settings, Say it.", "dependencies": {@@ -38,44 +38,5 @@ "SKILL.md", "BUILD-SKILL.md", "CHANGELOG.md"- ],- "changelog": "Module 2.4.0: the panel gutter check now measures against the containing block",- "discovery_triggers": [- "parts search",- "find part",- "source component",- "source part",- "compare distributors",- "compare vendors",- "Mouser or DigiKey",- "BOM sourcing",- "price this part",- "where can I get",- "check stock",- "parts availability",- "fastest delivery",- "cheapest part",- "component search",- "part lookup",- "price breaks"- ],- "discovery_pitch": "Search Mouser, DigiKey and JLCPCB in parallel with product photos; Mouser-preferred best-source pick. Say 'with parts search' to your AI.",- "sample_prompts": [- {- "label": "source an STM32F103RBT6",- "prompt": "source an STM32F103RBT6"- },- {- "label": "where can I get an ESP32-S3-WROOM cheape",- "prompt": "where can I get an ESP32-S3-WROOM cheapest and fastest"- },- {- "label": "compare Mouser, DigiKey and JLCPCB for a",- "prompt": "compare Mouser, DigiKey and JLCPCB for an LM555"- },- {- "label": "price this BOM across distributors",- "prompt": "price this BOM across distributors"- } ]-}+}
page.json+9−50@@ -4,7 +4,7 @@ "slug": "adom-parts-search", "title": "Parts Search", "brief": "One search box across Mouser, DigiKey and JLCPCB: parallel queries, side-by-side product photos, and a Mouser-preferred recommendation that reasons around stock and lead time.",- "version": "2.0.18",+ "version": "2.0.4", "install": { "binary_name": "adom-parts-search", "install_dir": "",@@ -33,25 +33,13 @@ }, "hero": { "type": "image",- "path": "screenshots/hero.png"+ "path": "docs/hero.png" }, "sample_prompts": [- {- "label": "source an STM32F103RBT6",- "prompt": "source an STM32F103RBT6"- },- {- "label": "where can I get an ESP32-S3-WROOM cheape",- "prompt": "where can I get an ESP32-S3-WROOM cheapest and fastest"- },- {- "label": "compare Mouser, DigiKey and JLCPCB for a",- "prompt": "compare Mouser, DigiKey and JLCPCB for an LM555"- },- {- "label": "price this BOM across distributors",- "prompt": "price this BOM across distributors"- }+ "source an STM32F103RBT6",+ "where can I get an ESP32-S3-WROOM cheapest and fastest",+ "compare Mouser, DigiKey and JLCPCB for an LM555",+ "price this BOM across distributors" ], "discovery_triggers": [ "parts search",@@ -66,41 +54,12 @@ "where can I get", "check stock", "parts availability",- "fastest delivery",- "cheapest part",- "component search",- "part lookup",- "price breaks"+ "fastest delivery" ],- "discovery_pitch": "Search Mouser, DigiKey and JLCPCB in parallel with product photos; Mouser-preferred best-source pick. Say 'with parts search' to your AI.",+ "discovery_pitch": "Search Mouser + DigiKey + JLCPCB in parallel from one interface, with product photos and a Mouser-preferred best-source pick.", "metadata": { "platform": "linux" }, "category": "tools",- "org": "adom",- "description": "One search box across Mouser, DigiKey and JLCPCB. Parallel queries, side-by-side product photos, and a Mouser-preferred recommendation that reasons around stock and lead time. Dock Bar ready: serve, health, settings, Say it.",- "dependencies": {- "adom/adom-mouser": "*",- "adom/adom-digikey": "*",- "adom/adom-jlcpcb": "*"- },- "needs_sudo": false,- "scripts": {- "install": "./install.sh",- "uninstall": "./uninstall.sh"- },- "files": [- "bin/**",- "skills/**",- "docs/icon.svg",- "dockbar.json",- "install.sh",- "uninstall.sh",- "package.json",- "README.md",- "SKILL.md",- "BUILD-SKILL.md",- "CHANGELOG.md"- ],- "changelog": "Module 2.4.0: the panel gutter check now measures against the containing block"+ "org": "adom" }
src/vendors.rs+50@@ -816,7 +816,35 @@ fn pick_cheapest(agg: &AggregatedResults, query: &str) -> Option<Recommendation> /// vendor beats the wrong part from the first) /// 3. a variant, FLAGGED as a substitution, never silent /// A family/keyword query has no exact hit and keeps the vendor-order policy.+/// The Mouser-preferred policy only recommends a distributor that ships parts+/// to you (Mouser, then DigiKey); JLCPCB stock feeds their assembly line. So a+/// substitution there must say WHICH vendors lacked the part, and must not hide+/// that JLCPCB has the exact one. 2026-10-06: TPS2553DBVR read "not available in+/// stock from the vendors that answered" while JLCPCB answered with 44,248.+fn distributor_substitution(rec: Option<Recommendation>, agg: &AggregatedResults, query: &str) -> Option<Recommendation> {+ let mut rec = rec?;+ if rec.substitution.is_none() { return Some(rec); }+ let q = norm_mpn(query);+ let mut text = format!(+ "You asked for {} but it is not in stock at Mouser or DigiKey. {} is a DIFFERENT orderable \+ part (packaging or grade variant): check its own datasheet before substituting.",+ query.trim(), rec.mpn+ );+ if let Some(j) = agg.jlcpcb.hits.iter().find(|h| h.stock > 0 && norm_mpn(&h.mpn) == q) {+ text.push_str(&format!(+ " The exact part IS in stock at JLCPCB ({} {}, {} in stock): use that if JLCPCB is assembling the board.",+ j.vendor_pn, j.mpn, j.stock+ ));+ }+ rec.substitution = Some(text);+ Some(rec)+}+ fn pick_winner(agg: &AggregatedResults, query: &str) -> Option<Recommendation> {+ distributor_substitution(pick_winner_inner(agg, query), agg, query)+}++fn pick_winner_inner(agg: &AggregatedResults, query: &str) -> Option<Recommendation> { let long_lead = |lt: &str| lt.contains("280"); let exact_query = is_exact_query(agg, query); let q = norm_mpn(query);@@ -928,6 +956,28 @@ mod recommendation_tests { assert!(r.reason.contains("SUBSTITUTION")); } + /// 2026-10-06, live: TPS2553DBVR was out of stock at Mouser and DigiKey+ /// but in stock at JLCPCB, and the note claimed no vendor that answered had+ /// it. The distributor pick stays a distributor; the note must be accurate+ /// and must point at JLCPCB's exact part.+ #[test]+ fn substitution_names_the_distributors_and_points_at_jlcpcb_stock() {+ let mut a = agg("TPS2553DBVR", vec![+ hit(Vendor::Mouser, "TPS2553DBVR-1", 11183, "$0.90"),+ hit(Vendor::Mouser, "TPS2553DBVR", 0, "$0.85"),+ ], vec![hit(Vendor::DigiKey, "TPS2553DBVR", 0, "$0.88")]);+ let mut j = hit(Vendor::JLCPCB, "TPS2553DBVR", 44248, "$0.40");+ j.vendor_pn = "C61101".into();+ a.jlcpcb.hits = vec![j];+ let r = pick_winner(&a, &a.query).unwrap();+ assert_eq!(r.winner, Vendor::Mouser);+ assert_eq!(r.mpn, "TPS2553DBVR-1");+ let note = r.substitution.expect("still a flagged substitution");+ assert!(note.contains("not in stock at Mouser or DigiKey"), "{note}");+ assert!(!note.contains("vendors that answered"), "the false claim is gone: {note}");+ assert!(note.contains("IS in stock at JLCPCB") && note.contains("C61101") && note.contains("44248"), "{note}");+ }+ /// A family/keyword query matches no hit exactly and keeps the old /// vendor-order behaviour, unflagged. #[test]
uninstall.sh