Commit graph

21 commits

Author SHA1 Message Date
5cd2745db5 feat(editor): localised analyzer/strip/run messages + monospace font fallback
Closes operator-reported issues around the text-tab font and
the English-only run + diagnostic surfaces.

  - **Real monospace everywhere**. The package never bundled
    JetBrains Mono as an asset (it relied on Studio's
    google_fonts pre-load), so a host that doesn't ship the
    font saw the YAML editor render in the system proportional
    default. New _monoTextStyle() pins fontFamilyFallback to a
    cross-platform monospace chain (Menlo / Consolas / Courier
    New / monospace) so the editor stays a grid in every host.
    Applied to the code field, gutter, diagnostic strip,
    issue rows, hover card, fix buttons, and the run tab's
    error box.

  - **Locale-aware analyzer messages**. New AnalyzerStrings
    adapter holds every string the analyzer emits, with an
    .english default + an .from(FlowEditorStrings) factory.
    FlowYamlCodeController.setCapabilityProviders takes the
    strings; FlowEditorPage wires them from the active locale.
    The analyzer's 'Unknown capability', 'Did you mean',
    'Not in the store — install locally with fai install
    --link', 'Unknown input/output type', YAML parse errors,
    and every quick-fix label (Install / Add source / Use /
    Change to) now flip to DE when the editor's locale is DE.

  - **Localised run + diagnostic chrome**. The bottom strip's
    'No issues', '2 errors · 1 warning', 'Copy all'; the
    issue row's L-prefix + Copy tooltip; the hover card's
    L-prefix + Copy tooltip; the run-block tooltip + inline
    message ('2 Fehler verhindern den Lauf · siehe
    Diagnose-Leiste unten'); the step-row 'awaiting approval'
    suffix; the CopyableErrorBox's Copy / Copied tooltip —
    all now flow through FlowEditorStrings.

Bumped to 0.20.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-09 01:36:39 +02:00
b6bb8741a9 feat(editor): persistent diagnostic strip + run-block + copyable run errors
Three connected fixes that close the loop the analyzer started:

  - **Strip lives at page level, not text-tab**. Moved
    _DiagnosticStrip out of _textTab and into _tabbedBody
    below the TabBarView, so the same error list + quick-fix
    buttons are visible on Graph + Text + Run. The Graph-tab
    operator now sees both the pulsing node AND the message
    explaining what's broken, without flipping tabs. One strip,
    one source of truth.

  - **Run button disabled when there are analyzer errors**.
    FlowEditorController.analyzerErrorCount counts `IssueType.error`
    issues; the action strip's Run button + the RunTab's Start
    button both check it. When > 0:
      · button greys out + icon flips to Icons.block
      · tooltip names the count ('2 errors prevent the run')
      · the Run tab grows an inline red explanation next to
        the disabled Start so the operator isn't left guessing
    Warnings (orange) do NOT block — they're nudges. Operators
    who want to run a half-broken flow during dev can still
    silence the analyzer via the strip.

  - **Run failures are copyable everywhere**. New
    _CopyableErrorBox replaces both the per-run failure detail
    and the per-step failure suffix in the RunTab. Selectable
    monospace + explicit Copy button with 'Copied' feedback.
    Scrollbar-capped at 220 px so the box doesn't push outputs
    off-screen. Mirrors the operator-visible contract:
    'every error is copyable, always.'

Closes the operator-reported regression where a failed run
showed 'Lauf fehlgeschlagen' as plain Text, leaving the
operator unable to paste it into a bug report.

Bumped to 0.19.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-09 01:13:45 +02:00
efdfa7dd79 feat(editor): graph-pulse on errors + store-aware install / add-source
Three connected improvements to the analyzer-driven diagnostics:

  - **Pulsing halo on broken graph nodes**. Every step / pseudo-
    node (inputs / outputs) carrying an analyzer issue now
    breathes a red (error) or amber (warning) halo on the graph
    tab — operator sees the problem on the canvas without
    flipping to text. Honours prefers-reduced-motion: reduce-
    motion users get a static halo at the same intensity. The
    canvas reads severity via a new `stepIssueSeverity` map on
    FlowEditorController; pseudo-nodes use `__inputs__` /
    `__outputs__` sentinel ids.

  - **Store-aware install button**. The analyzer now takes a
    second closure, `storeCapabilities`, listing what the public
    store can install. Unknown-capability issues only carry the
    "Install …" quick-fix when the bare cap is in that list;
    otherwise the issue carries an "Add source for …" fix
    instead. Resolves the asymmetry the operator reported: the
    Store didn't show `htw.digiscout/onet.lookup` but the editor
    happily offered to install it (and would have failed). The
    install path no longer lies about itself.

  - **Did-you-mean suggestion**. When the unknown cap is within
    edit-distance two of an installed or store cap (different
    spelling — distance-0 stays hidden because that's an install
    case, not a typo), the analyzer emits a `ReplaceLineValueFix`
    suggesting the closest match. Preserves the version
    constraint by reusing the installed spec when present
    (e.g. `text.echi@^0.1` → `text.echo@^0.1`).

New public surface:

  - `AddModuleSourceFix` + `AddModuleSourceCallback`
  - `FlowEditorPage.storeCapabilities` + `onAddModuleSource`
  - `FlowAnalyzer.stepSeverity` + the `kInputsNodeId` /
    `kOutputsNodeId` sentinels
  - `FlowIssueSeverity` enum + `FlowNode.issueSeverity`

Tests:
  - Install fix only when in store
  - AddModuleSourceFix as fallback when not in store
  - Did-you-mean replaces install when a near miss exists
  - stepSeverity populated for both step ids and pseudo-nodes

All 36 editor tests green. Bumped to 0.18.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-09 00:21:48 +02:00
f43c1ac6cf feat(editor): copyable diagnostics + hover tooltip + quick fixes
Three operator-UX gaps closed in one pass — the diagnostic
strip stayed plain Text (no copy), the wavy underline gave no
hover tooltip (had to read the strip), and there was no way to
act on an issue without leaving the editor.

  - **Selectable + copyable strip**. Both the header summary
    and the per-row issue message are now SelectableText. Per-
    row Copy button (compact icon) lives next to the message;
    header carries a 'Copy all' that copies every issue as
    'L7: <message>' lines. Trackpad-only operators no longer
    have to use the system selection gesture.

  - **Hover tooltip on the wavy underline**. The controller
    attaches TextSpan.onEnter / onExit handlers to every
    issue-overlapping leaf and publishes IssueHoverRequest via
    a ValueNotifier. FlowEditorPage listens and inserts an
    OverlayEntry tooltip card near the cursor. Card carries
    its own MouseRegion that cancels the dismiss timer so the
    operator can slide INTO it to click the action buttons.

  - **Quick fixes for the two most common issues**:
      · 'Unknown capability X' → InstallCapabilityFix (delegated
        to host via the new onInstallCapability callback). On
        success, controller.setAvailableCapabilities + reanalyze
        clear the issue automatically.
      · 'Unknown type Y' with a Levenshtein-distance-≤2 match
        → ReplaceLineValueFix. Applied by the editor itself:
        line is mutated, key + indent preserved, comment
        preserved, then reanalyze.
    Distance > 2 stays unfixed — pushing 'zonglefax' to 'bytes'
    would be worse than no suggestion.

New public surface (exported from the package):

  - QuickFix sealed base + InstallCapabilityFix + ReplaceLineValueFix
  - InstallCapabilityCallback typedef
  - IssueHoverRequest + IssueHoverSeverity
  - FlowEditorPage.onInstallCapability prop

Tests:
  - FlowAnalyzer attaches InstallCapabilityFix to capability
    issues
  - FlowAnalyzer suggests the closest valid type for typos
  - FlowAnalyzer emits no fix when the typo is too far

All 33 editor tests green. Bumped to 0.17.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-09 00:01:51 +02:00
1b50924e16 fix(editor): drop misaligned gutter error pin, add bottom diagnostic strip
Two glitches visible in 0.15.1:

  1. The flutter_code_editor gutter renders an Icons.cancel pin
     for every analyzer issue, hard-positioned inside a 16-px
     slot. With a single-digit indent the icon centres look off;
     with double-digit line numbers it shifts further.
  2. The hover popup for that pin paints at `offset.dx + width`
     of the gutter cell — which lands INSIDE the code area, so
     'Unknown capability ...' overlaps the YAML text the
     operator is trying to read.

The package doesn't expose a fix for either — the icon is a
const in error.dart, the popup position is hard-coded in the
StatefulWidget. Cleanest path: hide the gutter error column
entirely (`GutterStyle.showErrors: false`) and surface the
analyzer issues through a dedicated bottom strip we own.

The new _DiagnosticStrip renders as a single 22-px line under
the editor: a coloured dot + 'N errors · M warnings · L7:
${first.message}'. Tap expands into a per-issue list capped at
140-px scroll, dot-coloured by IssueType. Empty state shows a
muted 'No issues' so the strip never disappears and never
shifts the layout.

Wavy underlines in the text body keep working untouched — the
two surfaces complement each other: the underline points at
the broken token, the strip names what's wrong.

Bumped to 0.16.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-04 10:32:40 +02:00
885d2db4e1 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>
2026-06-04 00:46:51 +02:00
flemming-it
73736769a0 feat(editor): tab-aware action strip + simpler tap handling
Reposition the per-tab action buttons under the TabBar so the
operator's eye flows TabBar → context buttons → canvas. Graph
keeps Add-Step + Save + Run, Text trims to Save + Run, Run
hides them entirely (the Run tab has its own start button).

Drop the outer wrapping GestureDetector that competed with the
inner Positioned.fill detector inside the canvas Stack — two
TapGestureRecognizers in the same arena meant the inner one
sometimes lost edge-tap and background-tap.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-06-02 16:32:27 +02:00
1e9968496e feat(editor): properties-panel toggle + LabVIEW-style type colours
Two visible UX fixes:

1. Properties-panel toggle. Tap a step → opens. Tap the
   same step again → closes (deselect via toggle). Tap the
   canvas background → also closes. selectStep now returns
   to null when called with the already-selected id; the
   editor-controller test updated for the new semantics.

2. LabVIEW-style type colours on every port + wire.
   _typeAccent now picks vibrant brightness-aware hues per
   datatype:
     text   → magenta/pink (LabVIEW string)
     json   → amber/orange (cluster feel)
     bytes  → cyan/teal (raw binary)
     file   → green (file reference)
     number → yellow/amber
   Step-input dots, step-output dots, and outputs-endpoint
   dots all switch from the generic theme.primary to the
   field's declared type accent. Operators read the
   payload from the dot alone.

Editor tests still pass (20).

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-02 11:13:01 +02:00
7b256b5e35 feat(editor): type-checked connections + card-height fix + widget tests
Three changes:

1. Type-checked port connections. Drag from an output port
   only highlights compatible-type input ports as drop
   targets; incompatible drops are silently refused. Source
   of truth: ModuleSpec for declared types. When either side
   is unknown (no manifest, implicit YAML field) the check
   degrades to 'compatible with anything' so a missing
   manifest never blocks composition.

2. NodeGeometry.heightFor now adds a 2 px allowance for the
   1 px AnimatedContainer border on top + bottom of the
   card. Without this the last port-row's Column would
   overflow by 2 px under tight layout constraints (caught
   by the new widget tests; production canvas clipped it
   silently inside InteractiveViewer).

3. Width parameter on FlowNode now actually drives the
   outer SizedBox. The dynamic _stepWidth value from the
   canvas finally reaches the card border rather than being
   shadowed by NodeGeometry.width. Long labels
   (model_endpoint, source_language) no longer ellipsis-clip.

Tests: 8 new widget + geometry tests covering width growth,
two-column body layout, port-tooltip attachment,
row-alignment between input and output sides, and the
existing kindForStep classifier. All 20 editor tests pass.

Bumps fai_studio_flow_editor to 0.11.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-02 01:29:00 +02:00
a71f654162 feat(editor): reduce-motion clamp + edge gradient + breathing pulse + style sheet
Four polish features in one push.

1. Reduce-motion clamp. FaiEditorStyle.clampedForA11y(
   disableAnimations: ...) returns a style with glass, gradient,
   flow animation, and node shadows forced off when the OS
   reduce-motion preference is set. FlowCanvas + FlowEditorPage
   call it on every build with
   MediaQuery.disableAnimationsOf(context) so operators on
   accessibility settings get a flat, static editor without an
   explicit Studio setting.

2. Edge gradient source→target. EdgePainter now accepts an
   optional colorEnd per segment and paints a linear shader
   along the bezier when both ends carry distinct type accents.
   In practice this draws attention to type mismatches: a
   text→json wire reads as a colour transition; a same-type
   wire stays a solid colour. ModuleSpec lookup at both edge
   endpoints feeds the colour pair.

3. Breathing pulse on running steps. FlowNode accepts a
   Listenable pulse; when status=running AND the canvas's
   _flowAnim is ticking, the node wraps in an AnimatedBuilder
   that drives a sine-wave shadow halo (alpha + blur + spread
   oscillating). Stops when the run completes. Gated by
   _style.flowAnimation so reduce-motion / minimal-style modes
   stay still.

4. In-editor style sheet. Bottom-right canvas chrome gains a
   ⚙ Style button that opens a modal bottom sheet with four
   SwitchListTiles (glass, gradient backdrop, flow animation,
   node shadows) plus 'Reset to default'. Edits live in
   _styleOverride on the canvas state — operator can tweak the
   look for the session without restarting or editing code.
   No persistence in this version; follow-up could pipe the
   override through SharedPreferences via the host.

Bumps fai_studio_flow_editor to 0.10.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 22:55:18 +02:00
b1fe765468 feat(editor): per-field input/output ports + i18n tooltips + 4 jai_client patterns
Phase A of the per-field-output-ports roadmap.

FlowRunDriver gains optional moduleInfo(capability) hook
that returns a ModuleSpec carrying declared inputs +
outputs (each ModuleField has name, type, locale->
description map). Default returns null so legacy hosts
that didn't implement the hook keep compiling — the editor
falls back to YAML-reference-derived ports.

FlowCanvas caches ModuleSpec per step capability, kicked
off lazily on each build. When the spec lands, the canvas
rebuilds with:

- Per-field input ports on the LEFT (declared input names,
  in stable manifest order) — replaces the with_-keys-as-
  port-labels shape.
- Per-field output ports on the RIGHT — one anchor per
  declared output field. A flow like summarize that
  declares response/model_endpoint/model_name/model_digest
  now exposes four distinct anchors instead of fanning
  every downstream reference out of one collapsed point.
- Tooltips on each port label, picked from the field's
  description.<locale> with English fallback.

NodeGeometry refactored: heightFor(inputs, outputs) takes
max(inputs, outputs); outputPortY(index) for the new
multi-port output side; outputAnchorY() preserves the
legacy single-anchor fallback so edges still draw before
the spec resolves.

Edges + hit-tester now use _outputPortPosition(stepId,
fieldName: edge.fromField). Field index lookup falls
through to outputAnchorY when the spec isn't loaded yet.

Patterns: ported modern, classic, blueprint, minimal from
jai_client's CanvasPatternPainter. Dropdown now has seven
choices (dots, grid, modern, classic, blueprint, minimal,
blank).

Bumps fai_studio_flow_editor to 0.9.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 22:32:26 +02:00
1f5601a461 feat(editor): FaiEditorStyle + frosted glass panel + pattern/zoom dropdowns
Visual-effect knobs (FaiEditorStyle) the host can override —
distinct from ColorScheme, which stays orthogonal. Today
Studio passes it through FlowEditorPage's new style:
parameter; tomorrow a theme plugin can ship both colours and
effects in one move.

Two presets ship today: modern (frosted glass, gradient
backdrop, animated flow, shadowed nodes — the default) and
minimal (everything off, flat surfaces — accessibility /
reduce-motion / low-spec).

Properties panel rebuilt as a floating sidebar with
BackdropFilter blur when glass-style is on; falls back to
the pre-0.7 flush-divider layout under solid style.

Pattern + zoom controls promoted to PopupMenuButton dropdowns
so operators reach a specific zoom level / pattern in one
click instead of cycling.

Bumps fai_studio_flow_editor to 0.8.0.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 18:10:51 +02:00
fb8892687d feat(editor): port geometry fix + type colours + endpoint editor + unsaved badge
Four operator-visible improvements in one cut.

Port geometry (the "connection dots don't line up with the
labels" bug):
 - Header now has a fixed two-line structure (title + 16-px
   subtitle slot) regardless of node kind, so the body's
   port-row Y-offsets are identical across step nodes and
   endpoint nodes.
 - NodeGeometry constants rewritten so the inline 8-px dot
   inside each port row and the 16-px canvas-side dot both
   compute to the same Y. Port rows now anchor where the
   eye expects them.

Port type colours:
 - Inputs endpoint labels carry `name: type` (e.g.
   `doc: bytes`), so the FlowNode body parses the type and
   tints the inline dot per type: text → primary,
   bytes/file → tertiary, json → secondary, number → amber,
   unknown → muted. Step input ports stay muted until a
   future commit can read module manifests for type info.

Endpoint editor:
 - Inputs and outputs nodes are now selectable. Selecting
   opens a dedicated editor in the properties panel:
   add / rename / retype / remove entries graphically
   instead of forcing the operator into the text tab.
   Inputs editor offers a Type dropdown
   (text / bytes / json / file / number);
   outputs editor exposes name + expression
   (e.g. `$step.field`).

Unsaved badge:
 - The toolbar's tiny 8-px dirty dot was easy to miss.
   Replace it with a coloured "unsaved" / "ungespeichert"
   chip + colour the file name itself in the primary accent
   when dirty. Operators see at a glance that a flow has
   unsaved changes, which matters because the Discard
   confirmation prompt won't fire if they don't realise
   they made changes.

Version 0.3.0 -> 0.4.0 — first new editor feature surface
since 0.3.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 16:31:28 +02:00
307eaccc3e feat(editor): success snackbar after save
Brief 1.5 s green-check snackbar appears at the bottom of
the editor after a successful save. Confirmation matters
here because Cmd+S is the operator's most common keystroke
in any editor — without visible feedback, they'd need to
hunt for the dirty dot to verify the save landed.

The error path's existing snackbar (which has no duration
cap by design — operators must read errors) stays
unchanged.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 01:57:52 +02:00
9d21bd5318 feat(editor): empty-graph CTA + fit-to-screen button
Two polishing tweaks on the graph tab:

 - When the active flow has no steps, the canvas would have
   rendered as a near-empty grid (just inputs / outputs
   sidebars). Overlay a centred call-to-action with the
   graph icon, the "No steps yet" / "Noch keine Schritte"
   heading, a one-liner explainer, and a primary Add Step
   button that opens the capability picker directly.
 - Auto-fit zoom on first open of each flow + a floating
   "Fit to screen" button (bottom-right corner of the
   canvas viewport). The auto-fit re-runs only when the
   flow name changes, so the operator's manual pan / zoom
   on the current flow is preserved. The fit math expands
   the bounding box to include inputs + outputs pseudo-
   nodes too, so wide flows zoom out enough to show every
   port at once.

Fit math:
 - Walk every step's stored position + height; merge with
   the inputs sidebar's known position.
 - Compute scale that makes the bounding box fit the
   viewport with 80 px padding on each side.
 - Clamp to [0.4, 2.0] — InteractiveViewer's own bounds.
 - Translate so the box centres in the viewport.

`flutter analyze` clean, all 11 tests still pass.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 01:51:11 +02:00
870cbc29f7 feat(editor): three-tab WYSIWYG editor — graph / text / run
Full rewrite of the editor surface, layered on top of the
FlowGraph foundation. One in-memory flow drives three tabs
that the operator can flip between freely:

 - Graph: a drag-and-drop canvas. Nodes are step cards with
   port dots on their left (one per `with:` field) and a
   combined output port on the right. Pinned inputs and
   outputs pseudo-nodes sit at the left and right edges so
   every flow has a visually obvious source and sink. Pan +
   zoom via InteractiveViewer; drag a node by its body to
   reposition it (positions persisted to a sidecar JSON file
   under ~/.fai/data/flows/.layout/<name>.json — kept OUT of
   the YAML so `fai run` stays byte-stable).
 - Text: the existing YAML CodeField with expands:true so
   line 1 anchors at the top edge. YAML-aware syntax
   highlighting picks up the theme's primary / secondary /
   tertiary palette for keys / strings / numbers.
 - Run: an inputs form (text fields + file-pick), a Start
   button that calls the host's FlowRunDriver, a live step
   list driven by the driver's event stream (matches the
   `fai run` CLI rendering — ◻ pending, · running, ✔ done +
   duration, ✗ failed, ⏸ awaiting approval), and the typed
   outputs once the run resolves.

Source of truth = the YAML text. Graph edits emit fresh YAML
into the shared CodeController; text edits re-parse the
graph on a 350 ms debounce. Layout sidecar persists drag
positions only.

New public API (lib/fai_studio_flow_editor.dart):

  FlowEditorPage(
    initialFlowName: ...,
    locale: ...,
    runDriver: FlowRunDriver?,        // NEW — host bridge
    availableCapabilities: List<String>, // NEW — for the
                                          // capability picker
                                          // dialog when adding
                                          // a step
  )

The host (Studio) implements FlowRunDriver to bridge the
hub's gRPC SDK into the editor's event vocabulary. The
StepStarted/Completed/Failed/AwaitingApproval events are
shared verbatim with the CLI's run_progress renderer so
both surfaces speak the same visual language.

Files in this commit:
 - lib/src/editor_controller.dart       — shared state +
   debounced reparse loop
 - lib/src/run_driver.dart              — host bridge
   interface + event types
 - lib/src/widgets/flow_canvas.dart     — pan / zoom / drag /
   port-to-port connection drawing
 - lib/src/widgets/flow_node.dart       — node card primitive
   (module / approval / inputs / outputs variants)
 - lib/src/widgets/edge_painter.dart    — single CustomPainter
   for every edge + draft drag line, cubic bezier with
   arrow-head caps
 - lib/src/widgets/properties_panel.dart — right-side editor
   when a step is selected (rename id, change capability, add
   / remove / rename with-fields, delete step)
 - lib/src/widgets/capability_picker.dart — searchable list
   dialog used by Add-step
 - lib/src/widgets/run_tab.dart         — inputs form +
   live step progress + outputs renderer
 - lib/src/flow_editor_page.dart        — host scaffolding,
   toolbar, file list, three-tab body, keyboard shortcuts
 - lib/src/l10n.dart                    — EN + DE strings for
   every new label
 - lib/fai_studio_flow_editor.dart      — exports the new
   public types (FlowRunDriver, FlowRunEvent variants,
   FlowOutputValue variants)

flutter analyze: 0 issues. flutter test: 7/7 green.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 00:48:35 +02:00
daf6732893 fix(editor): true top-align via CodeField expands + move refresh
Two changes Stefan flagged on 0.1.3:

(1) Short files were still vertically centered. The previous
fix (CrossAxisAlignment.stretch on the Row) only stretched
the SingleChildScrollView viewport — the CodeField inside
still had its natural content-sized height and sat centered
in the larger viewport. Drop the SCV wrapper entirely and
set `expands: true` (with `minLines: null` + `maxLines:
null`, the underlying TextField's required combination) on
the CodeField. The widget now fills its parent's bounded
height, the line-number gutter runs the full editor height,
and line 1 sits at the literal top edge. Standard IDE
behaviour.

(2) The round outlined Refresh button sandwiched between
New (filled-tonal) and Save (filled-tonal) in the editor
toolbar was a visual-style outlier — Stefan called it
"dazwischen, sieht falsch aus". Refresh is also
semantically scoped to the file list, not the open
editor, so move it out of the editor toolbar entirely and
into a small FLOWS header bar at the top of the file-list
panel. The editor toolbar now reads:
  [Back] file.yaml ● [Spacer] [New] [Save] [Run]
— four filled-tonal/filled buttons, no style outliers.
The file-list panel reads:
  ┌───────────────────────┐
  │ FLOWS              ⟲  │
  ├───────────────────────┤
  │ flow-a                │
  │ flow-b                │
  └───────────────────────┘

Adds `listHeader` to FlowEditorStrings (de + en both
"FLOWS" — same word, all-caps).

Version 0.1.3 -> 0.1.4.

Signed-off-by: flemming-it <sf@flemming.it>
2026-06-01 00:05:49 +02:00
a7485cadb5 fix(editor): always top-align content, even for short files
Row was using CrossAxisAlignment.center by default, so a
short YAML file appeared visually centered in the editor
viewport — uncomfortable in any IDE-style surface where
content is expected to start at the top edge regardless of
how much of the viewport it fills.

Set CrossAxisAlignment.stretch on the outer Row so the
SingleChildScrollView fills the full vertical extent. The
SCV's default scroll origin is the top, so once the
viewport is taller than the content the file renders at
the top with empty space below.

Version 0.1.2 -> 0.1.3.

Signed-off-by: flemming-it <sf@flemming.it>
2026-05-31 23:29:00 +02:00
7655e0dc32 fix: use fullText setter — content from previous file no longer leaks
Stefan reported that opening a flow (hello) sometimes showed
content from a different flow (extract-with-approval). Cause:
flutter_code_editor's CodeController distinguishes between
`text` (the visible buffer, post-fold) and `fullText` (the
underlying source). Setting `_code.text = newFile` only
updates the visible buffer; the underlying `_code` data
structure retains the previous file's source, so subsequent
folding/scrolling can surface its content.

Switch all three load sites + the save site to the canonical
setter:

  Before  _code.text = text       (visible buffer only)
  After   _code.fullText = text   (replaces underlying source)

Save also moves to writing `_code.fullText` so any text the
operator collapsed behind a fold survives the round-trip.

Verified against flutter_code_editor 0.3.5 source — the
fullText setter calls `_updateCodeIfChanged` then re-emits
`TextEditingValue(text: _code.visibleText)`, which is the
documented "fully reset the editor" path.

Bump version 0.1.1 → 0.1.2. Studio's pubspec already pins
`ref: main` so the next `flutter pub upgrade` picks it up.

Signed-off-by: flemming-it <sf@flemming.it>
2026-05-30 16:07:44 +02:00
8b918f8f2a fix: dirty detection — compare fullText, snapshot after load
Stefan reported false-positive "Discard unsaved changes?"
dialogs when clicking around in the file list. Cause: the
naive `_code.text == _loadedText` check fired stale even
right after _code.text = text, because CodeController
silently rewrites the visible text during the assignment
(trailing-newline normalisation, fold marker insertion on
some YAML constructs, …) so _code.text deviated from the
just-stored _loadedText immediately.

Fix:
  * Drop `_loadedText` field, replace with `_baseline`.
  * Set `_code.text = ...` first (outside setState), then
    capture `_baseline = _code.fullText` inside setState.
    fullText is the canonical post-processed value, not the
    visible-after-folding text — comparing fullText against
    fullText eliminates the spurious diff.
  * _dirty getter now compares `_code.fullText != _baseline`.

Applied to all four load / save / new-flow sites.

Bump version 0.1.0 → 0.1.1. Studio's pubspec already pins
`ref: main` so the next `flutter pub get` picks the fix up.

Signed-off-by: flemming-it <sf@flemming.it>
2026-05-30 14:56:39 +02:00
F∆I Platform
51f9a1d2b1 feat: initial scaffold — swappable flow editor
Extracted from fai/studio's lib/pages/flow_editor.dart so the
editor can be swapped out independently of the host.

Public surface kept minimal — a single FlowEditorPage widget
with three named parameters (initialFlowName, locale, onRun).
The package brings its own design tokens, empty/error
widgets, l10n table; no host-internal types leak through.

Studio depends on this repo via pubspec.yaml git reference.
Forks point Studio at a different URL and rebuild.

See README.md for the swap recipe.

Signed-off-by: F∆I Platform <platform@flemming.ai>
2026-05-30 14:33:03 +02:00