Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/OpenClaw.Shared/SettingsData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ public record class SettingsData
/// "User interface" section.
/// </summary>
public bool UseLegacyWebChat { get; set; } = false;
public string AppTheme { get; set; } = "System";
public bool? ShowDiagnostics { get; set; }
public List<UserNotificationRule>? UserRules { get; set; }

// ── MXC sandbox ─────────────────────────────────────────────────────
Expand Down
46 changes: 37 additions & 9 deletions src/OpenClaw.Tray.WinUI/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public partial class App : Application, OpenClawTray.Services.IAppCommands
public GatewayRegistry? Registry => _gatewayRegistry;
public GatewayConnectionManager? ConnectionManager => _connectionManager;
internal SettingsManager Settings => _settings ?? throw new InvalidOperationException("Settings are not initialized.");
internal SettingsManager? SettingsOrNull => _settings;
internal string DataDirectoryPath => DataPath;

/// <summary>The active hub window, exposed so pages can obtain an HWND for file pickers.</summary>
Expand Down Expand Up @@ -699,6 +700,7 @@ _dispatcherQueue is null
!prewarmIsBootstrapToken)
{
_chatWindow = new ChatWindow(prewarmUrl, prewarmToken);
ApplyThemePreference(_chatWindow);
// Window is created but hidden — WebView2 initializes in the background
}

Expand Down Expand Up @@ -736,6 +738,7 @@ private void InitializeKeepAliveWindow()
// This prevents GC/threading issues when creating windows after idle
_keepAliveWindow = new Window();
_keepAliveWindow.Content = new Microsoft.UI.Xaml.Controls.Grid();
ApplyThemePreference(_keepAliveWindow);
_keepAliveWindow.AppWindow.IsShownInSwitchers = false;

// Move off-screen and set minimal size
Expand Down Expand Up @@ -768,10 +771,28 @@ private void InitializeTrayMenuWindow()
{
// Pre-create menu window once - reuse to avoid crash on window creation after idle
_trayMenuWindow = new TrayMenuWindow();
ApplyThemePreference(_trayMenuWindow);
_trayMenuWindow.MenuItemClicked += OnTrayMenuItemClicked;
// Don't close - just hide
}

internal void ApplyThemePreferenceToOpenWindows()
{
ApplyThemePreference(_keepAliveWindow);
ApplyThemePreference(_hubWindow);
ApplyThemePreference(_trayMenuWindow);
ApplyThemePreference(_chatWindow);
ApplyThemePreference(_connectionStatusWindow);
}

private void ApplyThemePreference(Window? window)
{
if (_settings is null)
return;

ThemeHelper.ApplyTheme(window, _settings.AppTheme);
}

private void OnTrayIconSelected(TrayIcon sender, TrayIconEventArgs e)
{
if (_connectionManager?.CurrentSnapshot.OperatorState == RoleConnectionState.Connected)
Expand Down Expand Up @@ -806,6 +827,7 @@ internal void ShowChatWindow()
if (_chatWindow == null)
{
_chatWindow = new ChatWindow(url, token);
ApplyThemePreference(_chatWindow);
}

// Bug 2: cached ChatWindow may have been pre-warmed with empty/stale credentials
Expand Down Expand Up @@ -3334,6 +3356,7 @@ internal void ShowHub(string? navigateTo = null, bool activate = true)
if (_hubWindow == null || _hubWindow.IsClosed)
{
_hubWindow = new HubWindow();
ApplyThemePreference(_hubWindow);
_hubWindow.AppModel = _appState;
_hubWindow.BindAppNotifications(_appNotificationService!);
_hubWindow.ApplyNavPaneState(_settings!);
Expand Down Expand Up @@ -3489,7 +3512,6 @@ private void OnSettingsSaved(object? sender, EventArgs e)
WireAppCapabilityHandlers();
}

// Non-connection settings always applied regardless of impact
if (_settings!.GlobalHotkeyEnabled)
{
_globalHotkey ??= new GlobalHotkeyService();
Expand All @@ -3508,19 +3530,24 @@ private void OnSettingsSaved(object? sender, EventArgs e)
AutoStartManager.SetAutoStartAsync(_settings.AutoStart),
"[App] Failed to apply auto-start setting");

// Notify ad-hoc listeners (e.g. ChatWindow may be alive but not
// owned by the hub) that settings have changed. Marshal onto the
// UI thread because IAppCommands.NotifySettingsSaved is a public
// entry point that may be invoked from background work; existing
// handlers (DebugPage, ChatWindow) update UI directly and would
// crash if dispatched from a non-UI thread (Hanselman v2 #7).
// Apply UI-only settings and notify ad-hoc listeners. This public
// entry point can be invoked from background work, while existing
// listeners update UI directly.
void ApplyUiSettingsAndNotify()
{
ApplyThemePreferenceToOpenWindows();
if (_hubWindow is { IsClosed: false })
_hubWindow.RefreshDiagnosticsNavVisibility();
SettingsChanged?.Invoke(this, EventArgs.Empty);
}

if (_dispatcherQueue != null && !_dispatcherQueue.HasThreadAccess)
{
_dispatcherQueue.TryEnqueue(() => SettingsChanged?.Invoke(this, EventArgs.Empty));
_dispatcherQueue.TryEnqueue(ApplyUiSettingsAndNotify);
}
else
{
SettingsChanged?.Invoke(this, EventArgs.Empty);
ApplyUiSettingsAndNotify();
}
}

Expand Down Expand Up @@ -3581,6 +3608,7 @@ private void ShowConnectionStatusWindow()
_connectionManager!.Diagnostics,
_gatewayRegistry,
_connectionManager);
ApplyThemePreference(_connectionStatusWindow);
_connectionStatusWindow.Activate();
}

Expand Down
28 changes: 28 additions & 0 deletions src/OpenClaw.Tray.WinUI/Helpers/DiagnosticsGate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#if OPENCLAW_TRAY_TESTS
namespace OpenClawTray.Helpers;

internal static class DiagnosticsGate
{
public static bool BuildDefault => true;
public static bool IsVisible => BuildDefault;
}
#else
using Microsoft.UI.Xaml;

namespace OpenClawTray.Helpers;

/// <summary>
/// Gates the Diagnostics page. Diagnostics remain visible by default for
/// compatibility with existing installs; users can explicitly hide or show them
/// via Settings (SettingsManager.ShowDiagnosticsOverride).
/// </summary>
internal static class DiagnosticsGate
{
public static bool BuildDefault =>
true;

/// <summary>Effective visibility: user override if set, else the build default.</summary>
public static bool IsVisible =>
(Application.Current as OpenClawTray.App)?.SettingsOrNull?.ShowDiagnosticsEffective ?? BuildDefault;
}
#endif
14 changes: 14 additions & 0 deletions src/OpenClaw.Tray.WinUI/Helpers/ThemeHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ public static ElementTheme GetCurrentTheme()
return IsDarkMode() ? ElementTheme.Dark : ElementTheme.Light;
}

public static ElementTheme GetRequestedTheme(string? preference) =>
SettingsManager.NormalizeAppTheme(preference) switch
{
SettingsManager.AppThemeLight => ElementTheme.Light,
SettingsManager.AppThemeDark => ElementTheme.Dark,
_ => ElementTheme.Default
};

public static void ApplyTheme(Window? window, string? preference)
{
if (window?.Content is FrameworkElement rootElement)
rootElement.RequestedTheme = GetRequestedTheme(preference);
}

public static Color GetAccentColor()
{
// Returns the user's Windows accent color (previously hard-coded to
Expand Down
110 changes: 0 additions & 110 deletions src/OpenClaw.Tray.WinUI/Pages/AboutPage.xaml

This file was deleted.

Loading
Loading