From 7f59334176b637810d69caf48ce88541e78634fd Mon Sep 17 00:00:00 2001 From: flemming-it Date: Fri, 19 Jun 2026 01:52:52 +0200 Subject: [PATCH] fix(studio): System-AI test-connection error is copyable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The System-AI editor rendered its failure (test connection, save, model pull) as a bare Text(_error) — selectable-but-not-copyable, the exact thing the operator needs to paste back. Route it through ChainErrorBox (selectable + one-tap copy button), and show a failed test result via ChainErrorBox too instead of a plain SelectableText. Signed-off-by: flemming-it --- lib/widgets/chain_system_ai_editor.dart | 26 +++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/widgets/chain_system_ai_editor.dart b/lib/widgets/chain_system_ai_editor.dart index 90d70d5..b113ac6 100644 --- a/lib/widgets/chain_system_ai_editor.dart +++ b/lib/widgets/chain_system_ai_editor.dart @@ -11,6 +11,7 @@ import '../data/hub.dart'; import '../l10n/app_localizations.dart'; import '../theme/theme.dart'; import '../theme/tokens.dart'; +import 'chain_error_box.dart'; import 'chain_pill.dart'; /// Provider preset metadata kept in sync with @@ -459,12 +460,10 @@ class _FaiSystemAiEditorState extends State { ], if (_error != null) ...[ const SizedBox(height: ChainSpace.md), - Text( - _error!, - style: theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.error, - ), - ), + // Copyable — every error the operator sees must be + // selectable + one-tap copyable (ChainErrorBox), never + // a bare Text they can't paste back. + ChainErrorBox(text: _error!, isError: true, maxHeight: 200), ], if (_testResult != null) ...[ const SizedBox(height: ChainSpace.md), @@ -650,10 +649,17 @@ class _TestResultPanel extends StatelessWidget { ], ), const SizedBox(height: 4), - SelectableText( - ok ? l.systemAiReplyPrefix(result.text) : result.text, - style: ChainTheme.mono(size: 11, color: theme.colorScheme.onSurface), - ), + // Success → the reply preview (selectable). Failure → the + // raw error in a ChainErrorBox with an explicit copy button, + // so the operator can paste the exact failure back. + if (ok) + SelectableText( + l.systemAiReplyPrefix(result.text), + style: + ChainTheme.mono(size: 11, color: theme.colorScheme.onSurface), + ) + else + ChainErrorBox(text: result.text, isError: true, maxHeight: 200), if (!ok && result.fixHint(l).isNotEmpty) ...[ const SizedBox(height: ChainSpace.xs), Text(