Skip to content

Release 2.4.8#280

Merged
patchzyy merged 8 commits into
mainfrom
dev
Jun 1, 2026
Merged

Release 2.4.8#280
patchzyy merged 8 commits into
mainfrom
dev

Conversation

@patchzyy
Copy link
Copy Markdown
Member

@patchzyy patchzyy commented Jun 1, 2026

Release Wheel Wizard 2.4.8.\n\nValidation:\n- dotnet restore WheelWizard.sln\n- dotnet test WheelWizard.sln --configuration Release --verbosity normal\n- dotnet test WheelWizard.sln --configuration Release --verbosity minimal

Summary by CodeRabbit

  • Bug Fixes

    • Fixed eyebrow and eye color selection in Mii editor
    • Improved error handling for invalid Mii data during import
  • New Features

    • Added automatic window scale adjustment based on screen resolution and bounds
    • Enhanced window scale validation with configurable minimum and maximum limits
  • Tests

    • Added validation tests for window scale and Mii deserialization robustness
  • Chores

    • Version bumped to 2.4.8

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

WheelWizard v2.4.8 adds a window scale validation and clamping system to prevent invalid UI scaling factors, enhances Mii deserialization with graceful error handling, fixes eye/eyebrow color comparison logic in the Mii editor, and refactors ZIP extraction to use stream-based file abstraction.

Changes

Version 2.4.8 Release

Layer / File(s) Summary
Version and release metadata
WheelWizard/WheelWizard.csproj, Flatpak/io.github.TeamWheelWizard.WheelWizard.metainfo.xml
Version property updated to 2.4.8 and Flatpak release entry added for the new version.

Window Scale Validation and Clamping System

Layer / File(s) Summary
Window scale validation bounds and contract
WheelWizard/Features/Settings/Types/SettingConstants.cs
MinWindowScale and MaxWindowScale constants (0.5–2.0) are defined, along with IsValidWindowScale(object? value) validator method.
Settings manager scale validation integration
WheelWizard/Features/Settings/SettingsManager.cs
SAVED_WINDOW_SCALE and WINDOW_SCALE settings are configured with the new IsValidWindowScale validator via explicit method chaining.
Scale computation utility
WheelWizard/Views/ViewUtils.cs
New GetUsableWindowScale(double requestedScale, Size unscaledSize, Window window) helper computes a clamped scale factor from screen working area and min/max bounds.
Layout scale clamping and update handling
WheelWizard/Views/Layout.axaml.cs
Constructor calls ClampSavedWindowScaleToCurrentScreen() to enforce bounds at startup; OnSettingChanged recalculates window dimensions and transforms when scale changes.
UI scale normalization and validation tests
WheelWizard/Views/Popups/Base/PopupWindow.axaml.cs, WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs, WheelWizard.Test/Features/Settings/SettingsTests.cs
PopupWindow and settings dropdown normalize scales via GetUsableWindowScale before applying them; parameterized tests validate that Set rejects out-of-bounds values and preserves default scale.

Mii Deserialization Robustness

Layer / File(s) Summary
Deserializer error handling and robustness tests
WheelWizard/Features/WiiManagement/MiiManagement/MiiSerializer.cs, WheelWizard.Test/Features/MiiSerializerTests.cs
MiiSerializer.Deserialize now wraps base64 and byte[] decoding in try/catch blocks, delegates parsing to DeserializeCore, and routes exceptions through refactored InvalidDataExc helper; tests verify graceful failure for invalid calendar dates and malformed base64.

Bug Fixes

Layer / File(s) Summary
Mii editor color comparison fixes
WheelWizard/Views/Popups/MiiManagement/MiiEditor/EditorEyebrows.axaml.cs, WheelWizard/Views/Popups/MiiManagement/MiiEditor/EditorEyes.axaml.cs
SetEyebrowColor and SetEyeColor now compare the requested color index against the current color field instead of the current type, preventing incorrect early returns that skip color updates.
ZIP extraction stream-based handling
WheelWizard/Features/CustomDistributions/RetroRewindBeta.cs
ExtractZipFile opens the ZIP through injected IFileSystem and passes the stream to ArchiveFactory.OpenArchive instead of the file path directly.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant WhWzSettings
  participant ViewUtils
  participant SettingsService
  participant Layout
  
  User->>WhWzSettings: Select new window scale
  WhWzSettings->>ViewUtils: GetUsableWindowScale(requestedScale, size, window)
  ViewUtils-->>WhWzSettings: normalizedScale
  WhWzSettings->>WhWzSettings: Update dropdown display to normalized scale
  WhWzSettings->>SettingsService: Set(WINDOW_SCALE, normalizedScale)
  alt Set succeeds
    SettingsService-->>WhWzSettings: true
    WhWzSettings->>Layout: OnSettingChanged(WINDOW_SCALE)
    Layout->>Layout: Recalculate dimensions with new scale
    Layout-->>User: Window resized
  else Set fails (out of bounds)
    SettingsService-->>WhWzSettings: false
    WhWzSettings->>WhWzSettings: Revert dropdown to current WINDOW_SCALE
    WhWzSettings-->>User: No change applied
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • TeamWheelWizard/WheelWizard#248: Both PRs touch the Mii deserialization path; PR #248's April Fools logic in MiiStudioDataSerializer.Serialize calls MiiSerializer.Deserialize, so the refactored deserializer error handling directly impacts that feature.

Poem

🐰 Scales and Scales
Windows scale from half to double wide,
Clamped by screens, they're safely tied.
Mii colors shine with logic bright,
And ZIP streams flow—abstraction's right! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description provides validation steps but omits required template sections: Purpose, How to Test, What Has Been Changed, and the preflight checklist. The description is incomplete relative to the repository template. Expand the description to include all template sections: purpose of the release, detailed testing steps, summary of changes, related issues, and the completion checklist.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Release 2.4.8' directly corresponds to the PR's main objective of releasing version 2.4.8, which is confirmed by the version bump in the project file and metainfo.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
WheelWizard/Features/CustomDistributions/RetroRewindBeta.cs (1)

224-224: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the injected file system abstraction for consistency.

Line 224 uses File.Create directly instead of _fileSystem.File.Create, breaking the abstraction pattern used throughout the class. This makes the extraction logic harder to test with mocked file systems.

🔧 Proposed fix
-                using var outputStream = File.Create(destinationPath);
+                using var outputStream = _fileSystem.File.Create(destinationPath);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WheelWizard/Features/CustomDistributions/RetroRewindBeta.cs` at line 224,
Replace the direct System.IO call with the injected file system abstraction:
locate the line using File.Create(destinationPath) inside the RetroRewindBeta
class and change it to use the injected _fileSystem.File.Create(destinationPath)
so the extraction logic consistently uses the mocked/testable IFileSystem; keep
the existing using/disposal semantics (using var outputStream ...) and ensure
any tests/mock setups reference _fileSystem.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@WheelWizard/Features/WiiManagement/MiiManagement/MiiSerializer.cs`:
- Around line 158-171: The catch in Deserialize currently swallows all
exceptions from DeserializeCore and returns InvalidDataExc(ex); add a
debug-level log of the caught exception before returning so programming errors
are visible in diagnostics without changing the returned OperationResult. Update
the catch (Exception ex) block in Deserialize to call the project's logging
facility (e.g., Trace/ILogger/Serilog) to log a short contextual message like
"Deserialize failed" along with ex (stack and message) and then return
InvalidDataExc(ex); reference Deserialize, DeserializeCore, and InvalidDataExc
when making the change.

In `@WheelWizard/Views/Layout.axaml.cs`:
- Around line 167-176: The clamp currently runs in the Layout() constructor and
may use incorrect screen info; remove the call to
ClampSavedWindowScaleToCurrentScreen() from the Layout() constructor and instead
invoke it once the window is shown by overriding OnOpened or handling the
Opened/Loaded event (e.g., override OnOpened or subscribe to Opened and call
ClampSavedWindowScaleToCurrentScreen there). Keep the existing
ClampSavedWindowScaleToCurrentScreen() and GetUsableWindowScale() (which calls
ViewUtils.GetUsableWindowScale) and ensure it updates
SettingsService.SAVED_WINDOW_SCALE only after the window is opened so
ScreenFromWindow/Primary use correct geometry.

---

Outside diff comments:
In `@WheelWizard/Features/CustomDistributions/RetroRewindBeta.cs`:
- Line 224: Replace the direct System.IO call with the injected file system
abstraction: locate the line using File.Create(destinationPath) inside the
RetroRewindBeta class and change it to use the injected
_fileSystem.File.Create(destinationPath) so the extraction logic consistently
uses the mocked/testable IFileSystem; keep the existing using/disposal semantics
(using var outputStream ...) and ensure any tests/mock setups reference
_fileSystem.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e785248b-facb-429c-bb66-37be4627140d

📥 Commits

Reviewing files that changed from the base of the PR and between 4ebe2b7 and 7717c70.

📒 Files selected for processing (14)
  • Flatpak/io.github.TeamWheelWizard.WheelWizard.metainfo.xml
  • WheelWizard.Test/Features/MiiSerializerTests.cs
  • WheelWizard.Test/Features/Settings/SettingsTests.cs
  • WheelWizard/Features/CustomDistributions/RetroRewindBeta.cs
  • WheelWizard/Features/Settings/SettingsManager.cs
  • WheelWizard/Features/Settings/Types/SettingConstants.cs
  • WheelWizard/Features/WiiManagement/MiiManagement/MiiSerializer.cs
  • WheelWizard/Views/Layout.axaml.cs
  • WheelWizard/Views/Pages/Settings/WhWzSettings.axaml.cs
  • WheelWizard/Views/Popups/Base/PopupWindow.axaml.cs
  • WheelWizard/Views/Popups/MiiManagement/MiiEditor/EditorEyebrows.axaml.cs
  • WheelWizard/Views/Popups/MiiManagement/MiiEditor/EditorEyes.axaml.cs
  • WheelWizard/Views/ViewUtils.cs
  • WheelWizard/WheelWizard.csproj

Comment on lines 158 to 171
public static OperationResult<Mii> Deserialize(byte[]? data)
{
try
{
return DeserializeCore(data);
}
catch (Exception ex)
{
return InvalidDataExc(ex);
}
}

private static OperationResult<Mii> DeserializeCore(byte[]? data)
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Robustness refactor looks correct; consider logging the swallowed exception.

Routing DeserializeCore exceptions (including the impossible-date case from line 187) into a failure OperationResult is the right call for parsing untrusted Mii data. One caveat: catch (Exception ex) also absorbs genuine programming errors (e.g. IndexOutOfRangeException) and reports them as generic "invalid data". The message is preserved in the result, but a debug-level log here would aid diagnosis without changing behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WheelWizard/Features/WiiManagement/MiiManagement/MiiSerializer.cs` around
lines 158 - 171, The catch in Deserialize currently swallows all exceptions from
DeserializeCore and returns InvalidDataExc(ex); add a debug-level log of the
caught exception before returning so programming errors are visible in
diagnostics without changing the returned OperationResult. Update the catch
(Exception ex) block in Deserialize to call the project's logging facility
(e.g., Trace/ILogger/Serilog) to log a short contextual message like
"Deserialize failed" along with ex (stack and message) and then return
InvalidDataExc(ex); reference Deserialize, DeserializeCore, and InvalidDataExc
when making the change.

Comment on lines +167 to +176
private void ClampSavedWindowScaleToCurrentScreen()
{
var savedScale = SettingsService.Get<double>(SettingsService.SAVED_WINDOW_SCALE);
var usableScale = GetUsableWindowScale(savedScale);
if (!savedScale.Equals(usableScale))
SettingsService.Set(SettingsService.SAVED_WINDOW_SCALE, usableScale);
}

private double GetUsableWindowScale(double requestedScale) =>
ViewUtils.GetUsableWindowScale(requestedScale, new Size(WindowWidth, WindowHeight), this);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Avalonia 11.2.5 Window.Screens ScreenFromWindow availability before window shown

💡 Result:

In Avalonia 11.2.5, the Window.Screens.ScreenFromWindow(window) method is not reliable or may not return valid screen information before a window has been shown (i.e., before it has a valid platform implementation) [1][2]. The ScreenFromWindow method requires a TopLevel (or WindowBase) instance that has been initialized with a platform-specific implementation [2][3]. Before the window is shown, the PlatformImpl property is often null or uninitialized, which can lead to unexpected results or exceptions depending on the specific platform and state [2]. If you need to access screen information before a window is shown, consider these alternatives: 1. Use the static Screens API: You can access the general Screens collection via TopLevel.Screens or by using the global screen management services available in Avalonia to query primary or available displays without needing a specific window instance [4]. 2. Defer logic: If you need to position or size a window based on a specific screen, perform this logic in the Window's Opened event or an equivalent lifecycle hook where the PlatformImpl is guaranteed to be available [1][4]. 3. Platform-specific APIs: If you strictly require display information before any Avalonia UI elements are initialized, you may need to use platform-native APIs (such as Win32 APIs on Windows) or cross-platform libraries that operate independently of the Avalonia framework [5].

Citations:


🏁 Script executed:

rg -n "GetUsableWindowScale" -S .

Repository: TeamWheelWizard/WheelWizard

Length of output: 1055


🏁 Script executed:

rg -n "GetUsableWindowScale" -S .

Repository: TeamWheelWizard/WheelWizard

Length of output: 1055


🏁 Script executed:

rg -n "GetUsableWindowScale" -S .

Repository: TeamWheelWizard/WheelWizard

Length of output: 1055


🏁 Script executed:

sed -n '1,120p' WheelWizard/Views/ViewUtils.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 4997


🏁 Script executed:

sed -n '120,220p' WheelWizard/Views/Layout.axaml.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 4105


🏁 Script executed:

rg -n "ClampSavedWindowScaleToCurrentScreen\\(" WheelWizard/Views/Layout.axaml.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 182


🏁 Script executed:

rg -n "ClampSavedWindowScaleToCurrentScreen\\(" WheelWizard/Views/Layout.axaml.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 182


🏁 Script executed:

sed -n '50,110p' WheelWizard/Views/Layout.axaml.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 2197


🏁 Script executed:

sed -n '50,110p' WheelWizard/Views/Layout.axaml.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 2197


🏁 Script executed:

head -n 40 WheelWizard/Views/Layout.axaml.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 1461


🏁 Script executed:

head -n 40 WheelWizard/Views/Layout.axaml.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 1461


🏁 Script executed:

rg -n "MinWindowScale|MaxWindowScale" WheelWizard

Repository: TeamWheelWizard/WheelWizard

Length of output: 739


🏁 Script executed:

sed -n '1,80p' WheelWizard/Features/Settings/Types/SettingConstants.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 2367


🏁 Script executed:

rg -n "class BaseWindow" -S WheelWizard

Repository: TeamWheelWizard/WheelWizard

Length of output: 147


🏁 Script executed:

sed -n '1,80p' WheelWizard/Features/Settings/Types/SettingConstants.cs

Repository: TeamWheelWizard/WheelWizard

Length of output: 2367


🏁 Script executed:

rg -n "Avalonia\\.?|PackageReference.*Avalonia" -S . --glob '*.csproj' --glob '*.props' --glob '*.targets'

Repository: TeamWheelWizard/WheelWizard

Length of output: 1199


🏁 Script executed:

rg -n "Avalonia\\.?|PackageReference.*Avalonia" -S . --glob '*.csproj' --glob '*.props' --glob '*.targets'

Repository: TeamWheelWizard/WheelWizard

Length of output: 1199


Defer screen-based clamping until the window is opened

  • WheelWizard/Views/Layout.axaml.cs calls ClampSavedWindowScaleToCurrentScreen() inside public Layout() (constructor), before the window is shown.
  • ViewUtils.GetUsableWindowScale(...) uses window.Screens.ScreenFromWindow(window) ?? window.Screens.Primary; in Avalonia 11.2.5 ScreenFromWindow may not provide reliable screen geometry until after show/open, so multi-monitor setups can clamp against the wrong screen.
  • Move the clamp to Opened/OnOpened (or Loaded) so the correct WorkingArea/Scaling are available before updating SAVED_WINDOW_SCALE.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WheelWizard/Views/Layout.axaml.cs` around lines 167 - 176, The clamp
currently runs in the Layout() constructor and may use incorrect screen info;
remove the call to ClampSavedWindowScaleToCurrentScreen() from the Layout()
constructor and instead invoke it once the window is shown by overriding
OnOpened or handling the Opened/Loaded event (e.g., override OnOpened or
subscribe to Opened and call ClampSavedWindowScaleToCurrentScreen there). Keep
the existing ClampSavedWindowScaleToCurrentScreen() and GetUsableWindowScale()
(which calls ViewUtils.GetUsableWindowScale) and ensure it updates
SettingsService.SAVED_WINDOW_SCALE only after the window is opened so
ScreenFromWindow/Primary use correct geometry.

@patchzyy patchzyy enabled auto-merge June 1, 2026 00:26
@patchzyy patchzyy merged commit 1db6eb8 into main Jun 1, 2026
3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant