From 8b918f8f2afe5babe663897aa20483708a980c4b Mon Sep 17 00:00:00 2001 From: flemming-it Date: Sat, 30 May 2026 14:56:39 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20dirty=20detection=20=E2=80=94=20compare?= =?UTF-8?q?=20fullText,=20snapshot=20after=20load?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- lib/src/flow_editor_page.dart | 30 +++++++++++++++++++++--------- pubspec.yaml | 2 +- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/lib/src/flow_editor_page.dart b/lib/src/flow_editor_page.dart index 0f3f301..b46b4b2 100644 --- a/lib/src/flow_editor_page.dart +++ b/lib/src/flow_editor_page.dart @@ -52,7 +52,14 @@ class _FlowEditorPageState extends State { late final CodeController _code; late final FlowEditorStrings _l; String? _activeName; - String _loadedText = ''; + // Baseline captured AFTER the CodeController has finished + // processing the loaded text (line-ending normalisation, + // trailing-newline handling, fold-marker insertion, …). + // Comparing _code.fullText against this baseline avoids the + // false-positive "discard unsaved changes?" prompts the + // naive _code.text == _loadedText check used to trigger on + // files the controller silently rewrote during load. + String _baseline = ''; late Future> _files; Object? _runError; Map? _runOutputs; @@ -85,7 +92,7 @@ class _FlowEditorPageState extends State { if (mounted) setState(() {}); } - bool get _dirty => _activeName != null && _code.text != _loadedText; + bool get _dirty => _activeName != null && _code.fullText != _baseline; Future> _listFiles() async { final dir = Directory(_defaultFlowsDir()); @@ -115,10 +122,15 @@ class _FlowEditorPageState extends State { if (!f.existsSync()) return; final text = await f.readAsString(); if (!mounted) return; + // Set the controller text first, then snapshot fullText + // as the baseline. Reading fullText after the assignment + // captures whatever line-ending normalisation / fold- + // marker insertion CodeController applied, so the dirty + // check is comparing apples to apples. + _code.text = text; setState(() { _activeName = name; - _loadedText = text; - _code.text = text; + _baseline = _code.fullText; _runOutputs = null; _runError = null; }); @@ -130,10 +142,10 @@ class _FlowEditorPageState extends State { if (keep == false || !mounted) return; } final text = await File(f.path).readAsString(); + _code.text = text; setState(() { _activeName = f.name; - _loadedText = text; - _code.text = text; + _baseline = _code.fullText; _runOutputs = null; _runError = null; }); @@ -168,7 +180,7 @@ class _FlowEditorPageState extends State { await f.writeAsString(_code.text, flush: true); if (!mounted) return; setState(() { - _loadedText = _code.text; + _baseline = _code.fullText; _files = _listFiles(); }); } catch (e) { @@ -215,10 +227,10 @@ outputs: } await f.writeAsString(template, flush: true); if (!mounted) return; + _code.text = template; setState(() { _activeName = name; - _loadedText = template; - _code.text = template; + _baseline = _code.fullText; _files = _listFiles(); }); } catch (e) { diff --git a/pubspec.yaml b/pubspec.yaml index e0f65d0..1e9dc3b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: fai_studio_flow_editor description: Swappable inline YAML editor for F∆I Studio flows. -version: 0.1.0 +version: 0.1.1 publish_to: 'none' repository: https://git.flemming.ai/fai/studio-flow-editor