Config tab → live control panel; sidebar styling pass (T-414)

The Claude sidebar's settings table was read-only 12px rows. It becomes
the power panel's core:

- model / effort / permission-mode rows are popover controls on the
  owned anchored-menu primitive (ClideAnchoredOverlay + ClideMenu),
  showing the LIVE session values (SessionStatus, falling back to the
  probe/settings) with the active option marked.
- Picking an option publishes the explicit slash command (`/effort
  xhigh`) on builtin.claude/command; the PRIMARY pane subscribes and
  executes it through the same _send routing the composer uses — the
  control and the typed command are one code path (D-6), which is also
  what lets the sidebar drive /effort's respawn flow without reaching
  into the pane. Only the primary pane listens (controls target the
  primary session; a second listener would double-execute).
- Styling pass (user request): shared meta tables move from 12px-
  everything to 13px labels/values, accent-coloured section headers,
  wider row pitch; control rows get hover affordance + caret.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 13:25:49 +02:00
co-authored by Claude Fable 5
parent 7c4ac182f6
commit 959ec7ddc4
6 changed files with 209 additions and 22 deletions
@@ -133,6 +133,33 @@ void main() {
expect(find.text('Claude environment not loaded.'), findsOneWidget);
});
testWidgets('a settings control publishes its slash command on pick (T-414)', (tester) async {
final dir = Directory.systemTemp.createTempSync('cfg');
addTearDown(() => dir.deleteSync(recursive: true));
final config = ClaudeConfig(globalDir: dir, cacheDir: dir);
final published = <Message>[];
final sub = f.services.messages.subscribe(publisher: 'builtin.claude', channel: 'command').listen(published.add);
addTearDown(sub.cancel);
await tester.pumpWidget(harness(f, sidebar(config: config, initialTab: SidebarTab.config)));
await tester.pumpAndSettle();
// The three live controls render alongside the read-only rows.
expect(find.text('model'), findsOneWidget);
expect(find.text('effort'), findsOneWidget);
expect(find.text('permission mode'), findsOneWidget);
// Open the effort control and pick a level → the explicit slash command
// goes out on the bus (the primary pane executes it via _send, D-6).
await tester.tap(find.bySemanticsLabel(RegExp('effort: .*Click to change.')));
await tester.pumpAndSettle();
await tester.tap(find.textContaining('xhigh'));
await tester.pumpAndSettle();
expect(published, hasLength(1));
expect(published.single.data['text'], '/effort xhigh');
});
testWidgets('a meta.tab message switches the sub-tab (T-413 slash navigation)', (tester) async {
await tester.pumpWidget(harness(f, sidebar(stats: stats)));
await tester.pumpAndSettle();