Swappable inline YAML editor for F∆I Studio flows.
Find a file
flemming-it 7aeeae717f feat(editor): wires carry their type — animated flow + canvas depth
A 2026-feel pass on the visual language. Three structural
moves that change how the editor reads at a glance.

1. Wires now carry the colour of the data they transport.

   Every edge leaving the inputs endpoint takes the
   declared input's type accent — blue for text, orange
   for bytes, purple for json, green for file, amber for
   number. The operator can trace "this is the document
   path, this is the prompt" by following colour, no
   labels needed. Edges leaving step outputs stay neutral
   (we don't have module manifests to look up the output
   type yet — once the hub exposes them, this lookup
   grows).

   Hover / selection still wins the colour treatment so
   the "I'm pointing at this one" signal isn't lost in
   the type palette.

2. Edges entering a currently-running step animate.

   New AnimationController on the canvas loops a 0..1
   phase at 1.5 s when ANY step is running, stopped
   otherwise (no idle cost). EdgePainter takes the phase
   and renders animated edges as marching dashes in the
   wire's accent colour over a 35%-alpha base — the
   operator literally sees data moving along the wire in
   the direction of flow while a step executes. When the
   step completes, the animation stops, the wire returns
   to a static stroke. Run a flow with a slow step and
   the canvas comes alive.

3. Canvas gets depth.

   - Background is now a subtle top-left → bottom-right
     gradient between surfaceContainer and surface,
     giving the working area a "lit centre, recessed
     corners" cue instead of one flat dark page.
   - Node cards swap their Material widget for an
     AnimatedContainer with two-layer shadows: a sharp
     short-blur shadow gives the card a real footprint,
     a soft long-blur shadow casts depth onto the canvas
     behind. Selected nodes get a third accent-coloured
     halo so selection state reads from across the canvas.
   - 180 ms ease-out-cubic transitions on the
     AnimatedContainer mean hover-into-select and
     run-status-changes morph smoothly instead of
     snapping.

Edge stroke widths bumped slightly (2.1 / 2.8 from 2.0 /
2.6) so the typed wires read at a confident weight against
the new gradient backdrop.

Version 0.6.0 -> 0.7.0 — visible visual language change.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 17:45:27 +02:00
lib feat(editor): wires carry their type — animated flow + canvas depth 2026-06-01 17:45:27 +02:00
test test(editor): exercise the extract-with-approval flow shape 2026-06-01 02:01:46 +02:00
.gitignore feat: initial scaffold — swappable flow editor 2026-05-30 14:33:03 +02:00
pubspec.yaml feat(editor): wires carry their type — animated flow + canvas depth 2026-06-01 17:45:27 +02:00
README.md docs(editor): README + version bump to 0.2.1 2026-06-01 01:53:29 +02:00

fai_studio_flow_editor

Swappable in-Studio flow editor for the F∆I Platform. Hosts a three-tab editing surface on top of one in-memory flow YAML:

  • Graph — drag-and-drop WYSIWYG canvas of step nodes and their input / output ports, edges derived live from $ref expressions in the YAML.
  • Text — full-screen YAML editor with line numbers and syntax highlighting, anchored at the top of the viewport.
  • Run — inputs form, Start button, live step-progress list driven by the host's event stream, typed outputs once the run resolves.

The package is intentionally host-agnostic. Studio depends on it via a git: ref: main pubspec entry; replacing the editor is one pubspec change and a rebuild.

Public surface

import 'package:fai_studio_flow_editor/fai_studio_flow_editor.dart';

FlowEditorPage(
  initialFlowName: String?,                  // preload this flow
  locale: FlowEditorLocale,                  // en | de
  runDriver: FlowRunDriver?,                 // host-supplied hub bridge
  availableCapabilities: List<String>,       // for the Add-step picker
)

Host implements FlowRunDriver to bridge the editor's runFlow + events calls to whatever talks to the hub:

abstract class FlowRunDriver {
  Future<Map<String, FlowOutputValue>> runFlow({
    required String flowName,
    required Map<String, String> textInputs,
    required Map<String, Uint8List> fileInputs,
    required Map<String, String> fileMimes,
  });
  Stream<FlowRunEvent> events();
}

Event types: StepStarted, StepCompleted, StepFailed, StepAwaitingApproval. Output types: FlowOutputText, FlowOutputJson, FlowOutputBytes.

Source-of-truth model

YAML text is the canonical state. The graph view derives a FlowGraph object from the YAML on every change (debounced 350 ms). Graph edits emit fresh YAML into the shared CodeController via FlowGraph.toYaml(). The two tabs cannot fall out of sync because both observe the same FlowEditorController.

Node positions are the one piece NOT in the YAML — they live in a per-flow sidecar JSON file at ~/.fai/data/flows/.layout/<name>.json. This keeps the YAML byte-stable for the hub's audit log and avoids spurious diffs in version control.

Behaviour

  • File browser (left, 240 px) lists *.yaml in ~/.fai/data/flows/ with a FLOWS header + refresh icon.
  • Graph tab: InteractiveViewer with pan + zoom; drag a node body to reposition; drag an output port → drop on an input port to create a $source.field reference; click a node to open the properties panel on the right.
  • Text tab: YAML with syntax highlighting, top-aligned via expands: true on the underlying CodeField.
  • Run tab: inputs form with text fields + file picks, Start button, live step list driven by the driver's event stream, typed outputs once the run resolves.
  • Save writes directly to disk via dart:io; the hub picks up the change on its next ListFlows / RunSavedFlow call. Cmd+S / Ctrl+S — save. Cmd+Enter / Ctrl+Enter — jump to the Run tab.

Folder layout

lib/
├── fai_studio_flow_editor.dart   exports public API
└── src/
    ├── editor_controller.dart    shared state + reparse loop
    ├── flow_editor_page.dart     host scaffolding, tabs
    ├── l10n.dart                 EN/DE strings
    ├── run_driver.dart           host bridge interface
    ├── tokens.dart               spacing / radius / motion
    ├── widgets.dart              FaiEmptyState + FaiErrorBox
    ├── model/
    │   ├── flow_graph.dart       parser + serializer
    │   ├── auto_layout.dart      topological column layout
    │   └── layout_store.dart     sidecar JSON I/O
    └── widgets/
        ├── flow_canvas.dart      InteractiveViewer + nodes + drag
        ├── flow_node.dart        node card primitive
        ├── edge_painter.dart     cubic bezier edges
        ├── properties_panel.dart selected-step editor
        ├── capability_picker.dart Add-step modal
        └── run_tab.dart          inputs + step progress + outputs

Swap it

Studio depends on this package via pubspec.yaml git reference:

dependencies:
  fai_studio_flow_editor:
    git:
      url: https://git.flemming.ai/fai/studio-flow-editor
      ref: main

To use a different editor:

  1. Fork this repo (or write your own from scratch).
  2. Keep the FlowEditorPage constructor signature (initialFlowName, locale, runDriver, availableCapabilities) — that's the stable host contract.
  3. Point Studio's pubspec at your fork.
  4. Rebuild Studio.

The contract is minimal: one page widget + a driver interface. The package brings its own tokens, its own empty/error widgets, its own l10n table, so the host doesn't have to share internals. The trade-off is visual drift if Studio's design tokens change — that's the deal you accept for a swappable module.

Develop

flutter pub get
flutter analyze
flutter test

11 unit tests cover the parser (round-trip, edge detection, approval flagging, leading-comment preservation) and the controller (open / edit / dirty / select).

Versioning

Semantic. Major bumps when the public FlowEditorPage signature changes. Adding strings to FlowEditorStrings or events to FlowRunEvent is a minor bump. Patch covers visual + bug fixes inside the package.

Current: 0.2.1 — first WYSIWYG release.

License

Apache-2.0. Same as the rest of the F∆I platform.