diff --git a/src/TeamsISO.App.WinUI/Services/ThemeManager.cs b/src/TeamsISO.App.WinUI/Services/ThemeManager.cs
index 1553263..5e4880d 100644
--- a/src/TeamsISO.App.WinUI/Services/ThemeManager.cs
+++ b/src/TeamsISO.App.WinUI/Services/ThemeManager.cs
@@ -26,11 +26,26 @@ public sealed class ThemeManager
{
_uiSettings = new UISettings();
_uiSettings.ColorValuesChanged += OnSystemColorsChanged;
+
+ // Hydrate the preference from disk so the operator's choice
+ // survives across launches. Defaults to "System" if the prefs
+ // file is missing or unreadable (Load() catches its own errors).
+ try
+ {
+ var prefs = UIPreferences.Load();
+ if (prefs.Theme == "System" || prefs.Theme == "Dark" || prefs.Theme == "Light")
+ {
+ _preference = prefs.Theme;
+ }
+ }
+ catch
+ {
+ // Defensive — ThemeManager.Current is a static singleton; a
+ // throw here would prevent the app from getting any theme.
+ }
}
private readonly UISettings _uiSettings;
- // Default: System (follow OS app-mode). Override at runtime via Set();
- // persistence to UIPreferences.Theme lands in the view-model commit.
private string _preference = "System";
public string Preference => _preference;
@@ -63,7 +78,7 @@ public sealed class ThemeManager
return ResolveTheme();
}
- /// Set the preference and broadcast the resolved theme.
+ /// Set the preference, persist to disk, broadcast the resolved theme.
public void Set(string preference)
{
if (preference != "System" && preference != "Dark" && preference != "Light")
@@ -72,6 +87,8 @@ public sealed class ThemeManager
}
_preference = preference;
+ try { UIPreferences.SetTheme(preference); }
+ catch { /* persistence is best-effort */ }
Themed?.Invoke(this, ResolveTheme());
}
diff --git a/src/TeamsISO.App.WinUI/Services/UIPreferences.cs b/src/TeamsISO.App.WinUI/Services/UIPreferences.cs
new file mode 100644
index 0000000..da5a717
--- /dev/null
+++ b/src/TeamsISO.App.WinUI/Services/UIPreferences.cs
@@ -0,0 +1,85 @@
+using System;
+using System.IO;
+using System.Text.Json;
+
+namespace TeamsISO.App.WinUI.Services;
+
+///
+/// Persistent UI-side toggles shared between hosts via JSON at
+/// %LOCALAPPDATA%\TeamsISO\ui-prefs.json. The WPF host's copy at
+/// src/TeamsISO.App/Services/UIPreferences.cs reads/writes the same
+/// file with the same record shape — new fields added in one host's
+/// copy degrade gracefully in the other (JSON deserializer ignores
+/// unknown fields, missing fields fall back to defaults).
+///
+/// The WinUI copy adds the Theme field which the WPF host doesn't
+/// know about yet; when the WPF host's UIPreferences gets the same
+/// field, both hosts will share the operator's theme choice across
+/// host swaps.
+///
+public static class UIPreferences
+{
+ private static readonly object _gate = new();
+
+ private static string PrefsPath =>
+ Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+ "TeamsISO", "ui-prefs.json");
+
+ public enum SortMode { JoinOrder, Alphabetical, OnlineFirst, LoudestFirst }
+
+ /// The on-disk shape. New fields added here become opt-in for older files via default values.
+ public sealed record Prefs(
+ bool HideLocalSelf = true,
+ bool AutoDisableOnDeparture = false,
+ SortMode ParticipantSort = SortMode.JoinOrder,
+ bool MinimizeToTray = false,
+ bool ControlSurfaceLanReachable = false,
+ bool LaunchTeamsOnStartup = false,
+ bool AutoHideTeamsWindows = false,
+ bool AutoRecordOnCall = false,
+ bool EmbedTeamsWindow = false,
+ // Theme preference: "System" (follow OS app-mode), "Dark", or
+ // "Light". WinUI host owns the value for now; WPF host gets the
+ // same field in its UIPreferences when its theme system lands.
+ string Theme = "System");
+
+ public static Prefs Load()
+ {
+ try
+ {
+ if (!File.Exists(PrefsPath)) return new Prefs();
+ var json = File.ReadAllText(PrefsPath);
+ return JsonSerializer.Deserialize(json) ?? new Prefs();
+ }
+ catch
+ {
+ return new Prefs();
+ }
+ }
+
+ public static void Save(Prefs prefs)
+ {
+ try
+ {
+ lock (_gate)
+ {
+ var dir = Path.GetDirectoryName(PrefsPath);
+ if (!string.IsNullOrEmpty(dir)) Directory.CreateDirectory(dir);
+ var json = JsonSerializer.Serialize(prefs, new JsonSerializerOptions { WriteIndented = true });
+ File.WriteAllText(PrefsPath, json);
+ }
+ }
+ catch
+ {
+ // Disk full / permission denied — in-memory state still holds for this session.
+ }
+ }
+
+ /// Update just the Theme field without touching other prefs.
+ public static void SetTheme(string theme)
+ {
+ var current = Load();
+ Save(current with { Theme = theme });
+ }
+}