Stefan's "the connections to the round ports look unelegant" feedback was about three concrete things: (1) The bezier endpoint sat at the port dot's GEOMETRIC CENTRE. The dot was rendered ON TOP, so the line visually disappeared into the centre of the dot — looked like a thread being eaten by a bead. (2) A separate arrow-head triangle was drawn on top of the dot, doubling the visual terminator and making the endpoint look "noisy". (3) The dots were hollow rings the same colour as the line, so the boundary between line and dot blurred visually. They didn't read as connectors. Fix: - EdgeSegment now carries `fromSide` + `toSide` (left / right) so the painter knows which way to shorten each endpoint. The bezier ends `portRadius` px short of the centre — exactly on the dot's outer perimeter, on the side facing the line. The line now "docks" cleanly at the rim. - The end-cap triangle arrow is gone. The dot itself is the visual terminator; an arrow on top was redundant. - Port dot redesigned as a connector-socket: outer ring defines the footprint, inner 4-px surface-coloured pin appears when connected. The result reads as "an active socket with a plug seated in it" rather than "a hollow circle next to a card edge". Empty ports stay surface- filled rings — unambiguous empty-slot signal. - Bezier handle length floor raised from 40 to 60 px so vertical detours (e.g. inputs-endpoint → step far below) curve gracefully out of the start before dropping instead of kinking near the origin. - Stroke width nudged from 1.8 to 2.0 px for the normal accent so the line carries the right visual weight against the new docked-at-perimeter port treatment. Visual: lines now look like cables seated into sockets, not strings vanishing under beads. Signed-off-by: flemming-it <sf@flemming.it> |
||
|---|---|---|
| lib | ||
| test | ||
| .gitignore | ||
| pubspec.yaml | ||
| README.md | ||
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
$refexpressions 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
*.yamlin~/.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.fieldreference; click a node to open the properties panel on the right. - Text tab: YAML with syntax highlighting, top-aligned via
expands: trueon 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 nextListFlows/RunSavedFlowcall. 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:
- Fork this repo (or write your own from scratch).
- Keep the
FlowEditorPageconstructor signature (initialFlowName,locale,runDriver,availableCapabilities) — that's the stable host contract. - Point Studio's pubspec at your fork.
- 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.