// Left rail — the "inventory". Filterable component palette.
window.Inventory = function Inventory({
nodes,
resolved,
selected,
onToggleSelect,
onHoverNode,
}) {
const [filter, setFilter] = React.useState("all");
const [q, setQ] = React.useState("");
const selectedSet = React.useMemo(() => new Set(selected), [selected]);
// Group nodes by type, ordered.
const typeOrder = ["optimizer", "scheduler", "technique", "quantization", "architecture", "inference"];
const counts = React.useMemo(() => {
const c = { all: nodes.length };
for (const t of typeOrder) c[t] = 0;
for (const n of nodes) c[n.type] = (c[n.type] || 0) + 1;
return c;
}, [nodes]);
const filtered = React.useMemo(() => {
const lq = q.trim().toLowerCase();
return nodes.filter(n => {
if (filter !== "all" && n.type !== filter) return false;
if (lq && !(n.name.toLowerCase().includes(lq) || n.canonical.includes(lq))) return false;
return true;
});
}, [nodes, filter, q]);
const grouped = React.useMemo(() => {
const g = {};
for (const n of filtered) (g[n.type] = g[n.type] || []).push(n);
return g;
}, [filtered]);
return (
Inventory
{nodes.length} components
{typeOrder.map(t => (
counts[t] > 0 && (
)
))}
setQ(e.target.value)}
/>
{typeOrder.map(t => {
const list = grouped[t];
if (!list || list.length === 0) return null;
return (
{window.TYPE_META[t].label}
{list.map(n => {
const r = resolved[n.canonical] || {};
const isSelected = selectedSet.has(n.canonical);
const cls = [
"inv-row",
`tier-${n.tier}`,
isSelected ? "selected" : "",
r.status === "blocked" ? "blocked" : "",
r.status === "conditional" ? "dim" : "",
].filter(Boolean).join(" ");
const meta = window.TYPE_META[n.type];
const metaText =
isSelected ? "equipped"
: r.status === "blocked" ? `blocked · ${r.reasons[0]?.label || "breaks"}`
: r.status === "conditional" ? (r.reasons[0]?.label || "awaiting")
: null;
return (
onToggleSelect(n.canonical)}
onMouseEnter={e => onHoverNode(n, e)}
onMouseLeave={() => onHoverNode(null)}
>
{n.name}
{metaText ? (
{metaText}
) : (
{n.canonical}
)}
T{n.tier}
);
})}
);
})}
{filtered.length === 0 && (
no components match
)}
);
};