Commit 07dbbf6a authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix(chat): never end a failed run on a green tick

The closing line of every run was drawn green with a check mark regardless of
outcome. When the server's Claude login lapsed, every message died in 0s and
still ended "finished in 0s" beside a reassuring green tick — the one place a
failure most needs to be unmissable.

The footer now takes the message's real status and turns red with an error icon
when the run failed. Status is authoritative over the footer's wording because
every run stored before the server learned to write "failed" reads "finished";
for those, the word is corrected as well as the colour, so the text does not
contradict the icon next to it.
Co-Authored-By: 's avatarClaude Opus 5 (1M context) <noreply@anthropic.com>
parent d7ff664f
...@@ -658,7 +658,11 @@ class _Bubble extends StatelessWidget { ...@@ -658,7 +658,11 @@ class _Bubble extends StatelessWidget {
), ),
if (message.content.isNotEmpty || running) ...[ if (message.content.isNotEmpty || running) ...[
const SizedBox(height: 10), const SizedBox(height: 10),
AgentOutput(text: message.content, running: running), AgentOutput(
text: message.content,
running: running,
failed: message.status == RunStatus.failed,
),
], ],
], ],
), ),
......
...@@ -20,11 +20,20 @@ import '../core/theme.dart'; ...@@ -20,11 +20,20 @@ import '../core/theme.dart';
/// replies. Here the lines are first grouped into blocks, then each block is /// replies. Here the lines are first grouped into blocks, then each block is
/// rendered as what it actually is. /// rendered as what it actually is.
class AgentOutput extends StatelessWidget { class AgentOutput extends StatelessWidget {
const AgentOutput({super.key, required this.text, this.running = false}); const AgentOutput({
super.key,
required this.text,
this.running = false,
this.failed = false,
});
final String text; final String text;
final bool running; final bool running;
/// The run's actual outcome. Authoritative over the footer's wording, because
/// runs stored before the server learned to say "failed" all read "finished".
final bool failed;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final blocks = _parse(text); final blocks = _parse(text);
...@@ -61,7 +70,7 @@ class AgentOutput extends StatelessWidget { ...@@ -61,7 +70,7 @@ class AgentOutput extends StatelessWidget {
out.add(_Header(t.substring(1).trim())); out.add(_Header(t.substring(1).trim()));
} else if (t.startsWith('■')) { } else if (t.startsWith('■')) {
flushProse(); flushProse();
out.add(_Footer(t.substring(1).trim())); out.add(_Footer(t.substring(1).trim(), failed: failed));
} else if (t.startsWith('⚙')) { } else if (t.startsWith('⚙')) {
flushProse(); flushProse();
out.add(_Tool(t.substring(1).trim())); out.add(_Tool(t.substring(1).trim()));
...@@ -329,17 +338,31 @@ class _Header extends _Block { ...@@ -329,17 +338,31 @@ class _Header extends _Block {
); );
} }
/// The run's closing line. It used to be green unconditionally, so a run that
/// died in 0s on an auth error still ended in a reassuring green tick — the one
/// place a failure most needs to be unmissable.
class _Footer extends _Block { class _Footer extends _Block {
const _Footer(this.text); const _Footer(this.text, {this.failed = false});
final String text; final String text;
final bool failed;
bool get _bad => failed || text.startsWith('failed');
@override @override
Widget build(BuildContext context) => Row( Widget build(BuildContext context) {
mainAxisSize: MainAxisSize.min, final color = _bad ? Ember.bad : Ember.ok;
children: [ return Row(
const Icon(Icons.check_circle, size: 11, color: Ember.ok), mainAxisSize: MainAxisSize.min,
const SizedBox(width: 5), children: [
Text(text, style: const TextStyle(fontSize: 10.5, color: Ember.ok)), Icon(_bad ? Icons.error : Icons.check_circle, size: 11, color: color),
], const SizedBox(width: 5),
); Text(
// Older stored runs say "finished" even when they failed; do not
// repeat that lie next to a red icon.
_bad ? text.replaceFirst('finished', 'failed') : text,
style: TextStyle(fontSize: 10.5, color: color),
),
],
);
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment