A two-way visual editor for Flutter widget source. Edit a widget in a GUI, and the .dart file
changes the way you would have changed it by hand: one property edit, one changed line, and every
byte you did not touch left exactly as it was.
Your source is the source of truth. Loom has no project format, no sidecar metadata, and no generated regions. It reads the analyzer's AST, models it, and emits minimal edits back.
Selecting a node links all three panes at once. The outline is the tree, the canvas is a deterministic geometry pass over that tree rather than a live render, and the inspector edits the properties the kernel could model.
The point of a visual editor that writes source is that the diff has to be reviewable. Loom's acceptance test for undo and redo is byte equality against disk:
10 random property edits, assert the file on disk has diverged. 10 undos, assert the file is byte-identical to the original. 10 redos, assert it is byte-identical to the post-edit state. Then one undo plus one fresh edit, and assert the redo stack is empty.
That is a test, not a claim. It runs on every push.
Formatting, trailing commas, comments and whitespace in untouched regions survive an edit, because Loom plans an edit against the AST and rewrites the smallest span that expresses it, rather than reprinting the file.
Two packages, in a melos-orchestrated workspace.
| Package | What it is |
|---|---|
packages/loom |
The kernel. Pure Dart, headless, no Flutter dependency. Parses widget source into a WidgetTreeModel, and emits SourceEdits back. Usable from a CLI, a build step, or CI. |
packages/loom_app |
The editor. A Flutter desktop app that consumes the kernel: file tree, widget outline, canvas, property inspector, undo/redo, format on save, per-project session restore. |
The split is the design. The interesting work is all in the kernel, and it is testable without a window: 828 of the 933 tests in this repository never start a GUI.
Tests: 933 passing. 828 in the kernel, 105 in the app. CI runs both on every push.
The kernel's parse surface is additionally validated against an external corpus several times the size of the in-repo fixtures, which is the check that the foundation works on code that was not written for these tests.
- Reads widget trees across files, following helper methods that return widgets.
- Models properties structurally: numbers, strings, booleans, enums,
constconstructor calls such asEdgeInsets.all(8), and annotations. Anything it cannot model is preserved verbatim as an opaque block rather than dropped or rewritten. - Edits properties, and performs structural edits: insert, remove and move children, class members, function bodies and directives.
- Emits minimal diffs, with an orphan-indent guard and a property-test gauntlet that removes every removable item from every fixture and asserts the result reparses with zero diagnostics.
- Recognises three common codegen idioms (Freezed, json_serializable, Drift) so generated annotations are modeled rather than treated as noise.
M13 shipped: the low-fidelity widget canvas. The centre pane draws the widget tree as nested
labeled rectangles with slot-aware layout for Scaffold, Row, Column, Stack and the
scrollables. Click to select, hover to preview, double-click a Text to edit its data: inline.
M14 next: the toolbox and drag-drop insertion. The left rail is a placeholder until then, and says so in the app.
Real preview is deliberately deferred. The canvas is a geometry pass, so it works on any file the
kernel can parse, including files that do not compile. A real preview would need a running
flutter run, a VM-service screenshot reader and a coordinate mapping back to source spans, and it
would buy nothing for the select, inspect, edit and move workflows through M16. The canvas is about
six files, so swapping in a rendered preview later is a vertical slice, not a rewrite.
Requires the Flutter SDK, which brings the Dart SDK the kernel needs.
# Kernel only, no Flutter app involved
cd packages/loom
dart pub get
dart test
# The desktop editor
cd packages/loom_app
flutter pub get
flutter test
flutter run -d windows # or: ./run.bat
Open a project with File > Open Project, pick a folder containing Dart source, then choose a file from the Interface tab.
PROJECT_SPEC.mdfor scope, architecture, milestones, acceptance criteria and the invariants the tests enforce.DEVLOG.mdfor settled decisions and the reasoning behind them, including the ones that were reversed.
