add a native startup banner to the empty Claude pane (T-149)

Replaces the bare "Waiting for Claude…" empty state with ClaudeBanner:
clide's own logo, a "Claude" label, the session role (primary /
secondary N), the workspace (home-collapsed), the tmux status line, and
a warming-up hint. ConversationView gains an optional emptyState widget;
the pane supplies the banner from data it already has.

Fully owned — no tmux capture-pane, no Anthropic artwork. The "Claude"
label uses Anthropic's published accent #d97757 nominatively; recorded
under a new trademark_notices section in assets/licenses.yaml (clide is
unaffiliated, bundles no Anthropic logo/artwork). Also fixes a stale
forkpty->pty reference in that file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-23 09:26:07 +02:00
co-authored by Claude Opus 4.7
parent b52283b7c0
commit af483c2c34
8 changed files with 147 additions and 5 deletions
@@ -6,6 +6,7 @@ library;
import 'dart:async';
import 'package:clide/builtin/claude/src/claude_banner.dart';
import 'package:clide/builtin/claude/src/conversation_controller.dart';
import 'package:clide/builtin/claude/src/conversation_view.dart';
import 'package:clide/builtin/claude/src/transcript_publisher.dart';
@@ -137,6 +138,21 @@ void main() {
expect(find.text('Waiting for Claude…'), findsOneWidget);
});
testWidgets('empty controller shows the provided emptyState instead', (tester) async {
final stream = StreamController<ConversationItem>.broadcast();
final c = ConversationController(stream: stream.stream);
addTearDown(c.dispose);
await tester.pumpWidget(harness(
f,
ConversationView(
controller: c,
emptyState: const ClideText('CUSTOM EMPTY'),
),
));
expect(find.text('CUSTOM EMPTY'), findsOneWidget);
expect(find.text('Waiting for Claude…'), findsNothing);
});
testWidgets('renders a card per item kind with role/tool labels', (tester) async {
await pumpWith(tester, [
_user('a question'),
@@ -183,4 +199,27 @@ void main() {
expect(copied, contains('answer text'));
});
});
group('ClaudeBanner', () {
late KernelFixture f;
setUp(() async => f = await KernelFixture.create());
tearDown(() => f.dispose());
testWidgets('shows role, workspace, status, and a hint', (tester) async {
await tester.pumpWidget(harness(
f,
const ClaudeBanner(
role: 'primary',
workspace: '/work/space',
statusLine: 'tmux · clide-claude-x',
),
));
await tester.pump();
expect(find.text('Claude'), findsOneWidget);
expect(find.text('primary'), findsOneWidget);
expect(find.text('/work/space'), findsOneWidget);
expect(find.text('tmux · clide-claude-x'), findsOneWidget);
expect(find.textContaining('Warming up'), findsOneWidget);
});
});
}