The picker (node-settings-dialog) shared --z-modal with every other dialog
guest, so opening it from "Evaluate impact with LLM" - itself a modal - tied
on z-index with the report behind it and lost on DOM order: it rendered, and
its backdrop even blocked clicks, but neither was visible. It read as a
button that did nothing.
Named the layer this actually is - --z-dialog-over-modal, the same one the
confirmation dialog already needed and had defined ad hoc as --z-confirm -
and moved the picker onto it.
While chasing this, closed a real silence next to it: with no LLM provider
published at all, the picker answered null and the caller treated that like
a dismissal, so the button did nothing for a second, unrelated reason. It now
throws with a message, and Simulate surfaces it as a notification instead of
swallowing it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every bound was already in the schema and already enforced by the server,
but nothing passed it to the control: a temperature of 5 was typeable and
only failed on save. The settings dialog and the inline node editor now
share one validator, so a bound declared once reads the same wherever a
value can be typed. Numeric properties finally get a numeric control.
Arrow increment and required granularity are kept apart: step says what
the value must be a multiple of - 1 on an integer, nothing on a decimal -
while stepIncrement only moves the spinner. Arrows on a 0-to-1 field used
to jump by 1, reaching only the two ends of the range; they now move by a
tenth without making 0.35 wrong. FieldValueConstraints omits stepIncrement
so the increment cannot reach the validator to try.
An empty optional field now states that it is using the default, with a
reset beside the control that stays in place and greys out rather than
appearing once a value is typed. Going back to unset is the one thing a
filled box cannot express: clearing it by hand looks identical to never
having decided. Generic - it follows from the schema not requiring the
field, on all three editing surfaces, container included.
Also fixes the dialog reading as broken: descriptions were rendered twice,
once as a mat-hint and once below in error red, and the wrapping hint
overflowed the fixed-height subscript area onto the button beside it.
An optional group now sits in the fieldset of the object that owns it, so
a node holding two LLM descriptors cannot show two identical "Model
parameters" controls with nothing to tell them apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Provider and model are what anyone opening the simulation dialog came for. Shown
flat beside them, the five optional parameters turned the common case into a
seven-field form for a choice most runs do not make.
NodeSettingField gains an optional group, and the dialog renders those fields in
a collapsible section, closed until opened. A closed section that holds values
says how many, so one that is doing something never looks like one that is not -
and it counts a temperature of 0, which is a real setting rather than an empty
field.
The field markup moved into one ng-template used by both the plain list and the
sections. It is about a hundred lines of switch; a second copy would have drifted.
The open-state is a signal rather than a mutated Set. The component is OnPush, so
a Set only re-rendered when the change arrived through a template event - true
here by luck, and false the moment anything toggled a section from code. A test
caught it.
577 frontend tests green; the collapsed-by-default assertions fail when the group
is forced open. Initial bundle now 7.28 kB over budget, up from 4.26.
The node editor is untouched: that one is still to be discussed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Numeric fields inside a nested object were already collected, grouped and saved
by the schema-driven form, so most of this is what that form could not yet do.
A cleared numeric field now removes its key instead of saving 0. Number('') is 0,
so an emptied box used to persist a real zero - and on a temperature that is the
worst confusion available, because 0 is a valid and useful setting, which meant
that once a value had been given there was no way back to the provider default.
The container node carried its own copy of the same parsing and the same defect;
both now agree, and its maxIterations can no longer be cleared into a 0 its own
constraint forbids.
minimum and maximum are read from the schema and bound on the input, and the
placeholder says the range and that empty is allowed - otherwise the only way to
learn either is to save and be refused.
The simulation dialog gains the same five fields, which needed NodeSettingField
to grow a number type and NodeSettingsValues to admit numbers. That widening
rippled into three signatures that assumed string | boolean; the Angular compiler
found them, tsc --noEmit did not.
readSimulatorParameters is pure and tested rather than buried in the viewer: it
is where "the user left this empty" has to survive contact with Number('').
571 frontend tests green, and the cleared-field assertion fails when the parsing
is put back. Initial bundle 4.26 kB over budget, up from 2.88 - reported, not
raised.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each of these four dialogs hardcoded its own border-radius, border
color, shadow and z-index instead of sharing the tokens introduced
for modal-shell. Two of the mismatches were more than cosmetic:
- confirm-dialog sat at z-index 10000, below node-settings-dialog
(10010) and below any modal-shell-based dialog (10021). Both
bias-rerun-dialog and bias-impact-experiment-dialog call
ConfirmDialogService while staying open themselves, so a confirm
triggered from either would render completely hidden behind them
and be unclickable, silently stalling the flow. confirm-dialog is
now the top of the z-index ladder (--z-confirm: 10031) since it is
the one dialog that can legitimately nest inside any other.
- human-interaction-dialog used a 12px corner radius while every
other dialog uses 16px; aligned it to --radius-modal.
subflow-preview-dialog keeps its larger radius, gradient background
and node-card border color, since it deliberately mirrors the canvas
aesthetic of the subflow it previews rather than a form dialog — only
its z-index was aligned to the shared ladder.
Verified with the full suite (232/232) and a live browser check of
the token-consuming modal-shell dialogs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>