feat(editor): type-checked connections + card-height fix + widget tests

Three changes:

1. Type-checked port connections. Drag from an output port
   only highlights compatible-type input ports as drop
   targets; incompatible drops are silently refused. Source
   of truth: ModuleSpec for declared types. When either side
   is unknown (no manifest, implicit YAML field) the check
   degrades to 'compatible with anything' so a missing
   manifest never blocks composition.

2. NodeGeometry.heightFor now adds a 2 px allowance for the
   1 px AnimatedContainer border on top + bottom of the
   card. Without this the last port-row's Column would
   overflow by 2 px under tight layout constraints (caught
   by the new widget tests; production canvas clipped it
   silently inside InteractiveViewer).

3. Width parameter on FlowNode now actually drives the
   outer SizedBox. The dynamic _stepWidth value from the
   canvas finally reaches the card border rather than being
   shadowed by NodeGeometry.width. Long labels
   (model_endpoint, source_language) no longer ellipsis-clip.

Tests: 8 new widget + geometry tests covering width growth,
two-column body layout, port-tooltip attachment,
row-alignment between input and output sides, and the
existing kindForStep classifier. All 20 editor tests pass.

Bumps fai_studio_flow_editor to 0.11.0.

Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
flemming-it 2026-06-02 01:29:00 +02:00
parent a1d25fd48a
commit 7b256b5e35
5 changed files with 264 additions and 21 deletions

View file

@ -94,10 +94,18 @@ class NodeGeometry {
/// Total card height for a node carrying [inputCount] inputs
/// on the left + [outputCount] outputs on the right. The
/// taller side wins; the card always reserves at least one
/// row of body so the header doesn't sit naked.
/// row of body so the header doesn't sit naked. Adds a
/// 2 px allowance for the 1 px border on the top + bottom
/// of the AnimatedContainer wrapping the card, otherwise
/// the inner Column gets shorted by the border and the
/// last port row overflows.
static double heightFor(int inputCount, [int outputCount = 0]) {
final rows = inputCount > outputCount ? inputCount : outputCount;
return headerHeight + bodyTopPad + rows * portRowHeight + bodyBottomPad;
return headerHeight +
bodyTopPad +
rows * portRowHeight +
bodyBottomPad +
2;
}
/// Y offset (from the card's top edge) where the input
@ -290,11 +298,7 @@ class FlowNode extends StatelessWidget {
),
);
}
return SizedBox(
width: NodeGeometry.width,
height: height,
child: cardWidget,
);
return SizedBox(width: width, height: height, child: cardWidget);
}
Widget _card(ThemeData theme, Color accent, double height) {