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

@ -603,25 +603,63 @@ outputs:
Map<String, TextStyle> _yamlStyle(ThemeData theme) {
final cs = theme.colorScheme;
final isDark = theme.brightness == Brightness.dark;
final monoBase = TextStyle(
fontFamily: 'JetBrains Mono',
fontSize: 13,
height: 1.45,
);
// Pick clearly-distinguished hues so keys, strings,
// numbers, anchors, and comments don't blur into each
// other. We derive accents from the active ColorScheme
// so theme plugins (sunflower / sunset / space / glass)
// tint the highlight as expected without per-theme
// overrides. Falls back to a balanced light/dark palette
// when the scheme tints don't read well as code colours.
final keyAccent = cs.primary;
final stringAccent = isDark ? const Color(0xFFA5D6A7) : const Color(0xFF2E7D32);
final numberAccent = isDark ? const Color(0xFFFFCC80) : const Color(0xFFE65100);
final symbolAccent = isDark ? const Color(0xFFB39DDB) : const Color(0xFF6A1B9A);
final commentAccent = cs.onSurfaceVariant.withValues(alpha: 0.7);
final metaAccent = isDark ? const Color(0xFF80DEEA) : const Color(0xFF00838F);
return {
'root': monoBase.copyWith(color: cs.onSurface),
// YAML key bright primary so the structure pops at a
// glance. attr is the highlight grammar's key token.
'attr': monoBase.copyWith(color: cs.primary, fontWeight: FontWeight.w600),
'string': monoBase.copyWith(color: cs.tertiary),
'number': monoBase.copyWith(color: cs.secondary),
'bullet': monoBase.copyWith(color: cs.onSurfaceVariant),
'literal': monoBase.copyWith(color: cs.secondary),
'comment': monoBase.copyWith(
// YAML keys bold + primary accent so the structure
// reads top-down at a glance.
'attr': monoBase.copyWith(color: keyAccent, fontWeight: FontWeight.w600),
// Quoted + unquoted strings. The highlight grammar
// tags both as 'string'.
'string': monoBase.copyWith(color: stringAccent),
// Numbers (integers, floats, durations).
'number': monoBase.copyWith(color: numberAccent),
// Sequence dashes kept muted so list bullets don't
// shout. Operators read them as structure, not content.
'bullet': monoBase.copyWith(
color: cs.onSurfaceVariant,
fontWeight: FontWeight.w600,
),
// Booleans, null, named scalars.
'literal': monoBase.copyWith(
color: symbolAccent,
fontWeight: FontWeight.w600,
),
// YAML comments softened + italicised so reading the
// structure ignores them, while a deliberate scan
// still picks them out.
'comment': monoBase.copyWith(
color: commentAccent,
fontStyle: FontStyle.italic,
),
'meta': monoBase.copyWith(color: cs.secondary),
// Document markers, directives, tags (--- !!str etc.).
'meta': monoBase.copyWith(color: metaAccent),
// Anchor / alias names (& and *).
'symbol': monoBase.copyWith(color: symbolAccent),
// Templated values the highlight grammar tags some
// braced expressions as 'tag'; pick them out so FI's
// $step.field references stand out via the surrounding
// string colour.
'tag': monoBase.copyWith(color: metaAccent),
'type': monoBase.copyWith(color: symbolAccent),
};
}
}