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(