feat(studio): central error helpers + inline log viewer + Today CTAs
Some checks failed
Security / Security check (push) Failing after 1s

Bundles the error-UX overhaul and the inline log viewer:

  - New `FaiLog` (`~/.fai/logs/studio-errors.log`, 256 KiB rotation,
    JSON-per-line). Every operator-visible failure is appended so
    `fai admin doctor` and the new viewer can show the trail
    without the operator having to reproduce the failure.
  - New `showFaiErrorSnack` / `showFaiErrorDialog` helpers wrap
    `FaiErrorBox` in copyable surfaces; 27 ad-hoc
    `SnackBar(content: Text(e.toString()))` sites swept to use
    them (settings, doctor, audit, store, module sheet, system-AI
    editor, flow output).
  - New `FaiLogViewer` modal (`showFaiLogViewer`) renders log
    files inline with line numbers, JSON-key + `[level]` token
    colouring, Copy-all, Refresh, Open-externally. Doctor's
    daemon-paths panel grows a "View" button next to "Open" for
    every `.log` row and now also lists the Studio errors log.
  - Today carousel: CTAs now actually re-run search after a
    `filterCategory` / `runQuery` story is tapped (was only
    flipping the chip state). Fallback story list bumped to 8.
  - Editor bumped to git ref carrying 0.15.0 (type-token
    colouring + analyzer diagnostics).

Studio bumped to 0.62.0.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-06-04 02:08:15 +02:00
parent 2731062a13
commit 8f5cd2528b
22 changed files with 1087 additions and 134 deletions

View file

@ -1423,5 +1423,23 @@
"placeholders": {
"name": {"type": "String"}
}
}
},
"buttonView": "Ansehen",
"logViewerTitle": "{title} — letzte {count} Zeilen",
"@logViewerTitle": {
"placeholders": {
"title": {"type": "String"},
"count": {"type": "int"}
}
},
"logViewerEmpty": "Log-Datei ist leer oder existiert noch nicht.",
"logViewerCopyAll": "Alles kopieren",
"logViewerOpenExternal": "Im Standardeditor öffnen",
"logViewerLineCount": "{count, plural, =1{1 Zeile} other{{count} Zeilen}}",
"@logViewerLineCount": {
"placeholders": {
"count": {"type": "int"}
}
},
"doctorPathStudioErrors": "Studio-Fehler"
}

View file

@ -1426,5 +1426,23 @@
"placeholders": {
"name": {"type": "String"}
}
}
},
"buttonView": "View",
"logViewerTitle": "{title} — last {count} lines",
"@logViewerTitle": {
"placeholders": {
"title": {"type": "String"},
"count": {"type": "int"}
}
},
"logViewerEmpty": "Log file is empty or does not exist yet.",
"logViewerCopyAll": "Copy all",
"logViewerOpenExternal": "Open in default editor",
"logViewerLineCount": "{count, plural, =1{1 line} other{{count} lines}}",
"@logViewerLineCount": {
"placeholders": {
"count": {"type": "int"}
}
},
"doctorPathStudioErrors": "Studio errors"
}

View file

@ -3913,6 +3913,48 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'A flow named \"{name}\" already exists.'**
String flowEditorAlreadyExists(String name);
/// No description provided for @buttonView.
///
/// In en, this message translates to:
/// **'View'**
String get buttonView;
/// No description provided for @logViewerTitle.
///
/// In en, this message translates to:
/// **'{title} — last {count} lines'**
String logViewerTitle(String title, int count);
/// No description provided for @logViewerEmpty.
///
/// In en, this message translates to:
/// **'Log file is empty or does not exist yet.'**
String get logViewerEmpty;
/// No description provided for @logViewerCopyAll.
///
/// In en, this message translates to:
/// **'Copy all'**
String get logViewerCopyAll;
/// No description provided for @logViewerOpenExternal.
///
/// In en, this message translates to:
/// **'Open in default editor'**
String get logViewerOpenExternal;
/// No description provided for @logViewerLineCount.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 line} other{{count} lines}}'**
String logViewerLineCount(int count);
/// No description provided for @doctorPathStudioErrors.
///
/// In en, this message translates to:
/// **'Studio errors'**
String get doctorPathStudioErrors;
}
class _AppLocalizationsDelegate

View file

@ -2282,4 +2282,35 @@ class AppLocalizationsDe extends AppLocalizations {
String flowEditorAlreadyExists(String name) {
return 'Ein Flow namens \"$name\" existiert bereits.';
}
@override
String get buttonView => 'Ansehen';
@override
String logViewerTitle(String title, int count) {
return '$title — letzte $count Zeilen';
}
@override
String get logViewerEmpty => 'Log-Datei ist leer oder existiert noch nicht.';
@override
String get logViewerCopyAll => 'Alles kopieren';
@override
String get logViewerOpenExternal => 'Im Standardeditor öffnen';
@override
String logViewerLineCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count Zeilen',
one: '1 Zeile',
);
return '$_temp0';
}
@override
String get doctorPathStudioErrors => 'Studio-Fehler';
}

View file

@ -2287,4 +2287,35 @@ class AppLocalizationsEn extends AppLocalizations {
String flowEditorAlreadyExists(String name) {
return 'A flow named \"$name\" already exists.';
}
@override
String get buttonView => 'View';
@override
String logViewerTitle(String title, int count) {
return '$title — last $count lines';
}
@override
String get logViewerEmpty => 'Log file is empty or does not exist yet.';
@override
String get logViewerCopyAll => 'Copy all';
@override
String get logViewerOpenExternal => 'Open in default editor';
@override
String logViewerLineCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count lines',
one: '1 line',
);
return '$_temp0';
}
@override
String get doctorPathStudioErrors => 'Studio errors';
}