fix(ps): persist stopLevel/holdLevel as numbers across editor save
Node-RED's auto-form-binding writes <input type="number"> 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) <noreply@anthropic.com>
This commit is contained in:
@@ -11,10 +11,13 @@
|
|||||||
return Number.isFinite(v) ? v : null;
|
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) => {
|
ns.setNumberField = (id, val) => {
|
||||||
const el = document.getElementById(id);
|
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.
|
// Add input + change listeners to a list of node-input-* ids.
|
||||||
|
|||||||
@@ -68,11 +68,14 @@
|
|||||||
ns.setNumberField('node-input-stopLevel', node.stopLevel);
|
ns.setNumberField('node-input-stopLevel', node.stopLevel);
|
||||||
// holdLevel defaults to startLevel when omitted (no hold band). Show
|
// holdLevel defaults to startLevel when omitted (no hold band). Show
|
||||||
// the saved value if there is one; otherwise mirror startLevel so the
|
// 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',
|
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',
|
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-maxLevel', node.maxLevel);
|
||||||
ns.setNumberField('node-input-logCurveFactor', node.logCurveFactor);
|
ns.setNumberField('node-input-logCurveFactor', node.logCurveFactor);
|
||||||
ns.setNumberField('node-input-shiftLevel', node.shiftLevel);
|
ns.setNumberField('node-input-shiftLevel', node.shiftLevel);
|
||||||
|
|||||||
@@ -50,6 +50,15 @@
|
|||||||
node.logCurveFactor = parseNum('node-input-logCurveFactor');
|
node.logCurveFactor = parseNum('node-input-logCurveFactor');
|
||||||
node.startLevel = parseNum('node-input-startLevel');
|
node.startLevel = parseNum('node-input-startLevel');
|
||||||
node.maxLevel = parseNum('node-input-maxLevel');
|
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
|
// minLevel is no longer a user input — it's the derived dryRunLevel
|
||||||
// (outflowLevel × (1 + dryRunThresholdPercent/100)). The runtime still
|
// (outflowLevel × (1 + dryRunThresholdPercent/100)). The runtime still
|
||||||
// uses node.minLevel as the unconditional STOP threshold; we set it
|
// uses node.minLevel as the unconditional STOP threshold; we set it
|
||||||
|
|||||||
Reference in New Issue
Block a user