chain-studio-flow-editor/README.md
flemming-it cae5c94c4d docs(editor): README + version bump to 0.2.1
Expands README to describe the three-tab UX, the source-of-
truth model (YAML + sidecar layout), the FlowRunDriver
contract, the folder layout, and how to swap the editor.

Version 0.2.0 -> 0.2.1 — picks up the live step status,
the empty-graph CTA, and the fit-to-screen button added
since the initial 0.2.0 commit.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 01:53:29 +02:00

162 lines
5.4 KiB
Markdown

# 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
```dart
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:
```dart
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:
```yaml
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
```bash
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.