From a1cc631406643e83343f2b260053abe34b0a969b Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Fri, 20 Mar 2026 14:04:42 +0100 Subject: [PATCH] fix: component port labels showing wrong name after editing blueprint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The label lookup in drawComponentGate read from gate.component (potentially stale copy) while gateOutputCount read from state.customComponents (updated definition), causing a mismatch — fewer ports but old outputIds, so the first (deleted) output's label was shown instead of the surviving one. Three fixes: - renderer: use customComponents as authoritative source for label lookup - saveLoad: re-link gate.component refs to customComponents after loading - components: update existing instances even when a "new" component overwrites an existing definition with the same name Co-Authored-By: Claude Opus 4.6 --- js/components.js | 9 ++++++--- js/renderer.js | 6 ++++-- js/saveLoad.js | 10 ++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/js/components.js b/js/components.js index e801efd..dbacdd2 100644 --- a/js/components.js +++ b/js/components.js @@ -285,12 +285,15 @@ export function exitComponentEditor(name, shouldSave) { // Save the component (works for both new and edited) const result = saveComponentFromCircuit(name); - // If editing an existing component, update all placed instances in the main circuit - if (editingId && result.success && state.savedMainCircuit) { + // Update all placed instances of this component in the main circuit. + // Handles both: editing existing component (editingId matches) AND + // creating a "new" component that overwrites an existing one (same sanitized name). + if (result.success && state.savedMainCircuit) { const updatedComp = state.customComponents[result.component.id]; if (updatedComp) { + const matchId = editingId || result.component.id; for (const gate of state.savedMainCircuit.gates) { - if (gate.component && gate.component.id === editingId) { + if (gate.component && gate.component.id === matchId) { gate.component = updatedComp; // Clear persisted internal state so it re-initializes from updated blueprint delete gate._internalGates; diff --git a/js/renderer.js b/js/renderer.js index 88b62a1..a1b1259 100644 --- a/js/renderer.js +++ b/js/renderer.js @@ -325,8 +325,10 @@ function drawComponentGate(gate) { ctx.fillStyle = '#444'; ctx.fillText(getGateLabel(gate), gate.x + w / 2, gate.y + h - 6); - // Get port labels from the component definition - const comp = gate.component; + // Get port labels from the authoritative component definition (customComponents) + // This must match the source used by gateOutputCount/gateInputCount for port counts + const compId = gate.type.substring(10); + const comp = state.customComponents?.[compId] || gate.component; const inputLabels = []; const outputLabels = []; if (comp) { diff --git a/js/saveLoad.js b/js/saveLoad.js index 26fd6bf..b2396b6 100644 --- a/js/saveLoad.js +++ b/js/saveLoad.js @@ -56,6 +56,16 @@ export function loadState(data) { state.customComponents = JSON.parse(JSON.stringify(data.components)); } + // Re-link gate.component references to customComponents (authoritative source) + for (const gate of state.gates) { + if (gate.type.startsWith('COMPONENT:')) { + const compId = gate.type.substring(10); + if (state.customComponents[compId]) { + gate.component = state.customComponents[compId]; + } + } + } + // Load progress if (data.progress) { progress.unlockedLevels = data.progress.unlockedLevels || ['buffer'];