feat(editor): type-token coloring + analyzer diagnostics

The text tab now colours `type: text|json|bytes|file|number`
values in the same hues the graph canvas uses for the wire of
that type — a glance at the YAML confirms what a glance at the
graph shows. Promoted the wire-colour palette to a single
source of truth in `wire_colors.dart` so the graph, the
properties panel and the text tab can't drift.

Adds `FlowAnalyzer` (extends `AbstractAnalyzer`) so the gutter
shows error pins and broken lines get wavy red underlines for:

  - YAML parse errors (source-span pinned to the offending line)
  - `use:` referencing a capability the operator hasn't installed
    (bare provider/name match — `text.echo@^1` and `text.echo`
    compare equal)

Editor host passes a closure into `setAvailableCapabilities` so
the analyser always sees the current installed list without
re-creating the controller on every Studio rebuild. Bumps the
package version to 0.15.0.

Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
flemming-it 2026-06-04 00:46:51 +02:00
parent 73736769a0
commit 885d2db4e1
8 changed files with 400 additions and 52 deletions

View file

@ -42,6 +42,7 @@ import '../model/flow_graph.dart';
import '../model/layout_store.dart';
import '../run_driver.dart';
import '../tokens.dart';
import '../wire_colors.dart';
import 'edge_painter.dart';
import 'flow_node.dart';
@ -1714,38 +1715,7 @@ class _FlowCanvasState extends State<FlowCanvas>
/// five FI types stay distinguishable both at glance
/// (saturation differences) and for colour-blind operators
/// (different luminance, not only different hue).
Color _typeAccent(String type, ThemeData theme) {
final isDark = theme.brightness == Brightness.dark;
switch (type) {
case 'text':
// Magenta / pink LabVIEW's string colour. Pops on
// both light + dark surfaces; high saturation reads
// as "text flows here".
return isDark ? const Color(0xFFFF6FB5) : const Color(0xFFD81B60);
case 'json':
// Amber / orange structured-data colour, evokes
// LabVIEW's cluster brown. Reads as "compound payload".
return isDark ? const Color(0xFFFFB74D) : const Color(0xFFEF6C00);
case 'bytes':
// Cyan / teal raw binary. Distinct from string-pink
// and from the green of "file reference" so the
// operator never confuses "I'm sending raw bytes" with
// "I'm sending a file handle".
return isDark ? const Color(0xFF4DD0E1) : const Color(0xFF00838F);
case 'file':
// Green file reference / resource handle. LabVIEW
// uses green for refnums; reads as "this is a pointer
// to something on disk".
return isDark ? const Color(0xFF81C784) : const Color(0xFF2E7D32);
case 'number':
case 'integer':
return isDark ? const Color(0xFFFFD54F) : const Color(0xFFF9A825);
default:
// Unknown type neutral, deliberately desaturated
// so the operator notices "I haven't typed this".
return theme.colorScheme.onSurfaceVariant;
}
}
Color _typeAccent(String type, ThemeData theme) => wireColor(type, theme);
Future<void> _disconnectInputPort(
String stepId,

View file

@ -19,6 +19,7 @@ import '../l10n.dart';
import '../model/auto_layout.dart';
import '../model/flow_graph.dart';
import '../tokens.dart';
import '../wire_colors.dart';
class PropertiesPanel extends StatefulWidget {
final FlowEditorController controller;
@ -673,21 +674,7 @@ class _EdgeInfoView extends StatelessWidget {
return null;
}
Color _typeColor(String type, ThemeData theme) {
final isDark = theme.brightness == Brightness.dark;
switch (type) {
case 'text':
return isDark ? const Color(0xFFFF6FB5) : const Color(0xFFD81B60);
case 'json':
return isDark ? const Color(0xFFFFB74D) : const Color(0xFFEF6C00);
case 'bytes':
return isDark ? const Color(0xFF4DD0E1) : const Color(0xFF00838F);
case 'file':
return isDark ? const Color(0xFF81C784) : const Color(0xFF2E7D32);
default:
return theme.colorScheme.outline;
}
}
Color _typeColor(String type, ThemeData theme) => wireColor(type, theme);
void _disconnect(FlowEdge edge) {
final graph = controller.graph;