Fix wire property persistence and enable server auto-reload

Wire options (twisted pair, shielded, stripe, bundle) were not
persisting because the backend server had stale Python modules loaded
from before the WireUpdate schema was updated with these fields.
Pydantic silently ignored unrecognized fields, so only pre-existing
fields like color_stripe were saved.

Frontend fixes applied alongside the schema work:
- _patchWire now shows visible error on save failure instead of
  swallowing errors and falsely flashing Saved
- Bundle dropdown rebuild is guarded with _updatingBundleDropdown flag
  to prevent spurious change events from triggering unwanted PATCHes
- Enable uvicorn reload=True so server auto-restarts on code changes,
  preventing stale module issues in the future

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 14:57:36 -04:00
parent f32203b630
commit 0e507a95d3
2 changed files with 27 additions and 3 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
import uvicorn import uvicorn
if __name__ == "__main__": if __name__ == "__main__":
uvicorn.run("app.main:app", host="127.0.0.1", port=8000, reload=False) uvicorn.run("app.main:app", host="127.0.0.1", port=8000, reload=True)
+25 -1
View File
@@ -10,6 +10,7 @@ class WiringApp {
this._preDrag = null; // snapshot captured at dragstart for undo this._preDrag = null; // snapshot captured at dragstart for undo
this._octopartReady = null; // null=unknown, true/false after status check this._octopartReady = null; // null=unknown, true/false after status check
this._bundles = []; // wire bundles for current diagram this._bundles = []; // wire bundles for current diagram
this._updatingBundleDropdown = false;
this.canvas = new DiagramCanvas("canvas-container", { this.canvas = new DiagramCanvas("canvas-container", {
onDeviceSelected: (d) => this._showDeviceProps(d), onDeviceSelected: (d) => this._showDeviceProps(d),
@@ -62,11 +63,26 @@ class WiringApp {
this._savedTimer = setTimeout(() => { this._savedTimer = setTimeout(() => {
const el = document.getElementById("saved-indicator"); const el = document.getElementById("saved-indicator");
if (!el) return; if (!el) return;
el.textContent = "✓ Saved";
el.style.color = "";
el.classList.add("show"); el.classList.add("show");
setTimeout(() => el.classList.remove("show"), 1400); setTimeout(() => el.classList.remove("show"), 1400);
}, 600); }, 600);
} }
_flashError() {
clearTimeout(this._savedTimer);
const el = document.getElementById("saved-indicator");
if (!el) return;
el.textContent = "✗ Save failed";
el.style.color = "#e06c6c";
el.classList.add("show");
this._savedTimer = setTimeout(() => {
el.classList.remove("show");
el.style.color = "";
}, 3000);
}
// ── Undo ────────────────────────────────────────────────────────────────────── // ── Undo ──────────────────────────────────────────────────────────────────────
_pushUndo(fn) { _pushUndo(fn) {
@@ -599,6 +615,7 @@ class WiringApp {
}); });
document.getElementById("wire-bundle-select")?.addEventListener("change", (e) => { document.getElementById("wire-bundle-select")?.addEventListener("change", (e) => {
if (this._updatingBundleDropdown) return;
const bundleId = e.target.value ? parseInt(e.target.value) : null; const bundleId = e.target.value ? parseInt(e.target.value) : null;
this._patchWire({ bundle_id: bundleId }, true); this._patchWire({ bundle_id: bundleId }, true);
this._refreshBundleEditPanel(bundleId); this._refreshBundleEditPanel(bundleId);
@@ -708,8 +725,13 @@ class WiringApp {
Object.assign(w, data); Object.assign(w, data);
if (redraw) this.canvas.updateWire(w); if (redraw) this.canvas.updateWire(w);
} }
await api.wires.update(id, data).catch(e => console.error("Wire save failed:", e)); try {
await api.wires.update(id, data);
this._flashSaved(); this._flashSaved();
} catch (e) {
console.error("Wire save failed:", e);
this._flashError();
}
} }
// ── Diagram management ──────────────────────────────────────────────────────── // ── Diagram management ────────────────────────────────────────────────────────
@@ -1338,6 +1360,7 @@ class WiringApp {
_refreshBundleDropdown(selectedId) { _refreshBundleDropdown(selectedId) {
const sel = document.getElementById("wire-bundle-select"); const sel = document.getElementById("wire-bundle-select");
if (!sel) return; if (!sel) return;
this._updatingBundleDropdown = true;
sel.innerHTML = '<option value="">— none —</option>'; sel.innerHTML = '<option value="">— none —</option>';
this._bundles.forEach(b => { this._bundles.forEach(b => {
const opt = document.createElement("option"); const opt = document.createElement("option");
@@ -1346,6 +1369,7 @@ class WiringApp {
if (b.id === selectedId) opt.selected = true; if (b.id === selectedId) opt.selected = true;
sel.appendChild(opt); sel.appendChild(opt);
}); });
this._updatingBundleDropdown = false;
} }
_refreshBundleEditPanel(bundleId) { _refreshBundleEditPanel(bundleId) {