app
DigiKey Electronics Search
Public Made by Adomby adom
DigiKey component search in your workspace — millions of parts with real-time stock, quantity pricing and datasheets. CLI verbs, a Hydrogen app, and a shared backend service.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
//! Hydrogen tab lifecycle via `adom-cli hydrogen workspace`.
use base64::Engine;
use std::process::Command;
const WEBVIEW_PANEL_TYPE: &str = "adom/a1b2c3d4-0031-4000-a000-000000000031";
/// Substitute the current port into VSCODE_PROXY_URI so the webview
/// loads via the Coder proxy (same-origin with hydrogen.adom.inc).
/// Falls back to 127.0.0.1 for local testing when the env var is not
/// set — `adom-digikey app --no-tab` + `$ open http://127.0.0.1:8891`.
pub fn proxy_url(port: u16) -> String {
match std::env::var("VSCODE_PROXY_URI") {
Ok(tmpl) => tmpl.replace("{{port}}", &port.to_string()),
Err(_) => format!("http://127.0.0.1:{}/", port),
}
}
fn icon_data_url(icon_svg: &str) -> String {
let b64 = base64::engine::general_purpose::STANDARD.encode(icon_svg.as_bytes());
format!("data:image/svg+xml;base64,{}", b64)
}
pub fn add_webview_tab(name: &str, url: &str, icon_svg: &str) -> Result<(), String> {
// Find the focused leaf we should attach to.
let leaf = focused_leaf_for_tab()?;
let initial_state = serde_json::json!({ "url": url }).to_string();
let icon = icon_data_url(icon_svg);
let out = Command::new("adom-cli")
.args([
"hydrogen",
"workspace",
"add-tab",
"--panel-id",
&leaf,
"--panel-type",
WEBVIEW_PANEL_TYPE,
"--display-name",
name,
"--display-icon",
&icon,
"--initial-state",
&initial_state,
])
.output()
.map_err(|e| format!("adom-cli add-tab: {}", e))?;
if !out.status.success() {
return Err(format!(
"adom-cli add-tab failed: {}",
String::from_utf8_lossy(&out.stderr)
));
}
Ok(())
}
pub fn remove_webview_tab(name: &str) -> Result<(), String> {
let out = Command::new("adom-cli")
.args(["hydrogen", "workspace", "remove-tab", "--name", name])
.output()
.map_err(|e| format!("adom-cli remove-tab: {}", e))?;
if !out.status.success() {
return Err(format!(
"adom-cli remove-tab failed: {}",
String::from_utf8_lossy(&out.stderr)
));
}
Ok(())
}
/// Pick a leaf panel that is NOT the VS Code editor, so we don't cover
/// the user's code view. Walk the workspace tree, find nodes with
/// `type: "leaf"`, check whether any tab in the leaf is a VS Code
/// editor (`...eeee-4000-a000-...`). Return the first non-editor
/// leaf's id. Falls back to the focused panel id if nothing else fits.
fn focused_leaf_for_tab() -> Result<String, String> {
let out = Command::new("adom-cli")
.args(["hydrogen", "workspace", "get"])
.output()
.map_err(|e| format!("adom-cli workspace get: {}", e))?;
if !out.status.success() {
return Err(format!("adom-cli workspace get failed: {}", String::from_utf8_lossy(&out.stderr)));
}
let v: serde_json::Value = serde_json::from_slice(&out.stdout)
.map_err(|e| format!("parse workspace JSON: {}", e))?;
let mut leaves: Vec<LeafInfo> = Vec::new();
walk_leaves(&v, &mut leaves);
// Prefer leaves that contain no VS Code editor tabs.
let non_editor: Vec<&LeafInfo> = leaves.iter().filter(|l| !l.has_editor).collect();
if let Some(l) = non_editor.first() {
return Ok(l.id.clone());
}
if let Some(id) = v.get("focusedPanelId").and_then(|v| v.as_str()) {
return Ok(id.to_string());
}
leaves
.first()
.map(|l| l.id.clone())
.ok_or_else(|| "no panels found in workspace".into())
}
struct LeafInfo {
id: String,
has_editor: bool,
}
fn walk_leaves(v: &serde_json::Value, out: &mut Vec<LeafInfo>) {
if let Some(obj) = v.as_object() {
if obj.get("type").and_then(|v| v.as_str()) == Some("leaf") {
if let Some(id) = obj.get("id").and_then(|v| v.as_str()) {
let tabs = obj.get("tabs").and_then(|v| v.as_array());
let has_editor = tabs
.map(|arr| {
arr.iter().any(|t| {
let pt = t.get("panelType").and_then(|v| v.as_str()).unwrap_or("");
// VS Code editor panel UUID has "-eeee-4000-a000-"
// or "-0030-4000-a000-". Web View is "-0031-".
pt.contains("eeee-4000") || pt.contains("0030-4000")
})
})
.unwrap_or(false);
out.push(LeafInfo { id: id.to_string(), has_editor });
}
}
for (_, child) in obj {
walk_leaves(child, out);
}
} else if let Some(arr) = v.as_array() {
for c in arr {
walk_leaves(c, out);
}
}
}