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
24 changes: 24 additions & 0 deletions .github/release-notes/v1.1.5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## WinBat Lens v1.1.5

This release improves background startup behavior and removes misleading or
unused monitoring work from the always-on tray monitor.

### Highlights

- Windows startup now launches with `--background`, keeping the dashboard
hidden in the tray instead of briefly opening a window.
- Manual launches still open the dashboard normally.
- The installer writes the same background startup argument when autostart is
selected.
- Background warmup moves expensive performance-counter and WMI initialization
away from the first UI monitoring tick.
- Tray mode avoids unnecessary waveform redraws and repeated icon regeneration.
- Timed-out `powercfg` processes are terminated and temporary reports are
cleaned up.
- Unsupported RAM and battery values remain unavailable instead of using
fabricated fallback measurements.
- Duplicate launches can find installed and portable builds consistently.
- Parser coverage now includes missing capacity and no-battery cases.

The published executables are unsigned. Windows SmartScreen may show a warning
on first launch.
113 changes: 108 additions & 5 deletions AI_HANDOFF.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,31 @@
# Project State & Handoff

## v1.1.5 release preparation (2026-08-05)

The post-v1.1.4 background-startup and comprehensive audit changes are being
prepared for publication as v1.1.5. The project version is now 1.1.5 and the
release notes are in `.github/release-notes/v1.1.5.md`.

The intended release scope is the complete existing working-tree change set:
background startup, tray warmup/idle behavior, removal of unsupported or dead
telemetry work, timeout cleanup, cross-build duplicate-launch discovery,
parser coverage, and related documentation/comments. No unrelated changes were
found in the current diff. GitHub CLI authentication is valid in the host
network context. Local validation passed: Debug and Release builds with
`--no-restore -warnaserror` completed with 0 warnings/errors, the parser suite
passed 5/5, and `git diff --check` passed. `build-release.ps1` generated all
three assets; they are unsigned by design in this environment:

- `WinBatLens_v1.1.5_Portable_x64.exe`: SHA-256
`4EC0100C5A33479896D96ADE147A946FEC0FA3F299A58B89BB1B9AA3C162B3C9`
- `WinBatLens_v1.1.5_Portable_x64.zip`: SHA-256
`458CA504049B48A10A982F95FDB136E37425BBE039B2D7D146263E5D04C9D525`
- `WinBatLens_v1.1.5_Setup_x64.exe`: SHA-256
`B4F5C013BA3F6CEAF1D1E917C068304A2CD993E3CB8FE90A2A830DAFA4DDA4B9`

The ZIP contains `WinBatLens.exe`, `README.md`, and `LICENSE`. Publication to
GitHub is the remaining step.

## v1.1.4 released (latest)

`main` had diverged: the version-display / duplicate-launch work sat locally
Expand Down Expand Up @@ -38,11 +64,88 @@ portable build is running — the process takes its name from the file, which is
`WinBatLens_v1.1.4_Portable_x64.exe`. Two probes concluded "the app killed
itself" on that basis when the instance was alive the whole time and later
launches were correctly stepping aside. Match on `Win32_Process` `Name like
'%WinBat%'` instead. Same reason `SingleInstanceService.FindRunningInstance`
(`GetProcessesByName(self.ProcessName)`) cannot see an installed
`WinBatLens.exe` from a portable build: the mutex still blocks the second
instance, but the version prompt and the foreground handoff are skipped. Not
fixed here.
'%WinBat%'` instead. `SingleInstanceService.FindRunningInstance` now matches
the `WinBatLens` process-name prefix instead of only the current executable
name, so installed and portable launches can find each other while sharing the
mutex.

## Background Windows startup (2026-08-05)

The current task is to keep a Windows-started monitor in the background instead
of briefly opening the dashboard. `StartupService.SetAutoStart(true)` now writes
the per-user Run value as `"<exe>" --background`, and the Inno Setup autostart
entry uses the same argument. `MainWindow` detects that argument, initializes
the tray icon, hides the window during `Loaded`, and suppresses the first tray
balloon for this launch. Manual launches without the argument still open the
dashboard normally.

An existing pre-change Run value cannot identify whether the current launch
came from Windows startup or a manual shortcut, so users with that old value
must toggle auto-start off and on once (or reinstall with the autostart task)
to write the new argument.

Code changed: `Services/StartupService.cs`, `MainWindow.xaml.cs`, and
`installer/WinBatLens.iss`.

Verification: Debug and Release builds passed with `-warnaserror` and no
warnings/errors; the existing four parser tests passed. `git diff --check`
also passed. Inno Setup (`ISCC`) is not installed in this environment, and a
live `--background` startup smoke test was intentionally not run because an
already-running installed WinBat Lens instance owns the single-instance mutex;
terminating it would risk interrupting the user's monitor.

## Comprehensive code audit started (2026-08-05)

The working tree already contained the background-startup changes above; they
remain in place and are not being discarded. Baseline verification before the
audit: `dotnet build WinBatLens.csproj -c Debug -warnaserror` passed with 0
warnings/errors, and the parser test project passed 4/4.

Confirmed audit targets for the current pass:
- `RealTimePowerService` still calculated RAM, disk, network and legacy GPU
values that no control or history record consumes; these are dead per-tick
work and will be removed rather than left as misleading data fields.
- Native RAM-read failure still returned a hard-coded 8/16 GB pair; this must
become an unavailable result, never a fabricated measurement.
- Battery metadata retried every tick on no-battery systems because an invalid
cache bypassed the refresh interval; invalid battery-temperature replies also
never reached the failure threshold.
- `SingleInstanceService` searched only the exact current process name, so an
installed `WinBatLens.exe` and a portable `WinBatLens_v...exe` could share the
mutex while version/foreground handoff discovery failed.
- Timed-out `powercfg` runs could leave temporary report files, and dynamic tray
HICON ownership/cleanup needs a safer explicit lifetime.

Audit implementation and verification completed: the dead RAM/disk/network/
legacy-GPU/load calculations and their unused model fields were removed; RAM
failure no longer fabricates 8/16 GB; battery metadata and invalid-temperature
polling are throttled/failure-counted correctly; portable/installed process
names are matched safely; timed-out `powercfg` reports are cleaned up; tray
HICON handles and sensor shutdown have explicit cleanup; UI refreshes no longer
append duplicate history samples; charger deficit is shown as a warning; and
health is left unmeasured when the report lacks full-charge capacity. Debug and
Release builds both pass with `-warnaserror` and 0 warnings/errors, the parser
test suite passes 5/5, and `git diff --check` reports no errors. Inno Setup is
still unavailable in this environment, and no live hardware/UI smoke test was
run during this pass.

## Branch consolidation (2026-08-05)

After refreshing `origin`, every branch had no commits outside `main`. The
already-merged local `feat/show-version-and-handle-duplicate-launch` branch
and remote `claude/memory-cpu-optimization-p0z5ve` branch were deleted. Only `main` remains locally and on `origin`; the working-tree audit changes remain
uncommitted.

## Complete Traditional Chinese XML Documentation Pass (2026-08-05)

Added comprehensive Traditional Chinese XML doc comments (`/// <summary>`, `<param>`, `<returns>`) and inline explanatory notes across all project files:
- **Models**: `BatteryReportData.cs`, `GpuInfo.cs`, `PowerHistoryRecord.cs`
- **Services**: `AppInfo.cs`, `BatteryReportParser.cs`, `DxgiAdapterService.cs`, `DynamicTrayIconService.cs`, `GpuInfoService.cs`, `HardwareSensorService.cs`, `LocalizationService.cs`, `PowerCfgService.cs`, `PowerSupplyService.cs`, `RealTimePowerHistoryService.cs`, `RealTimePowerService.cs`, `SingleInstanceService.cs`, `StartupService.cs`
- **UI & Converters**: `App.xaml.cs`, `HealthGradeBrushConverter.cs`, `MainWindow.xaml.cs`
- **Scripts & Tests**: `build-release.ps1`, `tests/WinBatLens.Tests/BatteryReportParserTests.cs`

Verification: `dotnet build -c Debug -warnaserror` succeeds with 0 errors and 0 warnings. `dotnet test` passes all 5/5 unit tests cleanly.


## v1.1.3 release pipeline

Expand Down
22 changes: 17 additions & 5 deletions App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@

namespace WinBatLens
{
/// <summary>
/// WinBat Lens WPF 應用程式入口點與全域例外狀況處理器。
/// 包含單一執行個體鎖定控制與崩潰日誌記錄。
/// </summary>
public partial class App : System.Windows.Application
{
/// <summary>
/// 應用程式啟動入口邏輯。
/// </summary>
/// <param name="e">啟動參數事件。</param>
protected override void OnStartup(StartupEventArgs e)
{
// Another instance owning the session is not an error: it has
// already been brought to the front (or replaced, if this build is
// a different version), so this process just steps aside.
// 若已知有其他同版本執行個體運作中,即交由其喚醒,本行程自動結束。
if (!SingleInstanceService.TryClaimOwnership())
{
Shutdown();
return;
}

}
base.OnStartup(e);

AppDomain.CurrentDomain.UnhandledException += (s, args) =>
Expand All @@ -33,12 +38,19 @@ protected override void OnStartup(StartupEventArgs e)
};
}

/// <summary>
/// 應用程式結束清理邏輯。
/// </summary>
/// <param name="e">結束參數事件。</param>
protected override void OnExit(ExitEventArgs e)
{
SingleInstanceService.Release();
base.OnExit(e);
}

/// <summary>
/// 記錄未擷取的非預期例外狀況並彈出錯誤對話盒與日誌檔。
/// </summary>
private void LogException(Exception? ex, string source)
{
if (ex == null) return;
Expand Down
20 changes: 11 additions & 9 deletions HealthGradeBrushConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@
using System.Windows.Data;
using System.Windows.Media;

// Implicit usings pull in both System.Drawing and System.Windows.Media, and
// each has a Color; this file means the WPF one.
// 顯式指定 WPF 的 System.Windows.Media.Color
using MediaColor = System.Windows.Media.Color;

namespace WinBatLens
{
/// <summary>
/// Turns a health percentage into the colour for its grade, so a degraded
/// pack never gets painted the same green as a healthy one. The 80% / 60%
/// cuts are the ones <c>BatteryReportParser</c> uses for the status label —
/// they must stay in step, or the colour and the wording disagree.
/// Pass "Badge" as the converter parameter for the translucent pill fill.
/// 提供將電池健康度百分比 (0-100%) 轉換為對應評級顏色 (綠/黃/紅 SolidColorBrush) 之 WPF 值轉換器 (IValueConverter)。
/// 80% 與 60% 門檻與 <c>BatteryReportParser</c> 判定邏輯保持同步。
/// 若 ConverterParameter 設為 "Badge",則傳回半透明背景填滿 Brush。
/// </summary>
public sealed class HealthGradeBrushConverter : IValueConverter
{
Expand All @@ -31,8 +28,10 @@ private static SolidColorBrush Frozen(byte r, byte g, byte b, byte a = 0xFF)
var brush = new SolidColorBrush(MediaColor.FromArgb(a, r, g, b));
brush.Freeze();
return brush;
}

}
/// <summary>
/// 將健康度百分比數值轉換為對應之 SolidColorBrush。
/// </summary>
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double percent = value switch
Expand All @@ -50,6 +49,9 @@ public object Convert(object value, Type targetType, object parameter, CultureIn
return badge ? GoodBadge : Good;
}

/// <summary>
/// 不支援反向轉換。
/// </summary>
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
Expand Down
Loading
Loading