- configs/machineGroupControl.json: drop prioritypercentagecontrol mode
(unused — set.demand became unit-self-describing, so percentage-vs-absolute
is decided per-message, not by a node-wide scaling mode). Add output.process
/ output.dbase enums + functionality.distance{,Unit,Description} so the
editor's distance offset persists. Fixes the runtime warnings 'Unknown key
optimization/scaling/movement/curvePressureUnit etc.' the validator was
logging on every MGC instantiation.
- configs/measurement.json: same output.process/dbase block + nullable
position.x for the rare case a measurement has no parent yet.
- datasets/assetData/machine.json -> rotatingmachine.json: rename so
AssetMenu's softwareType lookup matches. AssetMenu.getActiveCategoryKey
no longer silently falls back to keys[0] (which mis-showed diffuser models
for rotatingMachine nodes) — returns null with a console.warn instead.
- menu/asset.js: re-derive supplier/assetType from saved model id on reopen.
The save handler intentionally discards the denormalized registry copies
to keep the persisted node small, so the cascade dropdown booted at
'Select...' even when a model was saved. Walk the registry tree to
reconstitute.
- predict/predict_class.js: minor.
- configs/index.js: minor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related changes bundled together because the diffuser curve files
only make sense once the registry namespace they live in exists.
src/registry — new asset-metadata resolver:
- AssetResolver with synchronous resolve(namespace, id) + lazy cache,
async refresh() for future remote pulls.
- FileBackend (per-id or single-file layouts, case-insensitive) and a
stub HttpBackend (disabled unless EVOLV_ASSET_REMOTE=1).
- Namespaces: curves, menu, monsterSamples, monsterSpecs, units. Menu
namespace re-keys by inner softwareType + filename so editors that
pass either string resolve to the same tree.
- README explains how to add a namespace.
- AssetCategoryManager (datasets/assetData/index.js) becomes a thin
facade over the resolver so existing consumers don't move.
- 246/246 tests pass — including the 39-test registry suite.
datasets/assetData — file moves + new diffuser data:
- modelData/*.json deleted; curves/*.json is the canonical home.
- New diffuser.json menu tree with GVA, Jäger, Aquaconsult/Entec,
PIK/PRK suppliers.
- gva-elastox-r.json migrated from the inline _loadSpecs hardcode,
re-tagged coverageBasis="bottom-coverage-pct" (the legacy 2.4
elements/m² was a prior mis-conversion; we can't recover the
original % so it's a single-point curve under key "0").
- jaeger-jetflex-td-65-2-g-epdm-1000.json — extracted from the Jäger
EPDM-1000mm SSOTE/DWP chart on the data sheet (vector-PDF read).
SSOTE 8.20→6.40 %/m, DWP 25→48 mbar across Q 2-12 Nm³/h. Single
coverage (vendor doesn't state test conditions).
- aerostrip-phoenix.json — 4-coverage SOTE family at 4.75 m water
depth (DD 5/10/15/20 %, flux 10-70 Nm³/h·m²) from the Entec/de
Winter 2023-11-22 dataset; DWP curve from the 21 % @ 4.05 m chart.
- pik300.json / prk300.json — 5-coverage SOTE + SSOTR (DD 5-25 %)
with split DWP per model variant, water depth ≈ 4.0 m inferred from
the SOTE↔SSOTR ratio in the source spreadsheet.
src/configs/diffuser.json:
- New asset.{model, assetTagNumber} block so the editor's selected
model id survives validation.
- diffuser.density description corrected to "Bottom coverage [%]";
default 2.4 → 15 (typical fine-bubble install).
src/configs/{rotatingMachine,valve}.json: small alignment edits that
came with the registry phase.
src/menu/asset.js + src/menu/aquonSamples.js: rewritten as facades
over assetResolver, keeping the editor-side cascade behaviour intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 243-line legacy AssetMenu was retained for backwards compatibility
but no code in the refactored platform references it. Removed.
loadCurve removal stays deferred — rotatingMachine + valve still call
it through src/curves/curveLoader.js and src/curve/supplierCurve.js.
Migration to loadModel is a follow-up after the platform refactor lands
on main.
113/113 basic tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reproduction (any node using assetMenu — measurement, rotatingMachine,
pumpingStation, monster, …):
open node -> pick Vega supplier -> pick Pressure type
-> model dropdown stays "Awaiting Type Selection"
Root cause: two interacting bugs in the chained dropdown wiring.
1. populate() inside both wireEvents() and loadData() auto-dispatched a
synthetic 'change' event whenever the value of the rebuilt <select>
differed from before the rebuild. That meant rebuilding 'type' inside
the supplier change handler could fire the *type* change handler
mid-way through, populate the model dropdown, and then return — only
for the supplier handler to continue and unconditionally call
populate(elems.model, [], '', undefined, 'Awaiting Type Selection'),
wiping the model dropdown back to empty.
2. loadData() ran the same auto-dispatch path, so on initial open of a
saved node the synthetic change cascaded through wireEvents listeners
AND loadData's own sequential populate calls double-populated each
level. The visible state depended on which path won the race.
Fix: convert the chain to an explicit downward cascade.
- populate() no longer dispatches change events. It simply rebuilds the
<select> with placeholder + options and assigns the requested value.
- New cascadeFromSupplier / cascadeFromType / cascadeFromModel helpers
read the *current DOM value* of each upstream <select>, look up the
matching item in menuData, and rebuild the next level — then call the
next cascade explicitly. Order is now deterministic and the parent
handler can never wipe the child after the child was populated.
- Each <select>'s native 'change' listener is just the corresponding
cascade function. Same code path runs for user picks AND for initial
load, so saved-node restore behaves identically to a fresh pick.
- The cascades are exposed under window.EVOLV.nodes.<name>.assetMenu._cascade
so loadData (or future sync code) can re-run them after async data
arrives without duplicating logic.
No new DOM dependencies, no test framework changes. Existing
generalFunctions tests still 52/61 (same 9 pre-existing failures
unrelated to this change).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace hardcoded position strings with POSITIONS.* constants
- Prefix unused variables with _ to resolve no-unused-vars warnings
- Fix no-prototype-builtins with Object.prototype.hasOwnProperty.call()
- Extract menuUtils.js (543 lines) into 6 focused modules under menu/
- menuUtils.js now 35 lines, delegates via prototype mixin pattern
- Add 158 unit tests for ConfigManager, MeasurementContainer, ValidationUtils
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>