Skip to content

Commit 1ab85b2

Browse files
committed
Release 1.1.1: installer, elevation & onboarding
Bump release to v1.1.1 and add release/build packaging, installer and onboarding enhancements. Key changes: - Version bump across csproj, README and release assets to 1.1.1. - CI/workflow: copy bundled Powerplans into single-file output, install Inno Setup, build Inno Setup installer, include installer artifacts in GitHub release, and stage unified package zips with uninstall/license files. - Installer: update InstallScript (setup.iss) to point to staged artifacts, include Powerplans folder, and output installer to artifacts/release/installer. - App startup/elevation: requireAdministrator manifest and App.xaml.cs now requests elevation early, terminating non-elevated instance when elevation is requested/declined. - UX: add Performance first-run onboarding overlay (modal with blurred background), updated loading overlay visuals, and related MainWindow logic to show/hide intro and persist seen state. - Settings & config: implement export/import configuration bundle (JSON) with Save/Open dialogs, add ReplaceConfigurationAsync and CloneConfiguration in association service, and adjust settings view labels. - Power plan features: add PowerPlanService.AddCustomPowerPlanFileAsync and UI/command to add .pow files; adjust Import/Apply button labels. - Performance tweaks: reduce power plan change delay from 1000ms to 250ms in config, models and viewmodels. - Theme detection: improve ThemeService to resolve registry values and fallback to system theme helper. - Misc: various logging/status improvements, packaging script fixes (build-release/build-msix), and added package-release-zips script. These changes prepare the project for a formal 1.1.1 release with installer distribution, improved elevation handling and onboarding, plus enhanced import/export and custom power plan support.
1 parent 2965c90 commit 1ab85b2

31 files changed

Lines changed: 848 additions & 106 deletions

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ body:
1010
id: version
1111
attributes:
1212
label: Version
13-
placeholder: 1.1.0
13+
placeholder: 1.1.1
1414
validations:
1515
required: true
1616
- type: dropdown

.github/workflows/release.yml

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,29 @@ jobs:
3434
- name: Publish self-contained single-file
3535
run: dotnet publish "ThreadPilot.csproj" --configuration Release -p:PublishProfile=WinX64-SingleFile
3636

37+
- name: Ensure bundled power plans in single-file output
38+
shell: pwsh
39+
run: |
40+
$ErrorActionPreference = "Stop"
41+
New-Item -ItemType Directory -Force -Path "artifacts/release/singlefile/Powerplans" | Out-Null
42+
Copy-Item "assets/Powerplans/*" -Destination "artifacts/release/singlefile/Powerplans" -Recurse -Force
43+
3744
- name: Publish ReadyToRun
3845
run: dotnet publish "ThreadPilot.csproj" --configuration Release -p:PublishProfile=WinX64-ReadyToRun
3946

47+
- name: Install Inno Setup
48+
shell: pwsh
49+
run: choco install innosetup --no-progress -y
50+
51+
- name: Build Inno Setup installer
52+
shell: pwsh
53+
run: |
54+
$ErrorActionPreference = "Stop"
55+
$version = "${{ github.ref_name }}".TrimStart('v')
56+
$sourceDir = (Resolve-Path "artifacts/release/singlefile").Path
57+
58+
& iscc.exe "/DMyAppVersion=$version" "/DMyAppSourceDir=$sourceDir" "Installer/setup.iss"
59+
4060
- name: Publish MSIX
4161
run: dotnet publish "ThreadPilot.csproj" --configuration Release -p:PublishProfile=WinX64-MSIX
4262

@@ -86,6 +106,7 @@ jobs:
86106
$targets = @()
87107
$targets += Get-ChildItem "artifacts/release/singlefile" -Recurse -File -Include *.exe -ErrorAction SilentlyContinue
88108
$targets += Get-ChildItem "artifacts/release/readytorun" -Recurse -File -Include *.exe -ErrorAction SilentlyContinue
109+
$targets += Get-ChildItem "artifacts/release/installer" -Recurse -File -Include *.exe -ErrorAction SilentlyContinue
89110
$targets += Get-ChildItem "artifacts/release/msix" -Recurse -File -Include *.msix,*.appx,*.msixbundle,*.appxbundle -ErrorAction SilentlyContinue
90111
91112
foreach ($target in $targets)
@@ -99,12 +120,75 @@ jobs:
99120
$ErrorActionPreference = "Stop"
100121
$version = "${{ github.ref_name }}"
101122
New-Item -ItemType Directory -Force -Path "artifacts/release/packages" | Out-Null
123+
New-Item -ItemType Directory -Force -Path "artifacts/release/package-staging" | Out-Null
124+
125+
$sharedUninstall = "artifacts/release/package-staging/uninstall.bat"
126+
$sharedLicense = "artifacts/release/package-staging/LICENSE.md"
127+
128+
$uninstallContent = @'
129+
@echo off
130+
setlocal EnableExtensions
131+
title ThreadPilot Uninstaller
132+
133+
set "APP_DIR=%~dp0"
134+
if "%APP_DIR:~-1%"=="\" set "APP_DIR=%APP_DIR:~0,-1%"
135+
136+
echo ======================================================
137+
echo ThreadPilot Uninstaller
138+
echo ======================================================
139+
echo.
140+
141+
echo [1/4] Closing running ThreadPilot processes...
142+
taskkill /IM "ThreadPilot.exe" /F >nul 2>&1
143+
144+
echo [2/4] Removing startup task and startup registry entry...
145+
schtasks /Delete /TN "ThreadPilot_Startup" /F >nul 2>&1
146+
reg delete "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "ThreadPilot" /f >nul 2>&1
147+
148+
echo [3/4] Optional user data cleanup...
149+
set "REMOVE_DATA=N"
150+
set /p REMOVE_DATA=Do you want to remove user settings at "%APPDATA%\ThreadPilot"? [y/N]:
151+
if /I "%REMOVE_DATA%"=="Y" (
152+
if exist "%APPDATA%\ThreadPilot" (
153+
rd /s /q "%APPDATA%\ThreadPilot"
154+
echo User settings removed.
155+
) else (
156+
echo No user settings folder found.
157+
)
158+
) else (
159+
echo User settings were kept.
160+
)
161+
162+
echo [4/4] Scheduling app folder removal...
163+
start "" /min cmd /c "timeout /t 5 /nobreak >nul & rd /s /q \"%APP_DIR%\""
164+
165+
echo.
166+
echo Uninstall completed. Remaining files will be removed in a few seconds.
167+
endlocal
168+
exit /b 0
169+
'@
170+
171+
Set-Content -LiteralPath $sharedUninstall -Value $uninstallContent -Encoding Ascii
172+
Copy-Item "LICENSE" -Destination $sharedLicense -Force
173+
174+
$singleStage = "artifacts/release/package-staging/singlefile"
175+
$readyToRunStage = "artifacts/release/package-staging/readytorun"
176+
New-Item -ItemType Directory -Force -Path $singleStage | Out-Null
177+
New-Item -ItemType Directory -Force -Path $readyToRunStage | Out-Null
178+
179+
Copy-Item "artifacts/release/singlefile/*" -Destination $singleStage -Recurse -Force
180+
Copy-Item "artifacts/release/readytorun/*" -Destination $readyToRunStage -Recurse -Force
181+
182+
Copy-Item $sharedUninstall -Destination (Join-Path $singleStage "uninstall.bat") -Force
183+
Copy-Item $sharedLicense -Destination (Join-Path $singleStage "LICENSE.md") -Force
184+
Copy-Item $sharedUninstall -Destination (Join-Path $readyToRunStage "uninstall.bat") -Force
185+
Copy-Item $sharedLicense -Destination (Join-Path $readyToRunStage "LICENSE.md") -Force
102186
103187
$singleFileZip = "artifacts/release/packages/ThreadPilot_$version`_singlefile_win-x64.zip"
104188
$readyToRunZip = "artifacts/release/packages/ThreadPilot_$version`_readytorun_win-x64.zip"
105189
106-
Compress-Archive -Path "artifacts/release/singlefile/*" -DestinationPath $singleFileZip -Force
107-
Compress-Archive -Path "artifacts/release/readytorun/*" -DestinationPath $readyToRunZip -Force
190+
Compress-Archive -Path "$singleStage/*" -DestinationPath $singleFileZip -Force
191+
Compress-Archive -Path "$readyToRunStage/*" -DestinationPath $readyToRunZip -Force
108192
109193
- name: Generate checksums
110194
shell: pwsh
@@ -114,6 +198,7 @@ jobs:
114198
115199
$releaseFiles = @()
116200
$releaseFiles += Get-ChildItem "artifacts/release/packages" -File -ErrorAction SilentlyContinue
201+
$releaseFiles += Get-ChildItem "artifacts/release/installer/*.exe" -File -ErrorAction SilentlyContinue
117202
$releaseFiles += Get-ChildItem "artifacts/release/msix" -Recurse -File -Include *.msix,*.appx,*.msixbundle,*.appxbundle -ErrorAction SilentlyContinue
118203
119204
$releaseFiles | ForEach-Object {
@@ -125,6 +210,7 @@ jobs:
125210
uses: softprops/action-gh-release@v2
126211
with:
127212
files: |
213+
artifacts/release/installer/*.exe
128214
artifacts/release/packages/*.zip
129215
artifacts/release/msix/**/*.msix
130216
artifacts/release/msix/**/*.appx

App.xaml.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,25 @@ protected override void OnStartup(StartupEventArgs e)
8282

8383
if (!elevationService.IsRunningAsAdministrator())
8484
{
85-
logger.LogWarning("Application is not running with administrator privileges");
86-
this.ShowElevationRequiredMessage();
85+
logger.LogWarning("Application is not running with administrator privileges. Requesting elevation before startup.");
8786

88-
// Allow the application to continue in read-only mode
89-
// The UI will handle showing elevation prompts as needed
87+
var elevationRequested = elevationService.RequestElevationIfNeeded().GetAwaiter().GetResult();
88+
if (!elevationRequested)
89+
{
90+
logger.LogWarning("Elevation was declined or failed. Application startup will be terminated.");
91+
System.Windows.MessageBox.Show(
92+
"ThreadPilot requires administrator privileges to start.\n\n" +
93+
"The application will now close.",
94+
"Administrator Privileges Required",
95+
MessageBoxButton.OK,
96+
MessageBoxImage.Warning);
97+
this.Shutdown(1);
98+
return;
99+
}
100+
101+
// An elevated instance has been requested; terminate this non-elevated instance.
102+
this.Shutdown();
103+
return;
90104
}
91105
else
92106
{

Configuration/ProcessPowerPlanAssociations.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"IsFallbackPollingEnabled": true,
66
"PollingIntervalSeconds": 5,
77
"PreventDuplicatePowerPlanChanges": true,
8-
"PowerPlanChangeDelayMs": 1000,
8+
"PowerPlanChangeDelayMs": 250,
99
"Associations": [
1010
{
1111
"Id": "sample-gaming-association",

Installer/setup.iss

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22
; Copyright (C) 2025 Prime Build
33

44
#define MyAppName "ThreadPilot"
5-
#define MyAppVersion "0.1.0-beta"
65
#define MyAppPublisher "Prime Build"
76
#define MyAppURL "https://github.com/PrimeBuild-pc/ThreadPilot"
87
#define MyAppExeName "ThreadPilot.exe"
9-
#define MyAppSourceDir "..\bin\Publish"
8+
9+
#ifndef MyAppVersion
10+
#define MyAppVersion "1.1.1"
11+
#endif
12+
13+
#ifndef MyAppSourceDir
14+
#define MyAppSourceDir "..\\artifacts\\release\\singlefile"
15+
#endif
1016

1117
[Setup]
1218
AppId={{E8F7A3B2-5C4D-4E6F-8A9B-1C2D3E4F5A6B}
@@ -22,7 +28,7 @@ DefaultGroupName={#MyAppName}
2228
AllowNoIcons=yes
2329
LicenseFile=..\LICENSE
2430
PrivilegesRequired=admin
25-
OutputDir=Output
31+
OutputDir=..\artifacts\release\installer
2632
OutputBaseFilename=ThreadPilot_v{#MyAppVersion}_Setup
2733
SetupIconFile=..\assets\icons\ico.ico
2834
Compression=lzma2/ultra64
@@ -37,23 +43,17 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
3743

3844
[Tasks]
3945
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
40-
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked; OnlyBelowVersion: 6.1; Check: not IsAdminInstallMode
4146

4247
[Files]
4348
Source: "{#MyAppSourceDir}\ThreadPilot.exe"; DestDir: "{app}"; Flags: ignoreversion
44-
Source: "{#MyAppSourceDir}\*.dll"; DestDir: "{app}"; Flags: ignoreversion
45-
Source: "{#MyAppSourceDir}\*.pdb"; DestDir: "{app}"; Flags: ignoreversion
49+
Source: "{#MyAppSourceDir}\*.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist
50+
Source: "{#MyAppSourceDir}\*.pdb"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist
4651
Source: "{#MyAppSourceDir}\Powerplans\*"; DestDir: "{app}\Powerplans"; Flags: ignoreversion recursesubdirs createallsubdirs
4752

4853
[Icons]
4954
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
5055
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
5156
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
52-
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon
5357

5458
[Run]
55-
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent runascurrentuser
56-
57-
[UninstallDelete]
58-
Type: filesandordirs; Name: "{localappdata}\ThreadPilot"
59-
Type: filesandordirs; Name: "{userappdata}\ThreadPilot"
59+
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: shellexec nowait postinstall skipifsilent

MainWindow.xaml

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,75 @@
199199
</Border>
200200
</Grid>
201201

202+
<!-- First-run Performance introduction modal -->
203+
<Grid x:Name="PerformanceIntroOverlay"
204+
Grid.RowSpan="3"
205+
Background="{DynamicResource OverlayBrush}"
206+
Visibility="Collapsed"
207+
Panel.ZIndex="950"
208+
AutomationProperties.Name="Performance introduction overlay">
209+
<Border x:Name="PerformanceIntroCard"
210+
Background="{DynamicResource CardBackgroundFillColorDefaultBrush}"
211+
BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}"
212+
BorderThickness="1"
213+
CornerRadius="8"
214+
Padding="26"
215+
HorizontalAlignment="Center"
216+
VerticalAlignment="Center"
217+
MinWidth="360"
218+
MaxWidth="560">
219+
<Border.Effect>
220+
<DropShadowEffect BlurRadius="18" ShadowDepth="0" Opacity="0.35"/>
221+
</Border.Effect>
222+
<StackPanel>
223+
<TextBlock x:Name="PerformanceIntroTitle"
224+
Text="Performance Dashboard"
225+
FontSize="24"
226+
FontWeight="SemiBold"
227+
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
228+
Margin="0,0,0,10"/>
229+
230+
<TextBlock x:Name="PerformanceIntroDescription"
231+
Text="This page gives you live CPU, memory and hotspot visibility so you can build precise optimization rules."
232+
TextWrapping="Wrap"
233+
Margin="0,0,0,10"
234+
Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
235+
236+
<TextBlock x:Name="PerformanceIntroTipsHeader"
237+
Text="Quick tips:"
238+
FontWeight="SemiBold"
239+
Foreground="{DynamicResource TextFillColorPrimaryBrush}"
240+
Margin="0,0,0,6"/>
241+
242+
<TextBlock x:Name="PerformanceIntroTip1"
243+
Text="1. Start monitoring to collect a live snapshot."
244+
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
245+
Margin="0,0,0,4"/>
246+
<TextBlock x:Name="PerformanceIntroTip2"
247+
Text="2. Review top processes and pick recurring workloads."
248+
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
249+
Margin="0,0,0,4"/>
250+
<TextBlock x:Name="PerformanceIntroTip3"
251+
Text="3. Create rules from hotspots to automate power plan and affinity behavior."
252+
TextWrapping="Wrap"
253+
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
254+
Margin="0,0,0,16"/>
255+
256+
<Button x:Name="PerformanceIntroContinueButton"
257+
Content="Continue to Performance"
258+
Click="PerformanceIntroContinue_Click"
259+
HorizontalAlignment="Right"
260+
Background="{DynamicResource AccentFillColorDefaultBrush}"
261+
Foreground="{DynamicResource TextOnAccentFillColorPrimaryBrush}"
262+
Padding="16,8"/>
263+
</StackPanel>
264+
</Border>
265+
</Grid>
266+
202267
<!-- Startup Loading Overlay -->
203268
<Grid x:Name="LoadingOverlay"
204269
Grid.RowSpan="3"
205-
Background="#99000000"
270+
Background="#FFF2F2F2"
206271
Visibility="Visible"
207272
Panel.ZIndex="1000"
208273
AutomationProperties.Name="Application startup loading overlay"
@@ -218,12 +283,18 @@
218283
MaxWidth="400">
219284
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
220285
<!-- Application Icon -->
221-
<Image x:Name="LoadingIcon"
222-
Source="pack://application:,,,/assets/icons/ico.ico"
223-
Width="64"
224-
Height="64"
225-
Margin="0,0,0,20"
226-
HorizontalAlignment="Center"/>
286+
<Border x:Name="LoadingIconBackground"
287+
Background="{DynamicResource ControlFillColorSecondaryBrush}"
288+
CornerRadius="10"
289+
Padding="10"
290+
Margin="0,0,0,20"
291+
HorizontalAlignment="Center">
292+
<Image x:Name="LoadingIcon"
293+
Source="pack://application:,,,/assets/icons/ico.ico"
294+
Width="64"
295+
Height="64"
296+
HorizontalAlignment="Center"/>
297+
</Border>
227298

228299
<!-- Loading Spinner -->
229300
<ui:ProgressRing IsIndeterminate="True" Margin="0,0,0,15" />
@@ -243,6 +314,7 @@
243314
TextAlignment="Center"/>
244315
</StackPanel>
245316
</Border>
317+
246318
</Grid>
247319
</Grid>
248320
</ui:FluentWindow>

0 commit comments

Comments
 (0)