feat(settings): schema-driven settings engine (T-448)
The core of the settings panel (epic T-444). Subsystems describe a category as data — a SettingsCategory of carded SettingsSections of SettingsFields (toggle / select / text / number / opens-external-file), each bound to a SettingsStore key with help text, a default, and reset-to-default. Registration is declarative: a new SettingsCategoryContribution carries the category; the extension manager routes it into a new kernel SettingsRegistry (exposed on KernelServices), which the panel reads via ClideKernel. Adding a category is now pure data + a contribution — no widget code. SettingsCategoryView renders a category into carded sections per ui-design surface.md: panelHeader card fill, dividerColor border, inputs receding to panelBackground; select reuses the anchored-overlay menu, text/number commit on Enter or blur (numeric clamps to bounds). The modal panel now shows the selected/first registered category, falling back to the empty state. Tests: registry (sort / dedup / notify), contribution routing on activation, renderer (render + toggle/select write-through + reset), modal-with-category. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import 'package:clide/kernel/src/settings_schema.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Holds the [SettingsCategory] schemas subsystems register against the kernel
|
||||
/// (via `SettingsCategoryContribution`, routed by the extension manager). The
|
||||
/// settings panel reads this to build its rail + panels (T-447/T-448) and
|
||||
/// rebuilds when the set changes.
|
||||
class SettingsRegistry extends ChangeNotifier {
|
||||
final Map<String, SettingsCategory> _byId = <String, SettingsCategory>{};
|
||||
|
||||
/// Registered categories, sorted by (priority, then case-insensitive title).
|
||||
List<SettingsCategory> get categories {
|
||||
final list = _byId.values.toList()
|
||||
..sort((a, b) {
|
||||
final p = a.priority.compareTo(b.priority);
|
||||
return p != 0 ? p : a.title.toLowerCase().compareTo(b.title.toLowerCase());
|
||||
});
|
||||
return List.unmodifiable(list);
|
||||
}
|
||||
|
||||
SettingsCategory? byId(String id) => _byId[id];
|
||||
|
||||
/// Register a category. Throws on a duplicate id — a collision is a wiring
|
||||
/// bug that should roll the contributing extension's activation back, the
|
||||
/// same way duplicate command/slot ids do.
|
||||
void register(SettingsCategory category) {
|
||||
if (_byId.containsKey(category.id)) {
|
||||
throw StateError('duplicate settings category id: ${category.id}');
|
||||
}
|
||||
_byId[category.id] = category;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void unregister(String id) {
|
||||
if (_byId.remove(id) != null) notifyListeners();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user