finish T-235 (persisted activity fold level); close T-132 via Q-34

T-235: app-scoped kActivityFoldLevelKey + a claude.activity.fold-level command
that cycles none→tools→thinking→everything; ClaudePane + team_panel_host read it
and re-fold live via the settings notifier. Unit tests for the helpers.

T-132 cleanup: the one blocked item (account/team token budget) is detached
(T-158), reframed as Q-34 'how + when to surface the budget given upstream
doesn't expose it', with T-158 as its backlog resolver. T-132 closed — all
doable work delivered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 19:24:19 +02:00
co-authored by Claude Opus 4.8
parent eb7b738c17
commit 86a52cd731
10 changed files with 173 additions and 1 deletions
@@ -30,6 +30,18 @@ enum FoldLevel {
everything,
}
/// Settings key for the persisted activity-card fold level (T-235). App-scoped:
/// a personal viewing preference, not per-repo.
const String kActivityFoldLevelKey = 'app.claude.activityFoldLevel';
/// Parse a stored fold-level name back to a [FoldLevel], defaulting to L1
/// ([FoldLevel.tools]) for null/unknown values.
FoldLevel foldLevelFromName(String? name) => FoldLevel.values.firstWhere((l) => l.name == name, orElse: () => FoldLevel.tools);
/// The next fold level in the cycle none → tools → thinking → everything → none
/// (T-235), for the toggle command.
FoldLevel nextFoldLevel(FoldLevel level) => FoldLevel.values[(level.index + 1) % FoldLevel.values.length];
/// A unit the conversation view renders: either a single first-class item or
/// a folded run of meta items.
sealed class RenderGroup {
+10
View File
@@ -10,6 +10,7 @@ import 'claude_composer.dart';
import 'claude_config.dart';
import 'claude_status.dart';
import 'clipboard_paste.dart';
import 'activity_cluster.dart' show foldLevelFromName, kActivityFoldLevelKey;
import 'conversation_controller.dart';
import 'conversation_view.dart';
import 'permission_mode_control.dart';
@@ -152,12 +153,20 @@ class _ClaudePaneState extends State<ClaudePane> {
// GlobalKey and spawns once, so without this it would keep the previous
// repo's session after a switch (T-269).
_projectSub = ClideKernel.of(context).events.on<ProjectOpened>().listen(_onProjectChanged);
// Re-fold the conversation when the activity fold-level setting changes
// (claude.activity.fold-level command, T-235).
ClideKernel.of(context).settings.addListener(_onSettingsChanged);
}
}
void _onSettingsChanged() {
if (mounted) setState(() {});
}
@override
void dispose() {
activeClaudeConfig?.removeListener(_onConfigChanged);
_kernel()?.settings.removeListener(_onSettingsChanged);
_projectSub?.cancel();
_projectSub = null;
_statusSub?.cancel();
@@ -493,6 +502,7 @@ class _ClaudePaneState extends State<ClaudePane> {
onTap: _focusComposerOnTap,
child: ConversationView(
controller: _conversation!,
foldLevel: foldLevelFromName(_kernel()?.settings.get<String>(kActivityFoldLevelKey)),
hiddenToolUseIds: _session?.promptedToolUseIds ?? const <String>{},
toolUseOutcomes: _session?.toolUseOutcomes ?? const <String, bool>{},
emptyState: ClaudeBanner(
+22
View File
@@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io';
import 'package:clide/clide.dart';
import 'package:clide/builtin/claude/src/activity_cluster.dart' show foldLevelFromName, kActivityFoldLevelKey, nextFoldLevel;
import 'package:clide/builtin/claude/src/claude_config.dart';
import 'package:clide/builtin/claude/src/claude_status.dart' show nextSafePermissionMode;
import 'package:clide/builtin/claude/src/claude_session_host.dart';
@@ -76,6 +77,15 @@ class ClaudeExtension extends ClideExtension {
title: 'Claude: session storage (disk usage + cleanup)',
run: _manageStorage,
),
// T-235: cycle how aggressively the activity card folds meta steps
// (none → tools → thinking → everything), persisted app-wide. The panes
// read kActivityFoldLevelKey and rebuild via the settings notifier.
CommandContribution(
id: 'claude.activity.fold-level',
command: 'claude.activity.fold-level',
title: 'Claude: cycle activity fold level',
run: _cycleFoldLevel,
),
// T-171: agent roster controls (D-6 CLI/UI parity).
// Usage: clide claude.agent.show <sessionId>
CommandContribution(
@@ -403,6 +413,18 @@ class ClaudeExtension extends ClideExtension {
/// this when they want a hard reset — after a Claude wedge or to start
/// completely fresh. All sessions are torn down through the orchestrator
/// (D-77); the primary will re-spawn and resume on the next pane build.
/// Cycle the persisted activity fold level (T-235). Reads the current value
/// from settings, advances none → tools → thinking → everything → none, and
/// writes it back; the panes listen to settings and re-fold live.
Future<IpcResponse> _cycleFoldLevel(List<String> args) async {
final ctx = _ctx;
if (ctx == null) return IpcResponse.ok(id: '', data: const {});
final current = foldLevelFromName(ctx.settings.get<String>(kActivityFoldLevelKey));
final next = nextFoldLevel(current);
await ctx.settings.set<String>(kActivityFoldLevelKey, next.name);
return IpcResponse.ok(id: '', data: {'foldLevel': next.name});
}
Future<IpcResponse> _killAllSessions(List<String> args) async {
final orch = _orchestrator;
if (orch == null) return IpcResponse.ok(id: '', data: const {});
+12 -1
View File
@@ -11,6 +11,7 @@ library;
import 'dart:async';
import 'package:clide/builtin/claude/src/conversation_controller.dart';
import 'package:clide/builtin/claude/src/activity_cluster.dart' show foldLevelFromName, kActivityFoldLevelKey;
import 'package:clide/builtin/claude/src/conversation_view.dart';
import 'package:clide/builtin/claude/src/transcript_publisher.dart';
import 'package:clide/kernel/kernel.dart';
@@ -202,7 +203,17 @@ class _TeammateTile extends StatelessWidget {
],
),
),
Expanded(child: ConversationView(controller: controller, wrapInSelectionArea: false)),
Expanded(
// Re-fold live with the activity fold-level setting (T-235).
child: ListenableBuilder(
listenable: ClideKernel.of(context).settings,
builder: (ctx, _) => ConversationView(
controller: controller,
wrapInSelectionArea: false,
foldLevel: foldLevelFromName(ClideKernel.of(ctx).settings.get<String>(kActivityFoldLevelKey)),
),
),
),
],
),
);