Compare commits

..

1 Commits

Author SHA1 Message Date
znetsixe
a83a85e958 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>
2026-05-28 19:21:59 +02:00
3 changed files with 20 additions and 5 deletions

View File

@@ -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.

View File

@@ -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);

View File

@@ -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