diff --git a/js/nodes_stats.js b/js/nodes_stats.js
index a8dc341..c12de68 100644
--- a/js/nodes_stats.js
+++ b/js/nodes_stats.js
@@ -88,6 +88,7 @@ async function showStatsDialog() {
const considerRemoving = custom.filter((p) => p.status === "consider_removing");
const unusedNew = custom.filter((p) => p.status === "unused_new");
const used = custom.filter((p) => p.status === "used");
+ const uninstalled = custom.filter((p) => p.status === "uninstalled");
let html = `
Node Package Stats
@@ -133,6 +134,11 @@ async function showStatsDialog() {
html += buildTable(used, "used");
}
+ if (uninstalled.length > 0) {
+ html += sectionHeader("Uninstalled", "Previously tracked, no longer installed", "#555");
+ html += buildTable(uninstalled, "uninstalled");
+ }
+
dialog.innerHTML = html;
overlay.appendChild(dialog);
document.body.appendChild(overlay);
@@ -168,6 +174,7 @@ const STATUS_COLORS = {
consider_removing: { bg: "#2a2215", hover: "#3a2e20" },
unused_new: { bg: "#1a1a25", hover: "#252530" },
used: { bg: "#151a15", hover: "#202a20" },
+ uninstalled: { bg: "#1a1a1a", hover: "#252525" },
};
function buildTable(packages, status) {
diff --git a/tracker.py b/tracker.py
index ae727af..bd13b42 100644
--- a/tracker.py
+++ b/tracker.py
@@ -130,10 +130,12 @@ class UsageTracker:
}
packages[pkg]["total_nodes"] = total
- # For packages that came only from DB (e.g. uninstalled), fill total_nodes
+ # Packages only in DB (not in mapper) are uninstalled/disabled
+ installed_packages = set(node_counts.keys()) | mapper.get_all_packages()
for pkg, entry in packages.items():
if "total_nodes" not in entry:
entry["total_nodes"] = entry["used_nodes"]
+ entry["installed"] = pkg in installed_packages
# Classify packages by usage recency
now = datetime.now(timezone.utc)
@@ -142,7 +144,9 @@ class UsageTracker:
tracking_start = self._get_first_prompt_time()
for entry in packages.values():
- if entry["total_executions"] > 0:
+ if not entry["installed"]:
+ entry["status"] = "uninstalled"
+ elif entry["total_executions"] > 0:
# Used packages: classify by last_seen recency
if entry["last_seen"] < two_months_ago:
entry["status"] = "safe_to_remove"