feat(editor): five distinct type colours + wired/total badge in step header

Two operator-visible improvements:

 - Five payload types now get five distinct hues that
   survive both light and dark themes:
     text   → blue (0xFF42A5F5)
     bytes  → deep orange (0xFFFF7043)
     json   → purple (0xFFAB47BC)
     file   → green (0xFF66BB6A)
     number → amber (0xFFFFCA28)
   Previously bytes + file shared a colour and the palette
   collapsed under custom theme plugins. Hardcoded hues
   keep them readable everywhere.

 - Step headers now carry a small "wired/total" badge.
   When all with-fields carry a `$src.field` reference,
   the badge collapses to a green check icon — the visual
   "this module is fully wired" signal Stefan asked for.
   Partial-wiring shows e.g. "3/5" in a pill matching the
   step accent. Endpoint nodes don't render the badge
   (ratio doesn't apply there).

Computed live from graph.edges + step.with_ values, so the
indicator updates the moment an operator drags a
connection in or out.

Version 0.5.0 -> 0.5.1.

Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
flemming-it 2026-06-01 16:47:29 +02:00
parent 099cd182b9
commit e4e2d45374
3 changed files with 70 additions and 10 deletions

View file

@ -311,6 +311,14 @@ class _FlowCanvasState extends State<FlowCanvas> {
final selected = widget.controller.selectedStepId == step.id;
final raw = widget.controller.stepStatuses[step.id] ?? StepRunStatus.idle;
final status = _toNodeStatus(raw);
// How many with-fields carry a wired-up `$src.field`
// expression. Drives the header's "n/total" badge so the
// operator can see at a glance whether the module is
// fully connected.
final wired = step.with_.values
.whereType<Object>()
.where((v) => _isWiredExpression(v.toString()))
.length;
return Positioned(
left: pos.x,
top: pos.y,
@ -319,6 +327,7 @@ class _FlowCanvasState extends State<FlowCanvas> {
title: step.id,
subtitle: step.use,
inputPortLabels: step.with_.keys.toList(),
wiredCount: wired,
kind: kindForStep(step),
selected: selected,
status: status,
@ -591,20 +600,26 @@ class _FlowCanvasState extends State<FlowCanvas> {
}
Color _typeAccent(String type, ThemeData theme) {
final cs = theme.colorScheme;
// Five distinct hues for the five payload types F-Delta-I
// flows declare today. Picked from a high-contrast set
// that survives both light and dark themes; intentionally
// NOT pulled exclusively from the theme's primary /
// secondary / tertiary slots because those collide once
// a custom theme plugin is active.
switch (type) {
case 'text':
return cs.primary;
return const Color(0xFF42A5F5); // blue
case 'bytes':
case 'file':
return cs.tertiary;
return const Color(0xFFFF7043); // deep orange
case 'json':
return cs.secondary;
return const Color(0xFFAB47BC); // purple
case 'file':
return const Color(0xFF66BB6A); // green
case 'number':
case 'integer':
return Colors.amber.shade400;
return const Color(0xFFFFCA28); // amber
default:
return cs.onSurfaceVariant;
return theme.colorScheme.onSurfaceVariant;
}
}
@ -786,7 +801,6 @@ class _FlowCanvasState extends State<FlowCanvas> {
return (best - portCenter).distance < 0.5;
}
void _finalizeDraft() {
final draft = _draft;
setState(() => _draft = null);