Skip to content

Commit 7be4cef

Browse files
committed
Enhance theming, UI behavior, perf & add CI
Multiple improvements across theming, UI flow, performance refresh logic, and CI: - Add CI DevSecOps GH Action workflow: .NET restore/build (Debug/Release), dependency audit, and gitleaks secret scan. - Theming: preload and apply theme at startup; add HasUserThemePreference to settings model; detect legacy theme flag when loading settings; ThemeService now reads system theme from registry (GetSystemUsesDarkTheme) and exposes system-use check. - Richer theme resources: major updates to FluentDark.xaml and FluentLight.xaml (expanded color palette, control styles/templates, system brushes) to modernize visuals and controls. - Startup/UI behavior: improved autostart/minimize-to-tray handling, consistent ShowInTaskbar/Visibility logic, OnSourceInitialized hook to reapply caption theme, and apply DWM immersive dark caption via DwmSetWindowAttribute. - Robust UI error handling: throttle dispatcher exception dialog to avoid multiple dialogs using an interlocked flag and short time window. - MainWindow refactor: introduce ShowWindowFromTray helper, unify show/activate flows, apply caption theming, and adjust several shortcut handlers to use the helper. - Tab switching: add SemaphoreSlim guard and improved selection logic to debounce concurrent tab changes; prompt/save/discard unsaved settings when switching away from Settings; dispose semaphore on shutdown. - PerformanceViewModel: debounce/gate top-process refreshes with SemaphoreSlim and a pending flag to avoid overlapping refreshes; update TopCpuProcesses on UI dispatcher and refresh selected process impact safely. - Misc: small XAML cleanups (Settings tab header simplification), add COMPLIANCE_AUDIT.md to .gitignore, and various logging/error message tweaks. These changes aim to reduce UI flicker on startup, respect user/system theme preferences, harden UI interactions, prevent concurrent refresh race conditions, and add CI/DevSecOps checks.
1 parent 5df2776 commit 7be4cef

17 files changed

Lines changed: 1276 additions & 544 deletions

.github/workflows/ci-devsecops.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: CI DevSecOps
2+
3+
on:
4+
push:
5+
branches: ["main", "master"]
6+
pull_request:
7+
8+
jobs:
9+
build-and-scan:
10+
runs-on: windows-latest
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
16+
- name: Setup .NET
17+
uses: actions/setup-dotnet@v4
18+
with:
19+
dotnet-version: "8.0.x"
20+
21+
- name: Restore
22+
run: dotnet restore "ThreadPilot.csproj"
23+
24+
- name: Build Debug
25+
run: dotnet build "ThreadPilot.csproj" --configuration Debug --no-restore
26+
27+
- name: Build Release
28+
run: dotnet build "ThreadPilot.csproj" --configuration Release --no-restore
29+
30+
- name: Dependency Audit
31+
run: dotnet list "ThreadPilot.csproj" package --vulnerable --include-transitive
32+
33+
- name: Secret Scan
34+
uses: gitleaks/gitleaks-action@v2
35+
env:
36+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,4 @@ GAME_BOOST_VALIDATION.md
131131
PROJECT_STRUCTURE.md
132132
TRADEMARK.md
133133
UI_STYLE_GUIDE.md
134+
COMPLIANCE_AUDIT.md

App.xaml.cs

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ namespace ThreadPilot
3434
public partial class App : System.Windows.Application
3535
{
3636
private Mutex? _singleInstanceMutex;
37+
private int _uiExceptionDialogOpen;
38+
private DateTime _lastUiExceptionDialogUtc = DateTime.MinValue;
3739
public IServiceProvider ServiceProvider { get; private set; }
3840

3941
public App()
@@ -135,6 +137,24 @@ protected override void OnStartup(StartupEventArgs e)
135137
}
136138
#endif
137139

140+
try
141+
{
142+
var settingsService = ServiceProvider.GetRequiredService<IApplicationSettingsService>();
143+
var themeService = ServiceProvider.GetRequiredService<IThemeService>();
144+
145+
Task.Run(async () => await settingsService.LoadSettingsAsync()).GetAwaiter().GetResult();
146+
var settings = settingsService.Settings;
147+
var useDarkTheme = settings.HasUserThemePreference
148+
? settings.UseDarkTheme
149+
: themeService.GetSystemUsesDarkTheme();
150+
151+
themeService.ApplyTheme(useDarkTheme);
152+
}
153+
catch (Exception ex)
154+
{
155+
logger.LogWarning(ex, "Failed to preload theme settings during startup");
156+
}
157+
138158
var mainWindow = ServiceProvider.GetRequiredService<MainWindow>();
139159

140160
// Handle startup behavior with comprehensive error handling
@@ -148,15 +168,27 @@ protected override void OnStartup(StartupEventArgs e)
148168
throw new InvalidOperationException("MainWindow could not be created");
149169
}
150170

151-
// Show the window with explicit visibility settings
152-
mainWindow.Visibility = Visibility.Visible;
153-
mainWindow.WindowState = (startMinimized || isAutostart)
154-
? WindowState.Minimized
155-
: WindowState.Normal;
156-
mainWindow.Show();
157-
if (mainWindow.WindowState != WindowState.Minimized)
171+
if (isAutostart)
172+
{
173+
mainWindow.ShowInTaskbar = false;
174+
mainWindow.Visibility = Visibility.Hidden;
175+
mainWindow.WindowState = WindowState.Minimized;
176+
mainWindow.Show();
177+
mainWindow.Hide();
178+
}
179+
else
158180
{
159-
mainWindow.Activate();
181+
// Show the window with explicit visibility settings
182+
mainWindow.ShowInTaskbar = true;
183+
mainWindow.Visibility = Visibility.Visible;
184+
mainWindow.WindowState = startMinimized
185+
? WindowState.Minimized
186+
: WindowState.Normal;
187+
mainWindow.Show();
188+
if (mainWindow.WindowState != WindowState.Minimized)
189+
{
190+
mainWindow.Activate();
191+
}
160192
}
161193

162194
logger.LogInformation("Main window displayed successfully");
@@ -247,6 +279,21 @@ private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledEx
247279

248280
logger?.LogError(e.Exception, "Unhandled dispatcher exception occurred");
249281

282+
if (Interlocked.CompareExchange(ref _uiExceptionDialogOpen, 1, 0) != 0)
283+
{
284+
e.Handled = true;
285+
return;
286+
}
287+
288+
if (DateTime.UtcNow - _lastUiExceptionDialogUtc < TimeSpan.FromSeconds(2))
289+
{
290+
e.Handled = true;
291+
Interlocked.Exchange(ref _uiExceptionDialogOpen, 0);
292+
return;
293+
}
294+
295+
_lastUiExceptionDialogUtc = DateTime.UtcNow;
296+
250297
var errorMessage = $"An error occurred in the user interface:\n\n{e.Exception.Message}\n\nDo you want to continue?";
251298
var result = System.Windows.MessageBox.Show(errorMessage, "UI Error",
252299
MessageBoxButton.YesNo, MessageBoxImage.Error);
@@ -259,6 +306,8 @@ private void OnDispatcherUnhandledException(object sender, DispatcherUnhandledEx
259306
{
260307
e.Handled = false; // Let the application crash
261308
}
309+
310+
Interlocked.Exchange(ref _uiExceptionDialogOpen, 0);
262311
}
263312
}
264313
}

MainWindow.xaml

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,6 @@
1313
WindowStartupLocation="CenterScreen">
1414

1515
<Window.Resources>
16-
<Style TargetType="GroupBox">
17-
<Setter Property="Background" Value="{DynamicResource SurfaceBrush}"/>
18-
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
19-
<Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}"/>
20-
</Style>
21-
22-
<Style TargetType="TabControl">
23-
<Setter Property="Background" Value="{DynamicResource AppBackgroundBrush}"/>
24-
<Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}"/>
25-
<Setter Property="BorderBrush" Value="{DynamicResource BorderBrush}"/>
26-
</Style>
27-
2816
<Style TargetType="StatusBar">
2917
<Setter Property="Background" Value="{DynamicResource SurfaceAltBrush}"/>
3018
<Setter Property="Foreground" Value="{DynamicResource TextPrimaryBrush}"/>
@@ -102,13 +90,7 @@
10290
<TabItem x:Name="SystemTweaksTab" Header="🔧 Tweaks">
10391
<views:SystemTweaksView x:Name="SystemTweaksView"/>
10492
</TabItem>
105-
<TabItem x:Name="SettingsTab">
106-
<TabItem.Header>
107-
<StackPanel Orientation="Horizontal">
108-
<TextBlock Text="" FontFamily="Segoe Fluent Icons, Segoe MDL2 Assets, Segoe UI Symbol, Segoe UI" Margin="0,0,6,0"/>
109-
<TextBlock Text="Settings"/>
110-
</StackPanel>
111-
</TabItem.Header>
93+
<TabItem x:Name="SettingsTab" Header="Settings">
11294
<views:SettingsView x:Name="SettingsView"/>
11395
</TabItem>
11496
</TabControl>

0 commit comments

Comments
 (0)