From 291f997d31ee061a00c7ef1ef4356d3dcbb3a30a Mon Sep 17 00:00:00 2001 From: Jeroen Schweitzer Date: Wed, 17 Jun 2026 12:41:20 +0200 Subject: [PATCH] =?UTF-8?q?feat(settings):=20Activity=20category=20?= =?UTF-8?q?=E2=80=94=20conversation=20fold=20level=20(T-453)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first real settings tab. The Claude extension contributes an Activity SettingsCategory whose fold-level select binds to app.claude.activityFoldLevel; the activity panes already rebuild off the settings notifier, so a pick applies live. Proves the schema-driven engine end to end with a production setting — opening Settings now shows a populated, working category. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 25 +++++++-------- lib/builtin/claude/src/extension.dart | 32 +++++++++++++++++++ .../claude/extension_commands_test.dart | 12 +++++++ 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df4d818d..5228317c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,30 +25,29 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit. ✕, Esc, or a barrier tap. (T-445) - **Schema-driven settings engine.** Subsystems register a `SettingsCategory` - (a `SettingsCategoryContribution` routed into the kernel `SettingsRegistry`); - the panel renders it as carded sections of field rows — toggle, select, - text, number, and an "opens external file" button — each bound to a - `SettingsStore` key with help text and reset-to-default. Inputs recede to - `panelBackground` inside the elevated `panelHeader` cards (ui-design - surface.md). Categories are data; registering one surfaces a new tab. (T-448) + (via `SettingsCategoryContribution` → the kernel `SettingsRegistry`); the + panel renders it as carded sections of toggle/select/text/number/file rows, + each bound to a `SettingsStore` key with help and reset-to-default. + Registering a category surfaces a new tab. (T-448) - **Settings category rail.** The modal's left rail lists the registered categories (icon + title, data-driven from the registry) with an accent left-stripe + surfaceHi selection; picking one swaps the panel. (T-447) -- **Per-field scope tags.** Each settings field shows where its value lives — - folder = Project (`.clide`), globe = Always (`~/.clide`), circle-dashed = - Default/unset — colour-coded with a tooltip. Tapping opens a menu to move the - value between the scopes the key supports or reset it to default. Backed by - scope-explicit `SettingsStore` access (`rawAt`/`setAt`/`removeAt`/ - `effectiveLayer`/`writableLayers`); `ext.*` keys layer project-over-app, - `app.*`/`project.*` keys live in their prefix's file. (T-449) +- **Per-field scope tags.** Each field shows where its value lives — folder = + Project (`.clide`), globe = Always (`~/.clide`), circle-dashed = + Default/unset — with a tooltip and a menu to move the value between scopes or + reset it. Backed by scope-explicit `SettingsStore` access. (T-449) - **Cross-category settings search.** A search box atop the rail filters fields across every category; the panel shows the matches grouped under category subheaders (editable inline), and each rail row shows its match count with zero-match categories dimmed. (T-450) +- **Settings → Activity category.** The first real settings tab: the + conversation fold level (none / tools / thinking / everything) as a schema + field; picking a level applies live to the activity stream. (T-453) + ### Changed - **Theme picker relabelled "Theme…".** The ⌘K theme picker's command title diff --git a/lib/builtin/claude/src/extension.dart b/lib/builtin/claude/src/extension.dart index c806bef7..2a5595ce 100644 --- a/lib/builtin/claude/src/extension.dart +++ b/lib/builtin/claude/src/extension.dart @@ -100,6 +100,38 @@ class ClaudeExtension extends ClideExtension { title: 'Claude: cycle activity fold level', run: _cycleFoldLevel, ), + // Activity settings category (T-453) — the fold level as a schema field, + // written to kActivityFoldLevelKey; the panes already rebuild off the + // settings notifier, so picking a level applies live. + const SettingsCategoryContribution( + id: 'activity', + category: SettingsCategory( + id: 'activity', + title: 'Activity', + iconName: 'cards-three', + priority: 50, + sections: [ + SettingsSection( + label: 'Conversation', + fields: [ + SettingsField( + key: kActivityFoldLevelKey, + kind: SettingsFieldKind.select, + label: 'Fold level', + help: 'How aggressively the conversation folds tool calls, thinking, and results.', + defaultValue: 'tools', + options: [ + SettingsOption(value: 'none', label: 'Show everything'), + SettingsOption(value: 'tools', label: 'Fold tool calls'), + SettingsOption(value: 'thinking', label: 'Fold tools + thinking'), + SettingsOption(value: 'everything', label: 'Fold all but prose'), + ], + ), + ], + ), + ], + ), + ), // T-171: agent roster controls (D-6 CLI/UI parity). // Usage: clide claude.agent.show CommandContribution( diff --git a/test/builtin/claude/extension_commands_test.dart b/test/builtin/claude/extension_commands_test.dart index a3b84a80..338ba0c6 100644 --- a/test/builtin/claude/extension_commands_test.dart +++ b/test/builtin/claude/extension_commands_test.dart @@ -145,4 +145,16 @@ void main() { expect(activeClaudeConfig, isNull); }); }); + + group('Activity settings category (T-453)', () { + final category = ext.contributions.whereType().firstWhere((c) => c.id == 'activity').category; + + test('contributes an Activity category with the fold-level field', () { + expect(category.title, 'Activity'); + final field = category.sections.expand((s) => s.fields).firstWhere((f) => f.key == kActivityFoldLevelKey); + expect(field.kind, SettingsFieldKind.select); + expect(field.defaultValue, 'tools'); + expect(field.options.map((o) => o.value), containsAll(['none', 'tools', 'thinking', 'everything'])); + }); + }); }