From a83a85e958ed90848acfdc2f85733f087e7713e4 Mon Sep 17 00:00:00 2001 From: znetsixe Date: Thu, 28 May 2026 19:21:59 +0200 Subject: [PATCH] fix(ps): persist stopLevel/holdLevel as numbers across editor save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node-RED's auto-form-binding writes values into the node object as strings. The editor's setNumberField helper used strict Number.isFinite(val) which rejects "0.5" and blanked the input on reopen, so users saw their stopLevel/holdLevel values disappear after clicking Done. - oneditsave: explicitly parseFloat stopLevel, holdLevel, and deadZoneKeepAlivePercent so they land in the node as numbers (matches the treatment of startLevel/maxLevel). - oneditprepare: parseFloat node.holdLevel / node.deadZoneKeepAlivePercent before the Number.isFinite check so existing string-typed flows still render their saved values. - index.js setNumberField: defensively coerce stringy numbers so this gotcha can't bite a future field. Verified end-to-end in headless Chromium: type new values, click Done, reopen — values persist and the stopLevel/holdLevel marker lines render at the correct x in the level-based mode preview. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/editor/index.js | 7 +++++-- src/editor/oneditprepare.js | 9 ++++++--- src/editor/oneditsave.js | 9 +++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/editor/index.js b/src/editor/index.js index 2606e8a..35d6a87 100644 --- a/src/editor/index.js +++ b/src/editor/index.js @@ -11,10 +11,13 @@ return Number.isFinite(v) ? v : null; }; - // Set a numeric input's value, or blank if not finite. + // Set a numeric input's value, or blank if not finite. Accepts numeric + // strings (Node-RED's auto-form-binding stores form values as strings). ns.setNumberField = (id, val) => { const el = document.getElementById(id); - if (el) el.value = Number.isFinite(val) ? val : ''; + if (!el) return; + const num = typeof val === 'number' ? val : parseFloat(val); + el.value = Number.isFinite(num) ? num : ''; }; // Add input + change listeners to a list of node-input-* ids. diff --git a/src/editor/oneditprepare.js b/src/editor/oneditprepare.js index cce5100..b5da8cf 100644 --- a/src/editor/oneditprepare.js +++ b/src/editor/oneditprepare.js @@ -68,11 +68,14 @@ ns.setNumberField('node-input-stopLevel', node.stopLevel); // holdLevel defaults to startLevel when omitted (no hold band). Show // the saved value if there is one; otherwise mirror startLevel so the - // user immediately sees the "no hold band" baseline. + // user immediately sees the "no hold band" baseline. Coerce to Number + // because Node-RED form-bind stores numeric inputs as strings. + const holdNum = parseFloat(node.holdLevel); ns.setNumberField('node-input-holdLevel', - Number.isFinite(node.holdLevel) ? node.holdLevel : node.startLevel); + Number.isFinite(holdNum) ? holdNum : node.startLevel); + const deadZoneNum = parseFloat(node.deadZoneKeepAlivePercent); ns.setNumberField('node-input-deadZoneKeepAlivePercent', - Number.isFinite(node.deadZoneKeepAlivePercent) ? node.deadZoneKeepAlivePercent : 1); + Number.isFinite(deadZoneNum) ? deadZoneNum : 1); ns.setNumberField('node-input-maxLevel', node.maxLevel); ns.setNumberField('node-input-logCurveFactor', node.logCurveFactor); ns.setNumberField('node-input-shiftLevel', node.shiftLevel); diff --git a/src/editor/oneditsave.js b/src/editor/oneditsave.js index 8ba082f..9e03259 100644 --- a/src/editor/oneditsave.js +++ b/src/editor/oneditsave.js @@ -50,6 +50,15 @@ node.logCurveFactor = parseNum('node-input-logCurveFactor'); node.startLevel = parseNum('node-input-startLevel'); node.maxLevel = parseNum('node-input-maxLevel'); + // Persist as numbers — Node-RED's auto-form-binding would store these as + // strings, and oneditprepare's setNumberField rejects non-Number values, + // so the input would blank out on reopen. + const stopLevelVal = parseNum('node-input-stopLevel'); + node.stopLevel = Number.isFinite(stopLevelVal) ? stopLevelVal : null; + const holdLevelVal = parseNum('node-input-holdLevel'); + if (Number.isFinite(holdLevelVal)) node.holdLevel = holdLevelVal; + const deadZoneVal = parseNum('node-input-deadZoneKeepAlivePercent'); + if (Number.isFinite(deadZoneVal)) node.deadZoneKeepAlivePercent = deadZoneVal; // minLevel is no longer a user input — it's the derived dryRunLevel // (outflowLevel × (1 + dryRunThresholdPercent/100)). The runtime still // uses node.minLevel as the unconditional STOP threshold; we set it