persist the chosen theme per repo across loads (T-293)

ThemeController.select() applied live but never persisted, so every restart reset
to the first bundled theme. Add wireThemePersistence (facade): on theme change
write app.theme (global default) + project.theme (the open repo's
.clide/settings.yaml); on settings change — notably when a repo opens and its
project values load — restore the most specific saved theme (project then app).
The name encodes the -hc variant so high-contrast persists; an unknown/removed
theme is ignored so a stale value can't wedge startup.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 17:36:04 +02:00
co-authored by Claude Opus 4.8
parent af39f5b98a
commit 5c6674d739
4 changed files with 153 additions and 0 deletions
+2
View File
@@ -5,6 +5,7 @@ import 'package:clide/kernel/src/clipboard.dart';
import 'package:clide/kernel/src/commands/keybindings.dart';
import 'package:clide/kernel/src/keymap/keymap_service.dart';
import 'package:clide/kernel/src/text_zoom.dart';
import 'package:clide/kernel/src/theme/theme_persistence.dart';
import 'package:clide/kernel/src/toast.dart';
import 'package:clide/kernel/src/commands/palette.dart';
import 'package:clide/kernel/src/commands/registry.dart';
@@ -158,6 +159,7 @@ class KernelServices {
}
final theme = ThemeController(bundled: bundledThemes);
wireThemePersistence(theme, settings);
final panels = PanelRegistry();
final arrangement = LayoutArrangement();
final commands = CommandRegistry();
@@ -0,0 +1,40 @@
/// Persist the chosen theme and restore it across loads (T-293).
///
/// clide runs a single live [ThemeController]; this bridges it to the
/// [SettingsStore] so a choice survives a restart. On every theme change we
/// write `app.theme` (the global last-used default) and, when a repo is open,
/// `project.theme` in that repo's `.clide/settings.yaml`. Whenever the settings
/// change — notably when a repo opens and its project values load — we restore
/// the most specific saved theme: `project.theme`, else the global `app.theme`.
/// The current name already encodes the high-contrast variant (`<base>-hc`), so
/// the toggle persists for free. An unknown/removed theme name is ignored, so a
/// stale value can't wedge startup.
library;
import 'dart:async';
import 'package:clide/kernel/src/settings.dart';
import 'package:clide/kernel/src/theme/controller.dart';
void wireThemePersistence(ThemeController theme, SettingsStore settings) {
theme.addListener(() {
final name = theme.currentName;
// Global default — lets a brand-new / unthemed repo inherit the last choice.
if (settings.get<String>('app.theme') != name) {
unawaited(settings.set<String>('app.theme', name));
}
// Per-repo, when one is open.
if (settings.projectDir != null && settings.get<String>('project.theme') != name) {
unawaited(settings.set<String>('project.theme', name));
}
});
void restore() {
final saved = settings.get<String>('project.theme') ?? settings.get<String>('app.theme');
if (saved == null || saved == theme.currentName) return;
if (theme.available.any((d) => d.name == saved)) theme.select(saved);
}
settings.addListener(restore);
restore(); // apply the global default already loaded at boot
}