Compare commits

..

2 commits

Author SHA1 Message Date
flemming-it
4ceb5bb567 fix(settings): reveal System-AI test result + refresh onboarding checklist
Some checks failed
Security / Security check (push) Failing after 1s
Two settings bugs the operator hit:

1. 'Test connection' appeared to do nothing. The result/error panel
   renders at the bottom of the System-AI editor's scrollable,
   600px-capped form, so on a tall dialog it lands below the fold —
   the footer button flickers 'Testing…' and nothing visible changes.
   The editor now holds a ScrollController and animates the content
   to reveal the outcome whenever a test result or error appears.

2. The welcome onboarding checklist kept 'connect System-AI'
   unchecked right after saving a working config. The checklist row
   opens the Settings dialog (an overlay, not a navigation), and on
   close the checklist never re-probed — so it showed the stale
   pre-config state. Both settings-opening rows (System-AI, MCP) now
   re-probe when the dialog closes; navigation-based rows already
   self-heal on return.

Studio suite green; flutter analyze clean.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
2026-07-13 14:15:17 +02:00
708bb74fa7 docs: add CLAUDE.md with UI verification gate (screenshot light+dark, click-flow)
Signed-off-by: Stefan Flemming <sf@flemming.it>
2026-07-13 14:10:14 +02:00
3 changed files with 58 additions and 2 deletions

22
CLAUDE.md Normal file
View file

@ -0,0 +1,22 @@
# fai_chain_studio — Ch∆In Studio (Flutter GUI)
Die grafische Oberfläche der Ch∆In-Plattform (Studio). Gehört dem `chain`-Agenten
(siehe `../../shared/AGENTS.md`). Plattform-/Naming-Konventionen und die
Zero-Learning-Curve-Regeln stammen aus dem Haupt-Repo `fai_chain` (dort `CLAUDE.md`).
## Design
Basis: `../../shared/DESIGN.md`. Studio-Abweichung: Zero-Learning-Curve führend
(Klartext, Inline-Hilfe, kopierbare Fehler); **Akzent bleibt Blau** (Wiedererkennung der
bestehenden Studio-Identität), nicht Petrol. Dark + Light sind Release-Gate.
## UI-Verifikations-Gate (PFLICHT)
Keine „fertig"-Meldung zu UI-Arbeit ohne:
1. **Frischen eigenen Screenshot-Beleg** der geänderten Screens — **hell UND dunkel**
(wie in `fai_chain` gefordert). Nicht aus dem Gedächtnis, nicht „sollte so aussehen".
2. **Durchgespielten Klick-Flow:** die betroffene Interaktion real im laufenden Studio
klicken/bedienen — nicht nur statisch gerendert prüfen. Sonst wiederkehrendes Muster:
„fertig gemeldet, aber der Klick tut nichts".
3. Bewertung gegen `shared/DESIGN.md` + die Studio-Abweichung oben; Fehlermeldungen
kopierbar; Theme-Umschaltung schaltet wirklich alles um.
Testzustände danach zurücksetzen. Stefan nutzt meist Safari — Web-Ansichten dort mitdenken.

View file

@ -641,6 +641,18 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
setState(() => _dismissed = true); setState(() => _dismissed = true);
} }
/// Open Settings (where System-AI + MCP are configured) and
/// re-probe when it closes. The Settings dialog overlays the
/// welcome page rather than navigating, so without this the
/// checklist kept showing the pre-config state (e.g. "connect
/// System-AI" stayed unchecked right after saving a working
/// config). Navigation-based rows self-heal on return; these
/// dialog-based ones did not.
Future<void> _openSettingsThenRefresh() async {
await ChainSettingsDialog.show(context);
if (mounted) await _refresh();
}
/// Localized plain-language label for the applied setup profile. /// Localized plain-language label for the applied setup profile.
String _profileLabel(AppLocalizations l) => switch (_setupProfile) { String _profileLabel(AppLocalizations l) => switch (_setupProfile) {
'enterprise' => l.setupProfileEnterprise, 'enterprise' => l.setupProfileEnterprise,
@ -664,13 +676,13 @@ class _OnboardingChecklistState extends State<_OnboardingChecklist> {
_aiOk, _aiOk,
l.welcomeChecklistAi, l.welcomeChecklistAi,
l.welcomeChecklistAiHint, l.welcomeChecklistAiHint,
() => ChainSettingsDialog.show(context), _openSettingsThenRefresh,
), ),
( (
_mcpOk, _mcpOk,
l.welcomeChecklistMcp, l.welcomeChecklistMcp,
l.welcomeChecklistMcpHint, l.welcomeChecklistMcpHint,
() => ChainSettingsDialog.show(context), _openSettingsThenRefresh,
), ),
( (
_moduleOk, _moduleOk,

View file

@ -144,6 +144,7 @@ class _FaiSystemAiEditorState extends State<ChainSystemAiEditor> {
String? _modelsError; String? _modelsError;
AskAiResult? _testResult; AskAiResult? _testResult;
String? _error; String? _error;
final _scrollController = ScrollController();
HardwareSnapshot? _hw; HardwareSnapshot? _hw;
CuratedSnapshot? _curated; CuratedSnapshot? _curated;
Map<String, CuratedModelInfo> _curatedById = const {}; Map<String, CuratedModelInfo> _curatedById = const {};
@ -200,9 +201,26 @@ class _FaiSystemAiEditorState extends State<ChainSystemAiEditor> {
_endpoint.dispose(); _endpoint.dispose();
_model.dispose(); _model.dispose();
_apiKeyEnv.dispose(); _apiKeyEnv.dispose();
_scrollController.dispose();
super.dispose(); super.dispose();
} }
/// Reveal the test-result / error panel. It renders at the bottom
/// of a scrollable, height-capped form, so on a tall dialog it
/// lands below the fold the operator taps "Test connection",
/// the footer button flickers, and nothing *visible* changes.
/// Scroll it into view so the outcome is never missed.
void _revealBottom() {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!_scrollController.hasClients) return;
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 250),
curve: Curves.easeOut,
);
});
}
void _onProviderChanged(_ProviderPreset p) { void _onProviderChanged(_ProviderPreset p) {
setState(() { setState(() {
_preset = p; _preset = p;
@ -255,6 +273,7 @@ class _FaiSystemAiEditorState extends State<ChainSystemAiEditor> {
_saving = false; _saving = false;
_error = e.toString(); _error = e.toString();
}); });
_revealBottom();
} }
} }
@ -297,11 +316,13 @@ class _FaiSystemAiEditorState extends State<ChainSystemAiEditor> {
_testing = false; _testing = false;
_testResult = r; _testResult = r;
}); });
_revealBottom();
} catch (e) { } catch (e) {
setState(() { setState(() {
_testing = false; _testing = false;
_error = e.toString(); _error = e.toString();
}); });
_revealBottom();
} }
} }
@ -375,6 +396,7 @@ class _FaiSystemAiEditorState extends State<ChainSystemAiEditor> {
content: ConstrainedBox( content: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 520, maxHeight: 600), constraints: const BoxConstraints(maxWidth: 520, maxHeight: 600),
child: SingleChildScrollView( child: SingleChildScrollView(
controller: _scrollController,
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,