feat(nam): download 10 new A2 models for bass, orange, and other missing categories

Bank 7 — Bass: Ampeg PF20T, Aguilar Tone Hammer 500, GK 800RB (Gallien-Krueger), Aguilar DB751
Bank 7 — Orange: Orange Rocker 30 Dirty + Natural
Bank 7 — Others: Fender Bassman dimed, Hiwatt DR103 Bright + Cornish, Supro Black Magick M3

All SlimmableContainer v0.7.0, ~287-295 KB, verified JSON.
Also fixed Bank 2 Tone3000 storage keys to include .nam extension (Tone3000 changed URL format).
Inventory updated: 54 → 64 total models.
This commit is contained in:
2026-06-13 02:09:56 -04:00
parent 11c838a27e
commit 5c51b3db28
164 changed files with 22831 additions and 577 deletions
+125 -18
View File
@@ -497,6 +497,49 @@ function FXBlock({ title, type, active = true, bypassed = false, params = [], on
);
}
// ── Signal Arrow Connector ──────────────────────────────────
function SignalArrow({ active = true, variant = 'right', animated = true }) {
const color = active ? T.green : T.textDim;
if (variant === 'right') {
return (
<svg width="22" height="14" viewBox="0 0 22 14" style={{ flexShrink: 0, display: 'block' }}>
<line x1="2" y1="7" x2="14" y2="7" stroke={color} strokeWidth="2" strokeLinecap="round" />
<polygon points="12,3 20,7 12,11" fill={color} />
{active && animated && (
<circle r="1.5" fill={color} opacity="0.6">
<animate attributeName="cx" values="4;18" dur="1.2s" repeatCount="indefinite" />
</circle>
)}
</svg>
);
}
if (variant === 'split') {
return (
<svg width="26" height="24" viewBox="0 0 26 24" style={{ flexShrink: 0, display: 'block' }}>
<line x1="2" y1="10" x2="14" y2="10" stroke={color} strokeWidth="2" strokeLinecap="round" />
<line x1="8" y1="10" x2="18" y2="18" stroke={color} strokeWidth="2" strokeLinecap="round" />
<polygon points="12,6 18,10 12,14" fill={color} />
<polygon points="16,14 21,20 19,13" fill={color} />
</svg>
);
}
if (variant === 'merge') {
return (
<svg width="26" height="24" viewBox="0 0 26 24" style={{ flexShrink: 0, display: 'block' }}>
<line x1="2" y1="4" x2="12" y2="12" stroke={color} strokeWidth="2" strokeLinecap="round" />
<line x1="2" y1="20" x2="12" y2="12" stroke={color} strokeWidth="2" strokeLinecap="round" />
<line x1="12" y1="12" x2="14" y2="12" stroke={color} strokeWidth="2" strokeLinecap="round" />
<polygon points="12,8 18,12 12,16" fill={color} />
</svg>
);
}
return null;
}
// ── Slide Panel ──────────────────────────────────────────────
function SlidePanel({ children, onClose }) {
return (
@@ -634,15 +677,38 @@ function RigScreen({ state, connected, refresh }) {
<div className="card-sm">
<div className="section-label">Signal Chain</div>
<div style={{ display: "flex", alignItems: "center", gap: 4, overflowX: "auto", padding: "4px 0" }}>
{["IN", state?.bypass ? "BYP" : (state?.nam_loaded ? "NAM" : "AMP"), state?.ir_loaded ? "IR" : "CAB", "OUT"].map((s, i, arr) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 4, flexShrink: 0 }}>
<div style={{ padding: "3px 8px", borderRadius: 4, fontSize: 10, fontWeight: 700,
background: i === 0 || i === arr.length - 1 ? T.blueDim : T.amberDim,
color: i === 0 || i === arr.length - 1 ? T.blue : T.amber,
border: `1px solid ${(i === 0 || i === arr.length - 1 ? T.blue : T.amber)}60` }}>{s}</div>
{i < arr.length - 1 && <div style={{ width: 16, height: 2, background: T.border, borderRadius: 1 }} />}
</div>
))}
{(() => {
const chain = ["IN", state?.bypass ? "BYP" : (state?.nam_loaded ? "NAM" : "AMP"), state?.ir_loaded ? "IR" : "CAB", "OUT"];
const is4CM = state?.routing_mode === '4cm';
const bp = state?.routing_breakpoint || 7;
const active = !state?.bypass;
return chain.map((s, i, arr) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 4, flexShrink: 0 }}>
{is4CM && s === "AMP" && (
<SignalArrow variant="split" active={active} />
)}
{is4CM && s === "CAB" && (
<SignalArrow variant="merge" active={active} />
)}
<div style={{ padding: "3px 8px", borderRadius: 4, fontSize: 10, fontWeight: 700,
background: i === 0 || i === arr.length - 1 ? T.blueDim : (is4CM && s === "AMP" ? "rgba(58,184,122,.15)" : T.amberDim),
color: i === 0 || i === arr.length - 1 ? T.blue : (is4CM && s === "AMP" ? T.green : T.amber),
border: `1px solid ${(i === 0 || i === arr.length - 1 ? T.blue : (is4CM && s === "AMP" ? T.green : T.amber))}60` }}>
{s}
{is4CM && s === "AMP" && <span style={{ fontSize: 8, marginLeft: 4, opacity: 0.7 }}>4CM</span>}
{is4CM && s === "BYP" && <span style={{ fontSize: 8, marginLeft: 4, opacity: 0.7 }}>4CM</span>}
</div>
{is4CM && s === "AMP" && (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", flexShrink: 0, gap: 1 }}>
<SignalArrow variant="split" active={active} />
<span style={{ fontSize: 7, color: T.textDim, letterSpacing: '.05em', lineHeight: '1' }}>LOOP</span>
<SignalArrow variant="merge" active={active} />
</div>
)}
{i < arr.length - 1 && !(is4CM && s === "AMP") && <SignalArrow active={active} />}
</div>
));
})()}
</div>
</div>
@@ -782,15 +848,56 @@ function FXScreen({ state, connected }) {
{/* Signal path */}
{fxList && fxList.length > 0 && (
<div style={{ display: "flex", alignItems: "center", gap: 4, padding: "4px 2px", overflowX: "auto" }}>
{["IN", ...fxList.map(f => f.type.toUpperCase()), "OUT"].map((s, i, arr) => (
<div key={i} style={{ display: "flex", alignItems: "center", gap: 4, flexShrink: 0 }}>
<div style={{ padding: "3px 8px", borderRadius: 4, fontSize: 10, fontWeight: 700,
background: s === "IN" || s === "OUT" ? T.blueDim : T.amberDim,
color: s === "IN" || s === "OUT" ? T.blue : T.amber,
border: `1px solid ${(s === "IN" || s === "OUT" ? T.blue : T.amber)}60` }}>{s}</div>
{i < arr.length - 1 && <div style={{ width: 12, height: 2, background: T.border, borderRadius: 1 }} />}
</div>
))}
{(() => {
const is4CM = state?.routing_mode === '4cm';
const bp = is4CM ? (state?.routing_breakpoint || 7) : -1;
const items = [];
// Build chain items array with connector metadata
items.push({ label: "IN", type: "io", idx: -1 });
fxList.forEach((f, idx) => {
if (is4CM && idx === bp) {
items.push({ label: null, type: "merge-bp", idx });
}
items.push({ label: f.type.toUpperCase(), type: "fx", idx, bypassed: f.bypassed });
if (is4CM && idx === bp - 1) {
items.push({ label: null, type: "split-bp", idx });
}
});
items.push({ label: "OUT", type: "io", idx: -1 });
return items.map((item, i) => {
if (item.type === 'split-bp') {
return (
<div key={`bp-${i}`} style={{ display: "flex", flexDirection: "column", alignItems: "center", flexShrink: 0, gap: 1 }}>
<SignalArrow variant="split" active={true} />
<span style={{ fontSize: 7, color: T.textDim, letterSpacing: '.05em', lineHeight: '1' }}>4CM</span>
<SignalArrow variant="merge" active={true} />
</div>
);
}
if (item.type === 'merge-bp') {
// Already rendered as part of the split-bp composite — skip
return null;
}
return (
<div key={`${item.type}-${i}`} style={{ display: "flex", alignItems: "center", gap: 4, flexShrink: 0 }}>
{item.type === 'io' && (
<div style={{ padding: "3px 8px", borderRadius: 4, fontSize: 10, fontWeight: 700,
background: T.blueDim, color: T.blue, border: `1px solid ${T.blue}60` }}>{item.label}</div>
)}
{item.type === 'fx' && (
<div style={{ padding: "3px 8px", borderRadius: 4, fontSize: 10, fontWeight: 700,
background: T.amberDim, color: item.bypassed ? T.textDim : T.amber,
border: `1px solid ${(item.bypassed ? T.textDim : T.amber)}60`, opacity: item.bypassed ? 0.5 : 1 }}>{item.label}</div>
)}
{/* Connector after this block */}
{i < items.length - 1 && items[i+1]?.type !== 'split-bp' && items[i]?.type !== 'merge-bp' && (
<SignalArrow active={item.type === 'io' || !item.bypassed} />
)}
</div>
);
});
})()}
</div>
)}
{(!fxList || fxList.length === 0) ? (