Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
dda7b5d
i18n(no): complete Norwegian Bokmål translation from 19% to 97%
codex May 3, 2026
1f9f41c
fix(i18n): preserve resx XML structure in translate tool and ca.resx
codex May 3, 2026
1107cec
refactor: Phase 2 MVVM refactor, structured logging, and test expansion
codex May 4, 2026
3095ff1
fix: resolve critical security, concurrency, and code quality issues
codex May 4, 2026
b391523
auto: finalize WPF UI fixes, title spacing, loading animation, CI val…
codex May 17, 2026
995a7fe
chore: sync upstream backend and smoke handoff
codex May 17, 2026
ebd1350
chore(deps): bump actions/cache from 4 to 5
dependabot[bot] May 17, 2026
524f111
chore(deps): bump dorny/test-reporter from 1 to 3
dependabot[bot] May 17, 2026
a235783
fix: stabilize plugin smoke and dashboard visibility
Chen-649-csy May 17, 2026
6cd6765
merge: integrate codex ai LenovoLegionToolkit relay branch
Chen-649-csy May 17, 2026
9907a3e
fix(ui): restore dashboard cards and tighten plugin layout
Chen-649-csy May 17, 2026
a24f31e
docs: refine compatibility and supported-device details
Chen-649-csy May 18, 2026
6d66c5b
merge: integrate origin/i18n-resx-fix into master
Chen-649-csy May 18, 2026
8872698
merge: acknowledge origin/phase2-mvvm-refactor (kept master content)
Chen-649-csy May 18, 2026
20d1854
merge: acknowledge origin/security-code-quality-fixes (kept master co…
Chen-649-csy May 18, 2026
3be3b90
merge: integrate origin/dependabot/github_actions/actions/cache-5 int…
Chen-649-csy May 18, 2026
d7b3038
merge: integrate origin/dependabot/github_actions/dorny/test-reporter…
Chen-649-csy May 18, 2026
53c084f
feat(ui): add single-line and double-line loading skeletons
Chen-649-csy May 18, 2026
38fc87d
fix(i18n): remove hardcoded close label in crash report window
Chen-649-csy May 18, 2026
8fc02fd
fix(ui): render preset action icons in gpu overclock window
Chen-649-csy May 18, 2026
15095b2
fix(ui): enforce english units and enlarge god mode window
Chen-649-csy May 18, 2026
098b945
Fix snackbar theme and sensor card layout
Chen-649-csy May 18, 2026
902a177
release: prepare 3.6.16 runtime and packaging updates
Chen-649-csy May 18, 2026
944329f
merge: prepare 3.6.16 release updates
Chen-649-csy May 18, 2026
97cccf8
release: add 3.6.16 winget manifest and smoke fix
Chen-649-csy May 18, 2026
0cac60e
ci: align workflows with net10 windows tfm
Chen-649-csy May 18, 2026
14dcdcf
Add acknowledgment to README files
Chen-649-csy May 18, 2026
800bc81
test: stage canonical plugin shared assembly in import test
Chen-649-csy May 18, 2026
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: 1 addition & 1 deletion .github/workflows/Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
with:
dotnet-version: 10.0.x
- name: Cache NuGet packages
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props') }}
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/Ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
dotnet-version: '10.0.x'

- name: Cache NuGet packages
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props') }}
Expand All @@ -37,10 +37,10 @@ jobs:

# Fast subset: tests that declare [Trait("Category", "Unit")]. Full suite below still runs everything.
- name: Test (Unit category — fail fast)
run: dotnet test LenovoLegionToolkit.Tests/LenovoLegionToolkit.Tests.csproj --framework net10.0-windows --configuration Release --no-build --filter "Category=Unit" --logger "trx;LogFileName=LenovoLegionToolkit.Tests.Unit.trx"
run: dotnet test LenovoLegionToolkit.Tests/LenovoLegionToolkit.Tests.csproj --framework net10.0-windows10.0.26100.0 --configuration Release --no-build --filter "Category=Unit" --logger "trx;LogFileName=LenovoLegionToolkit.Tests.Unit.trx"

- name: Test stable paths (full suite + coverage)
run: dotnet test LenovoLegionToolkit.Tests/LenovoLegionToolkit.Tests.csproj --framework net10.0-windows --configuration Release --no-build --logger "trx;LogFileName=LenovoLegionToolkit.Tests.trx" --collect:"XPlat Code Coverage" --settings LenovoLegionToolkit.Tests/coverlet.runsettings
run: dotnet test LenovoLegionToolkit.Tests/LenovoLegionToolkit.Tests.csproj --framework net10.0-windows10.0.26100.0 --configuration Release --no-build --logger "trx;LogFileName=LenovoLegionToolkit.Tests.trx" --collect:"XPlat Code Coverage" --settings LenovoLegionToolkit.Tests/coverlet.runsettings

- name: Check for vulnerable packages
shell: pwsh
Expand All @@ -51,7 +51,7 @@ jobs:
}

- name: Publish test results
uses: dorny/test-reporter@v1
uses: dorny/test-reporter@v3
continue-on-error: true
with:
name: Test results
Expand All @@ -77,8 +77,8 @@ jobs:
$expectedVersion = "$majorVersion.$minorVersion.$patchVersion"
}

$cliExePath = Join-Path $PWD 'LenovoLegionToolkit.CLI\bin\Release\net10.0-windows\win-x64\llt.exe'
$cliDllPath = Join-Path $PWD 'LenovoLegionToolkit.CLI\bin\Release\net10.0-windows\win-x64\llt.dll'
$cliExePath = Join-Path $PWD 'LenovoLegionToolkit.CLI\bin\Release\net10.0-windows10.0.26100.0\win-x64\llt.exe'
$cliDllPath = Join-Path $PWD 'LenovoLegionToolkit.CLI\bin\Release\net10.0-windows10.0.26100.0\win-x64\llt.dll'
if (-not (Test-Path $cliExePath)) {
throw "CLI executable not found at $cliExePath"
}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/CodeQL.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
dotnet-version: 10.0.x

- name: Cache NuGet packages
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props') }}
Expand Down
85 changes: 34 additions & 51 deletions .github/workflows/MainAppPluginUi.Smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ on:
plugin_ids:
description: Comma-separated plugin ids
required: false
default: shell-integration,custom-mouse
default: custom-mouse,shell-integration,vive-tool,network-acceleration
plugin_sources:
description: Comma-separated plugin install sources (<pluginId>=online|local)
required: false
default: shell-integration=online,custom-mouse=local
default: *=online
screenshot_mode:
description: Screenshot capture policy
type: choice
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
dotnet-version: '10.0.x'

- name: Cache NuGet packages
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props') }}
Expand All @@ -104,14 +104,6 @@ jobs:
PLUGIN_SOURCES: ${{ inputs.plugin_sources }}
SCENARIO: ${{ inputs.scenario }}
run: |
function Resolve-ScenarioPluginIds([string]$scenario) {
switch (($scenario ?? '').Trim().ToLowerInvariant()) {
'shell-local' { return @('shell-integration') }
'combo-local' { return @('custom-mouse', 'shell-integration') }
default { return @() }
}
}

function ConvertTo-PluginDirectoryName([string]$pluginId) {
$segments = $pluginId.Split('-', [System.StringSplitOptions]::RemoveEmptyEntries)
if ($segments.Count -eq 0) {
Expand All @@ -129,52 +121,43 @@ jobs:
}

$requestedLocalPlugins = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
$scenarioPluginIds = Resolve-ScenarioPluginIds $env:SCENARIO

if ($scenarioPluginIds.Count -gt 0) {
foreach ($pluginId in $scenarioPluginIds) {
[void]$requestedLocalPlugins.Add($pluginId)
$pluginIds = @($env:PLUGIN_IDS -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
$explicitSources = @{}
$wildcardSource = $null

foreach ($entry in @($env:PLUGIN_SOURCES -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })) {
$parts = $entry.Split('=', 2)
if ($parts.Count -ne 2) {
throw "Invalid plugin source entry '$entry'. Expected '<pluginId>=online|local'."
}
}
else {
$pluginIds = @($env:PLUGIN_IDS -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
$explicitSources = @{}
$wildcardSource = $null

foreach ($entry in @($env:PLUGIN_SOURCES -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })) {
$parts = $entry.Split('=', 2)
if ($parts.Count -ne 2) {
throw "Invalid plugin source entry '$entry'. Expected '<pluginId>=online|local'."
}

$key = $parts[0].Trim()
$value = $parts[1].Trim().ToLowerInvariant()
if ($value -ne 'online' -and $value -ne 'local') {
throw "Invalid plugin source value '$value' for '$key'."
}
$key = $parts[0].Trim()
$value = $parts[1].Trim().ToLowerInvariant()
if ($value -ne 'online' -and $value -ne 'local') {
throw "Invalid plugin source value '$value' for '$key'."
}

if ($key -eq '*') {
$wildcardSource = $value
}
else {
$explicitSources[$key] = $value
}
if ($key -eq '*') {
$wildcardSource = $value
}
else {
$explicitSources[$key] = $value
}
}

foreach ($pluginId in $pluginIds) {
$resolvedSource = if ($explicitSources.ContainsKey($pluginId)) {
$explicitSources[$pluginId]
}
elseif ($null -ne $wildcardSource) {
$wildcardSource
}
else {
'online'
}
foreach ($pluginId in $pluginIds) {
$resolvedSource = if ($explicitSources.ContainsKey($pluginId)) {
$explicitSources[$pluginId]
}
elseif ($null -ne $wildcardSource) {
$wildcardSource
}
else {
'online'
}

if ($resolvedSource -eq 'local') {
[void]$requestedLocalPlugins.Add($pluginId)
}
if ($resolvedSource -eq 'local') {
[void]$requestedLocalPlugins.Add($pluginId)
}
}

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/Release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
with:
dotnet-version: 10.0.x
- name: Cache NuGet packages
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props') }}
Expand Down Expand Up @@ -71,7 +71,7 @@ jobs:
run: dotnet build LenovoLegionToolkit.sln --configuration Release --no-restore

- name: Test solution
run: dotnet test LenovoLegionToolkit.Tests/LenovoLegionToolkit.Tests.csproj --framework net10.0-windows --configuration Release --no-build --logger "trx;LogFileName=LenovoLegionToolkit.Release.Tests.trx" --collect:"XPlat Code Coverage"
run: dotnet test LenovoLegionToolkit.Tests/LenovoLegionToolkit.Tests.csproj --framework net10.0-windows10.0.26100.0 --configuration Release --no-build --logger "trx;LogFileName=LenovoLegionToolkit.Release.Tests.trx" --collect:"XPlat Code Coverage"

- name: Publish release payload
shell: pwsh
Expand Down
20 changes: 17 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Fixed / 修复
- 修复传感器 CPU 功耗 WMI 查询和单位归一化,并在 Lenovo 温度传感器不可用时回退读取 ACPI thermal zone,提升 CPU 温度与功耗显示可用性 / Fixed sensor CPU wattage WMI query and unit normalization, and added ACPI thermal-zone fallback when Lenovo temperature sensors are unavailable to improve CPU temperature and power readouts
- 修复插件 ZIP 提取中的路径遍历漏洞(Zip Slip),防止恶意插件包在提取过程中写入目标目录之外的文件 / Fixed Zip Slip path traversal vulnerability during plugin ZIP extraction to prevent malicious packages from writing files outside the intended extraction directory
- 挪威语 `Resource.no.resx` 中 `CopiedToClipboard_Message_WithParam` 补回 `{0}` 占位符,避免格式化参数丢失 / Restored the `{0}` placeholder in Norwegian `CopiedToClipboard_Message_WithParam` so clipboard notifications preserve the formatted argument
- 中文界面中的传感器频率单位改为标准 `GHz` 缩写 / Changed the sensor frequency unit in Chinese UI to the standard `GHz` abbreviation
- 修复 WPF-UI 4 迁移后的主窗口顶部 `Log`/设备信息按钮位置、交互和 hover 对比度,启动语言选择窗口透明背景、About 页小窗口滚动、插件扩展统计卡和深色主题文字对比回归 / Fixed WPF-UI 4 migration regressions in title-bar `Log`/device button placement, interaction, and hover contrast, startup language-selector transparency, About-page small-window scrolling, Plugin Extensions summary cards, and dark-theme text contrast

### Improved / 改进
- 补充 README 中社区续维护、下载渠道和 winget 安装说明,并新增中文宣发内容包与 winget 上架材料,便于发布传播和高流量下载承接 / Added README community-maintenance, download-channel, and winget installation guidance plus a Chinese promotion content pack and winget submission materials for launch communication and high-traffic download readiness
- 加固 CI 治理:添加 CodeQL 配置文件替换行内抑制注释以使 SARIF 处理器的规则排除生效,修复 WSL/UNC 路径测试用例和测试报告发布容错,并关闭已由 PR #14 覆盖的两个 Dependabot PR / Hardened CI governance by switching CodeQL suppression to config-file-based exclusion (SARIF-compatible), fixed WSL/UNC path test cases and test reporter resilience, and closed two Dependabot PRs superseded by the WPF-UI 4 migration
- 升级控制台页首次加载为贴近最终布局的骨架扫光动画,并为 CLI 等待主程序响应时增加不污染脚本输出的控制台加载动画 / Upgraded the Dashboard first-load state to a layout-matching shimmer skeleton and added a CLI loading animation while waiting for the main app response without polluting scripted output
- 应用设置、自动化管道与 CLI IPC 的 JSON 序列化迁移至 `System.Text.Json`,并保持与既有配置文件格式的兼容策略(含自动化判别符);Spectrum 配置文件导入导出同样使用该栈 / Migrated JSON serialization for application settings, automation pipelines, and CLI IPC to `System.Text.Json` while preserving compatibility strategies for existing files (including automation discriminators); Spectrum profile import/export uses the same stack
Expand All @@ -29,6 +31,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 插件依赖解析:调用方传入各插件版本字典时才做依赖版本区间校验(不再使用虚构默认版本);依赖关系图中未知版本显示为 `?`;移除未使用的 WPF 动画性能监控占位 API / Plugin dependency resolution now validates declared version ranges only when callers supply per-plugin version metadata (no fabricated defaults), shows `?` for unknown versions in the dependency graph, and removes the unused WPF animation performance-monitoring placeholder API
- 完善 `AGENTS.md` 与 `CLAUDE.md` 中关于 `CHANGELOG` 的 `[Unreleased]` 维护说明(合并/发版前整理用户可见变更;未发布迭代中的自修不必逐条堆砌)/ Clarified `[Unreleased]` changelog guidance in `AGENTS.md` and `CLAUDE.md` (consolidate user-visible changes before merge/release; avoid stacking every pre-release self-correction as its own line)

## [3.6.16] - 2026-05-18

### Fixed / 修复
- Restored the single-card CPU / Battery / GPU dashboard layout, kept detailed sensor rows collapsed by default, and added double-click expansion with a tooltip.
- Hardened sensor refresh and fallback behavior so CPU/GPU readings, detailed refreshes, and transient Lenovo WMI failures no longer hide the whole dashboard card as easily.
- Kept the power mode selector visible when runtime reads fail by falling back to the last known or balanced mode instead of collapsing the control.
- Fixed startup-time unobserved task failures around Lenovo WMI invalid-object errors and tightened plugin install/load compatibility for online smoke coverage.

### Improved / 改进
- Tightened the Plugin Extensions page layout, removed the redundant empty selection state, auto-selected the first plugin, and added richer descriptions plus usage guidance.
- Extended online plugin smoke coverage to exercise install, configure/open, and uninstall flows more reliably across the default plugin set.
- Updated release and distribution docs for the 3.6.16 train, including Chinese promotion copy plus explicit winget and Scoop maintainer workflows.

## [3.6.15] - 2026-04-29

### Improved / 改进
Expand Down Expand Up @@ -1488,9 +1503,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Support / 支持

- **GitHub Issues**: [Report bugs and request features](https://github.com/BartoszCichecki/LenovoLegionToolkit/issues)
- **Discord**: [Community support and discussions](https://discord.gg/)
- **QQ Channel**: [中文用户支持群](https://jq.qq.com/)
- **GitHub Issues**: [Report bugs and request features](https://github.com/SSC-STUDIO/LenovoLegionToolkit/issues)
- **Discord**: [Community support and discussions](https://discord.com/invite/legionseries)

---

Expand Down
5 changes: 4 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<MajorVersion>3</MajorVersion>
<MinorVersion>6</MinorVersion>
<PatchVersion>15</PatchVersion>
<PatchVersion>16</PatchVersion>
<ReleaseVersion>$(MajorVersion).$(MinorVersion).$(PatchVersion)</ReleaseVersion>
<WindowsVersion>$(ReleaseVersion).0</WindowsVersion>
<GitCommitHash>unknown</GitCommitHash>
Expand All @@ -14,6 +14,9 @@
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<InformationalVersion>$(Version)+$(GitCommitHash)</InformationalVersion>
<SkipGitInfoLookup>$([System.String]::Copy('$(MSBuildThisFileDirectory)').StartsWith('\\wsl.localhost\', System.StringComparison.OrdinalIgnoreCase))</SkipGitInfoLookup>
<LLTTargetFramework>net10.0-windows10.0.26100.0</LLTTargetFramework>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<RestoreConfigFile Condition="'$(RestoreConfigFile)' == '' and Exists('$(MSBuildThisFileDirectory)NuGet.Config')">$(MSBuildThisFileDirectory)NuGet.Config</RestoreConfigFile>
</PropertyGroup>

<Target Name="GetGitInfo" BeforeTargets="BeforeBuild">
Expand Down
6 changes: 6 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,18 @@
<PackageVersion Include="CoordinateSharp" Version="3.4.1.1" />
<PackageVersion Include="ManagedNativeWifi" Version="3.0.2" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.275" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7705" />
<PackageVersion Include="NAudio.Wasapi" Version="2.3.0" />
<PackageVersion Include="PubSub" Version="4.0.2" />
<PackageVersion Include="LibreHardwareMonitorLib" Version="0.9.6" />
<PackageVersion Include="NvAPIWrapper.Net" Version="0.8.1.101" />
<PackageVersion Include="Octokit" Version="14.0.0" />
<PackageVersion Include="PresentMonFps-Modified" Version="2.0.7" />
<PackageVersion Include="RAMSPDToolkit-NDD" Version="1.4.2" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="UniversalFanControl.Lib" Version="1.0.4" />
<PackageVersion Include="WindowsDisplayAPI" Version="1.3.0.13" />
<PackageVersion Include="ZenStates-Core" Version="1.0.1" />
</ItemGroup>

<!-- ==================== System Libraries (.NET 10) ==================== -->
Expand Down
6 changes: 5 additions & 1 deletion Docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,11 @@ Automatic Actions Execution
## Platform Compatibility

- **Windows**: 10 (1809+), 11 (x64 only)
- **Hardware**: Legion Gen 6-9, Ideapad Gaming, LOQ series
- **Hardware (code-driven detection)**:
- Vendor: `LENOVO` or `MOTOROLA`
- Primary families: Legion 5/Slim 5/Pro 5, Legion 7/Pro 7/9, Legion Go, LOQ, IdeaPad Gaming
- Chinese model naming variants are recognized (for example `R7000`, `R9000`, `Y7000`, `Y9000`)
- Detection source: `LenovoLegionToolkit.Lib/Utils/Compatibility.cs` (`AllowedModelsPrefix`, `MachineTypeMap`, `ModelKeywordMap`)
- **Dependencies**: .NET 10.0 Desktop Runtime, Lenovo drivers

## Performance Characteristics
Expand Down
Loading