Skip to content

Fix About page icon + version, tray icon size, and taskbar icon cache#8

Merged
twibster merged 7 commits into
mainfrom
fix/about-page-tray-icon
Apr 15, 2026
Merged

Fix About page icon + version, tray icon size, and taskbar icon cache#8
twibster merged 7 commits into
mainfrom
fix/about-page-tray-icon

Conversation

@twibster
Copy link
Copy Markdown
Owner

@twibster twibster commented Apr 15, 2026

Summary

Addresses three issues spotted after installing v1.2.3:

  1. About page shows wrong icon and "Version 1.0.0" — the wpfui:SymbolIcon Speaker224 and hardcoded version string are replaced with an Image bound to a new 256x256 Assets/app-icon.png and a TextBlock bound to AppVersionDisplay, which reads AssemblyInformationalVersion at runtime. CI's -p:Version=X.Y.Z already flows into that attribute, so no further hardcoding is needed.

  2. Tray icon smaller than peersNotifyIcon.Icon was being assigned from Icon.ExtractAssociatedIcon(exePath), which pulls a single arbitrary frame (typically 32x32) and then gets scaled down to tray size. Now it loads app.ico as a WPF resource and passes SystemInformation.SmallIconSize to the Icon ctor — a DPI-aware size (16/20/24) that matches our embedded frames exactly, so Windows picks an unscaled glyph.

  3. Taskbar still shows old icon after update — this is Explorer's shell-icon cache holding a stale copy of the pinned shortcut. The installer now runs ie4uinit.exe -show post-install to refresh it. Also set Window.Icon explicitly to the new PNG so taskbar/alt-tab renders get a high-res source instead of WPF's default ICO-frame-picker result.

Bonus: "Check for updates" in About

New UpdateService queries api.github.com/repos/.../releases/latest, compares semver tags, and returns one of four results. The About page renders a status line + "Open release page" hyperlink when an update is available. It's pointer-only (no auto-download), so we don't need admin elevation, update hosting, or code-signing rotation — the user clicks through and runs the installer themselves.

IconBuilder extended

Now dispatches on output extension: .ico keeps the current multi-size pack, .png emits a single rasterisation (used to generate app-icon.png). Regenerate either with:

dotnet run --project design/tools/IconBuilder -- \
    design/app-icon.svg AudioMonitorRouter/Assets/app-icon.png

Test plan

  • dotnet build -c Release passes
  • App launches without XAML parse errors (smoke-tested locally)
  • After install: About page shows the new icon + real version
  • After install: tray icon matches neighbour icons in size
  • After install: taskbar icon updates without needing a reboot (ie4uinit)
  • "Check for updates" button returns "up to date" when on latest
  • "Check for updates" offers the release URL when a newer tag exists
  • release:minor label triggers auto-release to v1.3.0 on merge

Summary by CodeRabbit

  • New Features

    • “Check for updates” flow with status text and an “Open release page” link when updates are found.
    • About page now shows live app version, updated copyright, app icon, and a disable-while-checking update button.
    • UI converters added for string->visibility and inverse-boolean bindings.
  • Chores

    • App icon packaged as a runtime resource and used in UI and system tray for more reliable loading.
    • Installer now refreshes Windows icon metadata after install.

The About page previously rendered a generic Fluent speaker glyph and a
hardcoded "Version 1.0.0" string — both drifted from reality once the
app started shipping under the new icon and auto-release started cutting
real version tags. Replace them with:

- An Image bound to a new 256x256 Assets/app-icon.png rasterised from
  design/app-icon.svg (a PNG, not the multi-image app.ico, because
  BitmapImage picks the first frame of an ICO — which for our
  16/20/24/... layout is the tiny 16x16 glyph, producing a blurry result).
- A TextBlock bound to AppVersionDisplay, which reads the assembly's
  AssemblyInformationalVersion at runtime so CI's -p:Version flows
  through without any further hardcoding.

Add a "Check for updates" button that hits the GitHub Releases API via a
new UpdateService, compares tags, and shows a one-line status plus an
"Open release page" hyperlink when a newer version is available. The
service is pointer-only — no auto-download — so no elevation, hosting,
or code-signing rotation is required.

Fix the tray icon, which was loading via Icon.ExtractAssociatedIcon
(returns one arbitrary frame, typically 32x32, then scaled down to tray
size). Pack app.ico as a WPF resource and load it with the Icon ctor
passing SystemInformation.SmallIconSize — a DPI-aware size that matches
our embedded 16/20/24 frames exactly, so Windows gets an unscaled glyph.

Set Window.Icon explicitly to Assets/app-icon.png so the taskbar gets a
crisp high-res source rather than whatever WPF's default ICO-frame
picker returns.

Add a post-install ie4uinit.exe -show step to installer.iss so users who
pinned the app to their taskbar before an update see the new icon
without waiting for Explorer's shell-icon cache to roll over.

Extend IconBuilder to dispatch on output extension: .ico keeps the
current multi-size pack, .png emits a single rasterisation (used to
generate app-icon.png here). Regenerate with:

  dotnet run --project design/tools/IconBuilder -- \
      design/app-icon.svg AudioMonitorRouter/Assets/app-icon.png
@twibster twibster added the release:minor 0.X.0 release - new feature label Apr 15, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 15, 2026

Caution

Review failed

Pull request was closed or merged during review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds WPF value converters and app icon resources, implements a GitHub-based UpdateService plus UpdateCheckResult variants, integrates update UI and ViewModel commands, switches tray icon loading to packed resources, extends an icon-builder tool for .ico/.png output, and adds an installer step to refresh Explorer icon cache.

Changes

Cohort / File(s) Summary
Value Converters
AudioMonitorRouter/App.xaml, AudioMonitorRouter/App.xaml.cs
Register StringToVisibilityConverter and InverseBooleanConverter in App.xaml; add their IValueConverter implementations.
Update Service
AudioMonitorRouter/Services/UpdateService.cs
Add UpdateService and UpdateCheckResult variants; perform unauthenticated GET to GitHub /releases/latest, parse tag_name/html_url, handle timeouts/rate-limits/errors, expose CurrentVersion and Copyright.
ViewModel: Updates Integration
AudioMonitorRouter/ViewModels/MainWindowViewModel.cs
Instantiate update service; add AppVersionDisplay, CopyrightDisplay, UpdateStatus, LatestReleaseUrl, IsCheckingForUpdate, and CheckForUpdatesAsync command with re-entry guard.
UI & Resources
AudioMonitorRouter/Views/MainWindow.xaml, AudioMonitorRouter/Views/MainWindow.xaml.cs, AudioMonitorRouter/AudioMonitorRouter.csproj
Set window/About icon to /Assets/app-icon.png; bind version and copyright; add update controls and conditional release link; load tray icon from packed app.ico; add app.ico and Assets\app-icon.png as WPF resources; update project copyright.
Tray Icon Loader
AudioMonitorRouter/Views/MainWindow.xaml.cs
Replace exe-based icon extraction with LoadTrayIconFromResources() using pack://application:,,,/app.ico; pick appropriate ICO frame; fallback to SystemIcons.Application.
Tools: IconBuilder
design/tools/IconBuilder/Program.cs
Dispatch output based on extension (.ico or .png); add WriteIco and WritePng helpers; refactor size constants and error handling.
Installer
installer/installer.iss
Add post-.NET-install Run entry to invoke {sys}\ie4uinit.exe -show (hidden) to refresh Explorer icon metadata before launch.

Sequence Diagram(s)

sequenceDiagram
    participant UI as MainWindow (UI)
    participant VM as MainWindowViewModel
    participant Svc as UpdateService
    participant GH as GitHub Releases API

    UI->>VM: Click "Check for updates"
    VM->>VM: set IsCheckingForUpdate = true\nUpdateStatus = "Checking…"
    VM->>Svc: CheckForUpdateAsync(ct)
    Svc->>GH: GET /repos/.../releases/latest\n(User-Agent, Accept: application/vnd.github.v3+json)
    alt 200 OK with valid tag_name
        GH-->>Svc: { tag_name, html_url }
        Svc-->>VM: UpToDate or UpdateAvailable(result)
    else HTTP 403 (rate limit)
        GH-->>Svc: 403
        Svc-->>VM: Failed(result)
    else Timeout/Network/Error
        GH-->>Svc: timeout/error
        Svc-->>VM: NetworkError(result)
    end
    VM->>UI: update UpdateStatus, LatestReleaseUrl\nset IsCheckingForUpdate = false
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibbled code and found a clue,
New converters, icons, and updates too,
I sniffed a GitHub tag with eager nose,
Brought versions, links, and tiny prose—
A hop for users, a carrot for you 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: About page icon and version display fixes, tray icon sizing improvements, and taskbar icon cache refresh—all primary objectives of the PR.

✏️ 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 fix/about-page-tray-icon

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

@twibster twibster self-assigned this Apr 15, 2026
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: 3

🧹 Nitpick comments (1)
AudioMonitorRouter/Views/MainWindow.xaml (1)

559-569: Prevent update status text from overflowing on long messages.

At Line 564, status text sits in a horizontal StackPanel; longer messages (e.g., network error details) can run wide and hurt readability. A small layout change to a Grid with a star column makes this robust.

♻️ Proposed layout tweak
-                                <StackPanel Orientation="Horizontal" Margin="0,0,0,8">
-                                    <wpfui:Button Content="Check for updates"
-                                                  Icon="ArrowSync24"
-                                                  Command="{Binding CheckForUpdatesCommand}"
-                                                  IsEnabled="{Binding IsCheckingForUpdate, Converter={StaticResource InverseBooleanConverter}}"/>
-                                    <TextBlock Text="{Binding UpdateStatus}"
-                                               VerticalAlignment="Center"
-                                               Margin="12,0,0,0"
-                                               FontSize="13"
-                                               Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
-                                </StackPanel>
+                                <Grid Margin="0,0,0,8">
+                                    <Grid.ColumnDefinitions>
+                                        <ColumnDefinition Width="Auto"/>
+                                        <ColumnDefinition Width="12"/>
+                                        <ColumnDefinition Width="*"/>
+                                    </Grid.ColumnDefinitions>
+                                    <wpfui:Button Grid.Column="0"
+                                                  Content="Check for updates"
+                                                  Icon="ArrowSync24"
+                                                  Command="{Binding CheckForUpdatesCommand}"
+                                                  IsEnabled="{Binding IsCheckingForUpdate, Converter={StaticResource InverseBooleanConverter}}"/>
+                                    <TextBlock Grid.Column="2"
+                                               Text="{Binding UpdateStatus}"
+                                               VerticalAlignment="Center"
+                                               FontSize="13"
+                                               TextWrapping="Wrap"
+                                               Foreground="{DynamicResource TextFillColorSecondaryBrush}"/>
+                                </Grid>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AudioMonitorRouter/Views/MainWindow.xaml` around lines 559 - 569, Replace the
horizontal StackPanel containing the Check for updates Button and the
UpdateStatus TextBlock with a Grid so the status TextBlock can live in a
star-sized column and wrap/truncate; specifically, keep the Button bound to
CheckForUpdatesCommand (and IsEnabled bound to IsCheckingForUpdate with the
InverseBooleanConverter) in a fixed-width column and move the UpdateStatus
TextBlock (Binding: UpdateStatus) into a second column with TextWrapping="Wrap"
or TextTrimming="CharacterEllipsis" and VerticalAlignment="Center" so long
status messages do not overflow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AudioMonitorRouter/Services/UpdateService.cs`:
- Around line 105-115: Replace the System.Version parse-and-fallback logic in
UpdateService (the block using Version.TryParse on variables latest and current
and returning UpdateCheckResult.UpToDate/UpdateAvailable) with proper SemVer
parsing and comparison using NuGet.Versioning.NuGetVersion: try
NuGetVersion.TryParse on both latest and current, and if both parse compare them
(NuGetVersion implements correct SemVer ordering) to decide UpToDate vs
UpdateAvailable; if only one parses, use the parsed value and the other as a
prerelease/unknown per SemVer rules (or conservatively treat as update) and
preserve the existing construction of UpdateCheckResult.UpdateAvailable/UpToDate
and the release.HtmlUrl fallback. Ensure you reference
NuGet.Versioning.NuGetVersion.TryParse and use NuGetVersion.CompareTo/Compare
operators instead of System.Version.

In `@design/tools/IconBuilder/Program.cs`:
- Around line 90-113: The WriteIco method fails if the target folder doesn't
exist; before creating the FileStream in WriteIco(string path) ensure the parent
directory exists by calling Directory.CreateDirectory on
Path.GetDirectoryName(path) (handle null/empty root safely) so the directory is
created when needed, then open the FileStream as before; this mirrors the
behavior in WritePng and prevents DirectoryNotFoundException for .ico outputs.

In `@installer/installer.iss`:
- Around line 84-89: The ie4uinit invocation runs in the elevated installer
token and needs the runasoriginaluser flag so it runs in the signed-in user's
context; update the installer entry that calls ie4uinit.exe (the line with
Filename: "{sys}\ie4uinit.exe"; Parameters: "-show"; Flags: ...) to include
runasoriginaluser alongside runhidden and skipifdoesntexist so Explorer's icon
cache is refreshed for the original user rather than the elevation/admin token.

---

Nitpick comments:
In `@AudioMonitorRouter/Views/MainWindow.xaml`:
- Around line 559-569: Replace the horizontal StackPanel containing the Check
for updates Button and the UpdateStatus TextBlock with a Grid so the status
TextBlock can live in a star-sized column and wrap/truncate; specifically, keep
the Button bound to CheckForUpdatesCommand (and IsEnabled bound to
IsCheckingForUpdate with the InverseBooleanConverter) in a fixed-width column
and move the UpdateStatus TextBlock (Binding: UpdateStatus) into a second column
with TextWrapping="Wrap" or TextTrimming="CharacterEllipsis" and
VerticalAlignment="Center" so long status messages do not overflow.
🪄 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: CHILL

Plan: Pro Plus

Run ID: f99e4364-77fd-482c-8de0-faab742c3f41

📥 Commits

Reviewing files that changed from the base of the PR and between 3b9c2df and dd034c4.

⛔ Files ignored due to path filters (1)
  • AudioMonitorRouter/Assets/app-icon.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • AudioMonitorRouter/App.xaml
  • AudioMonitorRouter/App.xaml.cs
  • AudioMonitorRouter/AudioMonitorRouter.csproj
  • AudioMonitorRouter/Services/UpdateService.cs
  • AudioMonitorRouter/ViewModels/MainWindowViewModel.cs
  • AudioMonitorRouter/Views/MainWindow.xaml
  • AudioMonitorRouter/Views/MainWindow.xaml.cs
  • design/tools/IconBuilder/Program.cs
  • installer/installer.iss

Comment on lines +105 to +115
if (!Version.TryParse(latest, out var latestVer) ||
!Version.TryParse(current, out var currentVer))
{
// If either side doesn't parse (e.g. "1.2.3-beta"), fall back to
// a simple string compare. Better to surface uncertainty than
// silently claim "up to date".
return string.Equals(latest, current, StringComparison.Ordinal)
? new UpdateCheckResult.UpToDate(current)
: new UpdateCheckResult.UpdateAvailable(current, latest,
release.HtmlUrl ?? $"https://github.com/twibster/AudioMonitorRouter/releases/tag/{release.TagName}");
}
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:

Does .NET System.Version.TryParseaccept semantic-version prerelease strings like1.2.3-beta.1, and if not, what .NET-supported library is commonly used to compare SemVer versions with prerelease labels?

💡 Result:

No, .NET System.Version.TryParse does not accept semantic-version prerelease strings like "1.2.3-beta.1". It requires the format major.minor[.build[.revision]] with numeric components only and fails on non-numeric prerelease suffixes, returning false. The commonly used .NET-supported library for comparing SemVer versions with prerelease labels is NuGet.Versioning from the NuGet package manager, specifically the NuGetVersion class (which inherits from SemanticVersion). It provides Parse/TryParse methods that handle prerelease labels like "-beta.1" and proper comparison logic following SemVer rules (with some NuGet-specific extensions). It is available via the NuGet.Versioning NuGet package. Alternatives include the "semver" NuGet package (SemVersion class) and older NuGet.Core SemanticVersion, but NuGet.Versioning is the most authoritative and widely used in the .NET ecosystem for package versioning.

Citations:


🏁 Script executed:

# Find and read the UpdateService.cs file around lines 105-115
fd -t f "UpdateService.cs" | head -5

Repository: twibster/AudioMonitorRouter

Length of output: 115


🏁 Script executed:

# Look for project files to check dependencies
fd -t f "\.(csproj|fsproj|vbproj)$" | head -10

Repository: twibster/AudioMonitorRouter

Length of output: 159


🏁 Script executed:

# Check if NuGet.Versioning is already referenced in the project
rg "NuGet\.Versioning|System\.Version" --type csharp

Repository: twibster/AudioMonitorRouter

Length of output: 53


🏁 Script executed:

# Read the UpdateService.cs file to verify the snippet
cat -n AudioMonitorRouter/Services/UpdateService.cs | sed -n '100,120p'

Repository: twibster/AudioMonitorRouter

Length of output: 1313


🏁 Script executed:

# Read the main project file to check current dependencies
cat AudioMonitorRouter/AudioMonitorRouter.csproj

Repository: twibster/AudioMonitorRouter

Length of output: 2584


🏁 Script executed:

# Search for any mention of SemVer, semantic versioning, or prerelease handling in codebase
rg -i "semver|semantic.*version|prerelease" --type csharp

Repository: twibster/AudioMonitorRouter

Length of output: 53


🏁 Script executed:

# Check git log for recent commits mentioning version comparison or SemVer
git log --oneline --all -20 | head -20

Repository: twibster/AudioMonitorRouter

Length of output: 1227


System.Version doesn't parse SemVer prerelease strings; the fallback approach has correctness implications.

When either version contains a prerelease label (e.g., 1.2.3-beta.1), System.Version.TryParse fails, and the code falls back to ordinal string equality. This means versions like 1.0.0 and 1.0.0-beta are treated as unequal, always triggering an "update available" result.

While this conservative fallback avoids false "up to date" claims, it doesn't implement proper SemVer comparison. Consider using NuGet.Versioning.NuGetVersion instead, which the .NET ecosystem standard library for SemVer parsing and comparison.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AudioMonitorRouter/Services/UpdateService.cs` around lines 105 - 115, Replace
the System.Version parse-and-fallback logic in UpdateService (the block using
Version.TryParse on variables latest and current and returning
UpdateCheckResult.UpToDate/UpdateAvailable) with proper SemVer parsing and
comparison using NuGet.Versioning.NuGetVersion: try NuGetVersion.TryParse on
both latest and current, and if both parse compare them (NuGetVersion implements
correct SemVer ordering) to decide UpToDate vs UpdateAvailable; if only one
parses, use the parsed value and the other as a prerelease/unknown per SemVer
rules (or conservatively treat as update) and preserve the existing construction
of UpdateCheckResult.UpdateAvailable/UpToDate and the release.HtmlUrl fallback.
Ensure you reference NuGet.Versioning.NuGetVersion.TryParse and use
NuGetVersion.CompareTo/Compare operators instead of System.Version.

Comment thread design/tools/IconBuilder/Program.cs
Comment thread installer/installer.iss Outdated
The previous design had 58px of padding top AND bottom (45% of the 256px
canvas empty vertically), which is why the taskbar glyph looked visibly
smaller than neighbour icons — at 24px cell size our artwork was only
~13px tall while Chrome/Explorer/etc fill ~22px.

Redraw with the same semantic layout (widescreen monitor + speaker +
soundwave + outbound arrow crossing the right bezel), but:

  • Monitor aspect 5:3 → 1.25:1 (170x100 → 170x140), shifted up/down to
    sit close to both canvas edges.
  • Strokes bumped 12→14 / 13→15 so linework doesn't look spindly at the
    new scale.
  • Speaker, soundwave, and arrow re-positioned so the arrow tail still
    starts just past the wave and the tip clears the bezel cleanly.

Content bounds:
                  before        after
    width fill    84%           89%
    height fill   55%           79%
    glyph@24px    ~20x13        ~21x19   (+45% visible area)

Regenerate both assets from the new SVG:

  dotnet run --project design/tools/IconBuilder -- \
      design/app-icon.svg AudioMonitorRouter/app.ico
  dotnet run --project design/tools/IconBuilder -- \
      design/app-icon.svg AudioMonitorRouter/Assets/app-icon.png
v2 still sat ~9%/12%/5%/6% padded (top/bottom/left/right) while peer
icons (Chrome, Explorer, etc.) fill ~3-4% per side. Pushing the glyph
harder into the canvas makes it read at a comparable weight on the
taskbar and tray.

- Content bounds now ~94% x 85% (was 89% x 79% in v2).
- Monitor aspect relaxed to 1.18:1 (was 1.25:1) to recover vertical
  height the old proportion was spending on empty padding. The stand
  still grounds it so it reads as a display, not a picture frame.
- Stand base widened (62..140 vs v2's narrower base) so the wider
  monitor frame still feels supported rather than top-heavy.
- Speaker, soundwave, and outbound arrow scaled proportionally; the
  arrow tip is now flush with the right canvas edge (round cap ends
  at x=247.5).
The bullet list of features was restating the app description in a less
skimmable form — anyone who reads the one-line description already knows
roughly what the app does, and the bullet format was the visual heaviest
block on the page.

Swapped it for a compact footer with:
  - A copyright line pulled from [AssemblyCopyright] (MSBuild fills it
    from the csproj's <Copyright>) with "(c)" rewritten to © for display.
  - Two external links via wpfui:Hyperlink with the Link24 glyph:
      Project website on GitHub    (repo root)
      Latest release on GitHub     (/releases/latest, GitHub redirects
                                    to the current tag)

UpdateService gained a Copyright property alongside CurrentVersion so
the VM has one place to read assembly metadata from.
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: 1

♻️ Duplicate comments (1)
AudioMonitorRouter/Services/UpdateService.cs (1)

115-125: ⚠️ Potential issue | 🟠 Major

Use a real SemVer comparison here.

This PR reads CurrentVersion from AssemblyInformationalVersion, so prerelease values like 1.2.4-beta.1 are a real input. System.Version.TryParse rejects those and drops into ordinal string comparison, which can misreport update availability.

Does .NET `System.Version.TryParse` accept semantic-version prerelease strings like `1.2.3-beta.1`, and what .NET-supported library is recommended for correct SemVer parsing and comparison?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AudioMonitorRouter/Services/UpdateService.cs` around lines 115 - 125, Replace
the System.Version-based parsing with a real SemVer parser: add a NuGet semver
library (e.g., NuGet.Versioning or Semver) and use its TryParse/Parse for the
variables latest and current instead of Version.TryParse; then compare the
parsed semantic versions (e.g., SemanticVersion or SemVersion comparison) to
decide whether to return UpdateCheckResult.UpToDate(current) or
UpdateCheckResult.UpdateAvailable(current, latest, ...). If either semver parse
fails, fall back to the existing ordinal string comparison fallback. Update the
logic around the Version.TryParse block in UpdateService.cs (the latest/current
parsing and comparison) and keep the same UpdateCheckResult branches and
release.HtmlUrl/release.TagName fallback URL.
🧹 Nitpick comments (1)
AudioMonitorRouter/Services/UpdateService.cs (1)

153-155: Don't show raw exception text to users.

AudioMonitorRouter/ViewModels/MainWindowViewModel.cs:704-718 displays Failed.Message verbatim. Returning ex.Message here leaks low-level parser/network wording to the About page and still drops the stack/context you need for diagnosis. Log the exception and return a stable user-facing message instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AudioMonitorRouter/Services/UpdateService.cs` around lines 153 - 155, In
UpdateService.cs replace the catch block that returns new
UpdateCheckResult.Failed(ex.Message) so you log the full exception for
diagnostics (e.g., _logger.Error(ex, "Update check failed") or similar existing
logging/telemetry) and return a stable, user-facing message instead (for
example: new UpdateCheckResult.Failed("Could not check for updates at this time.
Please try again later.")); keep the exception out of the Failed.Message shown
in the UI and ensure the exception object is logged/preserved for debugging
while UpdateCheckResult.Failed carries only the friendly text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AudioMonitorRouter/Services/UpdateService.cs`:
- Around line 123-124: When constructing the fallback release URL in
UpdateService (where UpdateCheckResult.UpdateAvailable is created), escape the
tag before concatenation to avoid broken links for tags with reserved
characters; replace direct use of release.TagName with an encoded form (e.g.,
Uri.EscapeDataString or WebUtility.UrlEncode) in both locations (the current
fallback at the shown line and the similar occurrence around lines 132-133) so
the generated "releases/tag/{...}" segment is URL-safe.

---

Duplicate comments:
In `@AudioMonitorRouter/Services/UpdateService.cs`:
- Around line 115-125: Replace the System.Version-based parsing with a real
SemVer parser: add a NuGet semver library (e.g., NuGet.Versioning or Semver) and
use its TryParse/Parse for the variables latest and current instead of
Version.TryParse; then compare the parsed semantic versions (e.g.,
SemanticVersion or SemVersion comparison) to decide whether to return
UpdateCheckResult.UpToDate(current) or
UpdateCheckResult.UpdateAvailable(current, latest, ...). If either semver parse
fails, fall back to the existing ordinal string comparison fallback. Update the
logic around the Version.TryParse block in UpdateService.cs (the latest/current
parsing and comparison) and keep the same UpdateCheckResult branches and
release.HtmlUrl/release.TagName fallback URL.

---

Nitpick comments:
In `@AudioMonitorRouter/Services/UpdateService.cs`:
- Around line 153-155: In UpdateService.cs replace the catch block that returns
new UpdateCheckResult.Failed(ex.Message) so you log the full exception for
diagnostics (e.g., _logger.Error(ex, "Update check failed") or similar existing
logging/telemetry) and return a stable, user-facing message instead (for
example: new UpdateCheckResult.Failed("Could not check for updates at this time.
Please try again later.")); keep the exception out of the Failed.Message shown
in the UI and ensure the exception object is logged/preserved for debugging
while UpdateCheckResult.Failed carries only the friendly text.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 6b161d27-9114-4c97-8e8d-746b778ebe9f

📥 Commits

Reviewing files that changed from the base of the PR and between dd034c4 and 7799979.

⛔ Files ignored due to path filters (3)
  • AudioMonitorRouter/Assets/app-icon.png is excluded by !**/*.png
  • AudioMonitorRouter/app.ico is excluded by !**/*.ico
  • design/app-icon.svg is excluded by !**/*.svg
📒 Files selected for processing (3)
  • AudioMonitorRouter/Services/UpdateService.cs
  • AudioMonitorRouter/ViewModels/MainWindowViewModel.cs
  • AudioMonitorRouter/Views/MainWindow.xaml
🚧 Files skipped from review as they are similar to previous changes (2)
  • AudioMonitorRouter/Views/MainWindow.xaml
  • AudioMonitorRouter/ViewModels/MainWindowViewModel.cs

Comment thread AudioMonitorRouter/Services/UpdateService.cs Outdated
Three adjustments to the About card:

  1. Copyright moves into the identity block next to the icon, stacked
     directly under the version line. One visual unit now answers "what
     is this and who made it" instead of scattering that info across
     the card.
  2. The one-sentence app description is dropped. It was re-explaining
     what the sidebar and home page already make obvious.
  3. Links section lifts into the vacated slot; Updates drops to the
     bottom. Reading order is now identity → external links → action
     (check for updates), which matches what someone actually comes to
     the About page to do.

Also updated the <Copyright> csproj property (and the UpdateService
fallback that mirrors it) from "twibster" to "Omar Omran" — that's the
real copyright holder; "twibster" is just the GitHub handle.
The two hyperlinks were being rendered with wpfui:Button's default
Padding (11,5,11,6), which made the stack read as two spaced-out
buttons instead of a compact link list like the rest of the app's
peers (LLT, EarTrumpet).

Dropped Padding to 8,2 and removed the 4px inter-link margin. The
horizontal hit target stays generous (16px total) so clicking remains
forgiving; only the vertical whitespace shrinks.
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.

🧹 Nitpick comments (1)
AudioMonitorRouter/Services/UpdateService.cs (1)

165-179: Consider caching the version string to avoid repeated reflection.

GetInformationalVersion() is called from both CurrentVersion and BuildUserAgent(). While the overhead is minimal, the value never changes at runtime. A static readonly field would eliminate redundant reflection calls.

♻️ Suggested refactor
+    private static readonly string _informationalVersion = ComputeInformationalVersion();
+
     public string CurrentVersion => GetInformationalVersion();

-    private static string GetInformationalVersion()
+    private static string GetInformationalVersion() => _informationalVersion;
+
+    private static string ComputeInformationalVersion()
     {
         var asm = Assembly.GetEntryAssembly() ?? typeof(UpdateService).Assembly;
         // ... rest unchanged
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@AudioMonitorRouter/Services/UpdateService.cs` around lines 165 - 179,
GetInformationalVersion() performs reflection every time it’s called from
CurrentVersion and BuildUserAgent; replace that repeated work by computing the
value once into a static readonly string (e.g., a private static readonly field
like s_informationalVersion) and have GetInformationalVersion() (or callers)
return that cached field instead; update references in CurrentVersion and
BuildUserAgent to use the cached value and ensure initialization happens once at
type load so the reflection call is eliminated at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@AudioMonitorRouter/Services/UpdateService.cs`:
- Around line 165-179: GetInformationalVersion() performs reflection every time
it’s called from CurrentVersion and BuildUserAgent; replace that repeated work
by computing the value once into a static readonly string (e.g., a private
static readonly field like s_informationalVersion) and have
GetInformationalVersion() (or callers) return that cached field instead; update
references in CurrentVersion and BuildUserAgent to use the cached value and
ensure initialization happens once at type load so the reflection call is
eliminated at runtime.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa0c76f4-5813-4ab2-bd17-8213616de195

📥 Commits

Reviewing files that changed from the base of the PR and between 7799979 and 2243ed8.

📒 Files selected for processing (3)
  • AudioMonitorRouter/AudioMonitorRouter.csproj
  • AudioMonitorRouter/Services/UpdateService.cs
  • AudioMonitorRouter/Views/MainWindow.xaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • AudioMonitorRouter/AudioMonitorRouter.csproj

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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@AudioMonitorRouter/Views/MainWindow.xaml`:
- Around line 560-569: The UpdateStatus TextBlock can be clipped because it's
inside a horizontal StackPanel; change the layout so the button gets auto width
and the TextBlock can take remaining space and wrap. Replace the StackPanel
containing the Button (Command="{Binding CheckForUpdatesCommand}",
IsEnabled="{Binding IsCheckingForUpdate, Converter={StaticResource
InverseBooleanConverter}}") and the TextBlock (Text="{Binding UpdateStatus}")
with a Grid with two columns (first "Auto" for the Button, second "*" for the
TextBlock), and set the TextBlock properties TextWrapping="Wrap" and
TextTrimming="CharacterEllipsis" (and optionally a MaxWidth or Margin) so long
UpdateStatus strings from MainWindowViewModel (e.g., the long network/failure
messages) render correctly.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 04de2bc4-06f4-4a1f-9797-091f5780f847

📥 Commits

Reviewing files that changed from the base of the PR and between 2243ed8 and 0a940be.

📒 Files selected for processing (1)
  • AudioMonitorRouter/Views/MainWindow.xaml

Comment thread AudioMonitorRouter/Views/MainWindow.xaml Outdated
- UpdateService: replace Version.TryParse-only compare with a local
  CompareSemVer helper so "1.2.3-beta" vs "1.2.3" no longer falls back
  to an ordinal string compare (which would incorrectly report the
  prerelease as a pending "update"). Numeric core via System.Version,
  prerelease tail with the rule that release outranks prerelease.
- UpdateService: Uri.EscapeDataString the tag name when composing the
  fallback release URL — tag names can legally contain '/' or '#',
  which would break the path segment.
- IconBuilder WriteIco: mirror WritePng's Directory.CreateDirectory so
  pointing at a not-yet-existing Assets/ subfolder doesn't throw.
- installer.iss: add runasoriginaluser to the ie4uinit -show step so
  over-the-shoulder UAC installs refresh the signed-in user's Explorer
  icon cache instead of the admin's.
- MainWindow About/Updates: swap the horizontal StackPanel for a Grid
  (Auto + *) with TextWrapping=Wrap on the status line, so long
  "Update available: x.y.z" messages wrap inside the card instead of
  clipping off the right edge.
@twibster twibster merged commit 8487afa into main Apr 15, 2026
1 of 2 checks passed
@github-actions
Copy link
Copy Markdown

Released as v1.3.0.

@twibster twibster deleted the fix/about-page-tray-icon branch May 5, 2026 00:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:minor 0.X.0 release - new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant