Skip to content

Fix chrome menu disappearing after 5 seconds#10

Merged
Computer-Tsu merged 4 commits intomainfrom
dev
Mar 31, 2026
Merged

Fix chrome menu disappearing after 5 seconds#10
Computer-Tsu merged 4 commits intomainfrom
dev

Conversation

@Computer-Tsu
Copy link
Copy Markdown
Owner

Summary

  • Removes the 5-second DispatcherTimer that auto-hid the chrome menu bar
  • Replaces with a focus/hover model that matches how users expect application menus to behave
  • Enables Alt+letter keyboard menu navigation (Alt+F, Alt+H, etc.)

Behaviour change

Trigger Before After
Mouse enters window No effect Chrome visible
Left-click anywhere Chrome visible, 5s timer starts Chrome pinned until focus lost
Alt / Alt+letter No effect Chrome pinned, keyboard nav works
Window loses focus Chrome hidden Chrome hidden, pin cleared
Mouse leaves No effect Chrome hidden if not pinned

What was checked

Test plan

  • Hover mouse over window — chrome appears
  • Move mouse away without clicking — chrome hides
  • Click anywhere — chrome stays visible
  • Click outside PortPane — chrome hides
  • Press Alt — chrome appears, arrow keys navigate the menu
  • Press Alt+F — File menu opens
  • Plug/unplug USB device — audio and COM port lists still update correctly

ChannelInfo.cs introduces ReleaseChannel enum with per-branch constants
for build expiry, feature unlock, telemetry defaults, logging level, and
version suffix. Protected from forward merges via .gitattributes merge=ours.

App.xaml.cs gains a kill switch (BuildExpiryDays), --reset flag for clean
reinstall, and channel-aware Serilog minimum level. LicenseService bypasses
validation when UnlockAllForTesting is set. SettingsService applies channel-
aware telemetry default on fresh installs.

BrandingInfo.Version bumped to 0.5.2. BuildDate constant added for CI
stamping. FullVersion property composes display string from version,
channel suffix, and build timestamp.

CI updated: dev branch trigger, channel-aware version composition, BuildDate
stamp step, and rolling-alpha job that keeps latest-alpha release current.
…piry

Introduces ChannelInfo.cs (already committed) driving channel-specific
behaviour across all three future branches (alpha/beta/stable).

Changes in this commit:
- BrandingInfo: Version → 0.5.2, adds BuildDate (CI-stamped), FullVersion
  (composed with channel suffix), DaysRemaining (countdown to expiry)
- LicenseService: internal unlockForTesting constructor so tests can opt out
  of the alpha Personal-tier override independently of ChannelInfo
- LicenseValidationTests: three tests updated to use unlockForTesting:false,
  fixing the CI build failure caused by UnlockAllForTesting=true on alpha
- MainViewModel: IsPreRelease, ChannelBadgeText, BuildExpiryText properties
  for the drag strip watermark binding
- MainWindow.xaml: ALPHA/BETA badge + days-remaining countdown in drag strip,
  right-aligned, hidden on stable builds via BoolToVis on IsPreRelease
- README: dev/main build status badges, alpha/stable download channel table
- CHANGELOG: [0.5.1-beta] promoted, [Unreleased] documents 0.5.2 additions
Replaces the 5-second DispatcherTimer auto-hide with a focus/hover model:

- Mouse enters window → chrome visible (hover, unpinned)
- Left-click anywhere → chrome pinned (stays until window loses focus)
- Alt / Alt+letter → chrome pinned so WPF keyboard menu navigation works
- Window loses focus (Deactivated) → chrome hidden, pin cleared
- Mouse leaves without clicking → chrome hidden if not pinned

Removes DispatcherTimer and using System.Windows.Threading entirely.
No impact on hardware refresh — audio and COM port updates are WMI
event-driven and have no dependency on the chrome visibility timer.
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 31, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA e599162.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

}

// ── Build expiry check (alpha/beta only) ──────────────────────────────
if (ChannelInfo.BuildExpiryDays > 0 && !string.IsNullOrEmpty(BrandingInfo.BuildDate)

Check warning

Code scanning / CodeQL

Constant condition Warning

Condition always evaluates to 'true'.

Copilot Autofix

AI 4 days ago

In general, to fix a constant condition you either (1) remove the redundant constant part if it does not affect behavior, or (2) replace it with the correct, non-constant predicate that reflects the intended logic. Here, CodeQL indicates ChannelInfo.BuildExpiryDays > 0 is always true, so it does not influence whether the if block runs. The actual gating factors are !string.IsNullOrEmpty(BrandingInfo.BuildDate) and the success of DateTimeOffset.TryParse.

The best behavior‑preserving change is to simplify the condition by removing the constant subcondition and leaving the truly variable checks. That keeps the build-expiry feature working exactly as before, but avoids the misleading constant comparison and satisfies the analyzer. Concretely, in src/PortPane/App.xaml.cs, change the if at lines 34–37 to:

if (!string.IsNullOrEmpty(BrandingInfo.BuildDate)
    && DateTimeOffset.TryParse(BrandingInfo.BuildDate, null,
        System.Globalization.DateTimeStyles.RoundtripKind, out var buildDate))
{
    ...
}

No new methods, fields, or imports are required.


Suggested changeset 1
src/PortPane/App.xaml.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/App.xaml.cs b/src/PortPane/App.xaml.cs
--- a/src/PortPane/App.xaml.cs
+++ b/src/PortPane/App.xaml.cs
@@ -31,7 +31,7 @@
         }
 
         // ── Build expiry check (alpha/beta only) ──────────────────────────────
-        if (ChannelInfo.BuildExpiryDays > 0 && !string.IsNullOrEmpty(BrandingInfo.BuildDate)
+        if (!string.IsNullOrEmpty(BrandingInfo.BuildDate)
             && DateTimeOffset.TryParse(BrandingInfo.BuildDate, null,
                 System.Globalization.DateTimeStyles.RoundtripKind, out var buildDate))
         {
EOF
@@ -31,7 +31,7 @@
}

// ── Build expiry check (alpha/beta only) ──────────────────────────────
if (ChannelInfo.BuildExpiryDays > 0 && !string.IsNullOrEmpty(BrandingInfo.BuildDate)
if (!string.IsNullOrEmpty(BrandingInfo.BuildDate)
&& DateTimeOffset.TryParse(BrandingInfo.BuildDate, null,
System.Globalization.DateTimeStyles.RoundtripKind, out var buildDate))
{
Copilot is powered by AI and may make mistakes. Always verify output.
}

// ── Build expiry check (alpha/beta only) ──────────────────────────────
if (ChannelInfo.BuildExpiryDays > 0 && !string.IsNullOrEmpty(BrandingInfo.BuildDate)

Check warning

Code scanning / CodeQL

Constant condition Warning

Condition always evaluates to 'true'.

Copilot Autofix

AI 4 days ago

In general, to fix a constant condition you either (1) remove the redundant part if it’s truly invariant, or (2) adjust the logic so it matches the intended behavior, ensuring that no sub-condition is trivially always true/false under normal operation.

Here, CodeQL says string.IsNullOrEmpty(BrandingInfo.BuildDate) is always true. Under that assumption, the current guard:

if (ChannelInfo.BuildExpiryDays > 0 && !string.IsNullOrEmpty(BrandingInfo.BuildDate)
    && DateTimeOffset.TryParse(...))
{
    ...
}

has a middle term that is always false, so the whole if is never entered and the build-expiry feature is dead. The minimal behavior-preserving change is to remove the logically impossible check against BrandingInfo.BuildDate and rely solely on the TryParse call to decide whether a valid build date is available. DateTimeOffset.TryParse already handles null and empty strings safely (it just returns false), so the extra !string.IsNullOrEmpty is redundant in any case.

Concretely, in src/PortPane/App.xaml.cs around line 34, update the if condition to drop !string.IsNullOrEmpty(BrandingInfo.BuildDate) and keep only the expiry-days check plus the TryParse call. No additional imports, methods, or definitions are needed; we only adjust the condition expression.

Suggested changeset 1
src/PortPane/App.xaml.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/App.xaml.cs b/src/PortPane/App.xaml.cs
--- a/src/PortPane/App.xaml.cs
+++ b/src/PortPane/App.xaml.cs
@@ -31,7 +31,7 @@
         }
 
         // ── Build expiry check (alpha/beta only) ──────────────────────────────
-        if (ChannelInfo.BuildExpiryDays > 0 && !string.IsNullOrEmpty(BrandingInfo.BuildDate)
+        if (ChannelInfo.BuildExpiryDays > 0
             && DateTimeOffset.TryParse(BrandingInfo.BuildDate, null,
                 System.Globalization.DateTimeStyles.RoundtripKind, out var buildDate))
         {
EOF
@@ -31,7 +31,7 @@
}

// ── Build expiry check (alpha/beta only) ──────────────────────────────
if (ChannelInfo.BuildExpiryDays > 0 && !string.IsNullOrEmpty(BrandingInfo.BuildDate)
if (ChannelInfo.BuildExpiryDays > 0
&& DateTimeOffset.TryParse(BrandingInfo.BuildDate, null,
System.Globalization.DateTimeStyles.RoundtripKind, out var buildDate))
{
Copilot is powered by AI and may make mistakes. Always verify output.
{
get
{
if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix))

Check warning

Code scanning / CodeQL

Constant condition Warning

Condition always evaluates to 'false'.

Copilot Autofix

AI 4 days ago

General approach: normalize ChannelInfo.VersionSuffix into a local variable and base all conditions on that local, ensuring that the condition is not provably constant to the analyzer and clarifying the logic. This avoids removing any behavior while addressing the constant‑condition warning.

Concrete fix in src/PortPane/BrandingInfo.cs, property FullVersion (lines 25–38):

  1. At the top of the getter, introduce var suffix = ChannelInfo.VersionSuffix ?? string.Empty;.
  2. Replace if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix)) with if (string.IsNullOrEmpty(suffix)).
  3. In the final return, replace $"{Version}-{ChannelInfo.VersionSuffix}" with $"{Version}-{suffix}".

No new imports or types are required; we use only string.Empty, which is already in System. This keeps behavior the same: if the suffix is null or empty, FullVersion returns Version. For alpha builds, we still generate the special alpha string; if that doesn’t apply, we append the normalized suffix.

Suggested changeset 1
src/PortPane/BrandingInfo.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/BrandingInfo.cs b/src/PortPane/BrandingInfo.cs
--- a/src/PortPane/BrandingInfo.cs
+++ b/src/PortPane/BrandingInfo.cs
@@ -26,7 +26,8 @@
     {
         get
         {
-            if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix))
+            var suffix = ChannelInfo.VersionSuffix ?? string.Empty;
+            if (string.IsNullOrEmpty(suffix))
                 return Version;
 
             if (ChannelInfo.Channel == ReleaseChannel.Alpha && !string.IsNullOrEmpty(BuildDate)
@@ -34,7 +35,7 @@
                     System.Globalization.DateTimeStyles.RoundtripKind, out var dt))
                 return $"{Version}-alpha.{dt:yyyyMMdd}";
 
-            return $"{Version}-{ChannelInfo.VersionSuffix}";
+            return $"{Version}-{suffix}";
         }
     }
     /// <summary>
EOF
@@ -26,7 +26,8 @@
{
get
{
if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix))
var suffix = ChannelInfo.VersionSuffix ?? string.Empty;
if (string.IsNullOrEmpty(suffix))
return Version;

if (ChannelInfo.Channel == ReleaseChannel.Alpha && !string.IsNullOrEmpty(BuildDate)
@@ -34,7 +35,7 @@
System.Globalization.DateTimeStyles.RoundtripKind, out var dt))
return $"{Version}-alpha.{dt:yyyyMMdd}";

return $"{Version}-{ChannelInfo.VersionSuffix}";
return $"{Version}-{suffix}";
}
}
/// <summary>
Copilot is powered by AI and may make mistakes. Always verify output.
if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix))
return Version;

if (ChannelInfo.Channel == ReleaseChannel.Alpha && !string.IsNullOrEmpty(BuildDate)

Check warning

Code scanning / CodeQL

Constant condition Warning

Condition always evaluates to 'true'.

Copilot Autofix

AI 4 days ago

In general, to fix a constant condition you either (1) remove the redundant constant subcondition if it doesn’t affect behavior, or (2) refactor so that the condition can truly vary at runtime (for example, by not using a const/readonly value that is always the same). Here, we must not assume or change the behavior outside this snippet, so we should preserve the runtime behavior while eliminating the constant part that CodeQL flags.

Given that CodeQL believes ChannelInfo.Channel == ReleaseChannel.Alpha is always true, the behavior of FullVersion today is: if ChannelInfo.VersionSuffix is null/empty, return Version; otherwise, if BuildDate is non‑empty and parseable, return an alpha‑style string ${Version}-alpha.{yyyyMMdd}; otherwise, return ${Version}-{ChannelInfo.VersionSuffix}. Removing just the ChannelInfo.Channel == ReleaseChannel.Alpha && from the condition keeps exactly the same behavior under the current assumption that the channel is always Alpha, while eliminating the constant expression. So, in src/PortPane/BrandingInfo.cs, inside the FullVersion getter, change the if at lines 32–35 to drop the ChannelInfo.Channel == ReleaseChannel.Alpha && portion and leave the rest intact. No new imports or helpers are needed.

Suggested changeset 1
src/PortPane/BrandingInfo.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/BrandingInfo.cs b/src/PortPane/BrandingInfo.cs
--- a/src/PortPane/BrandingInfo.cs
+++ b/src/PortPane/BrandingInfo.cs
@@ -29,7 +29,7 @@
             if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix))
                 return Version;
 
-            if (ChannelInfo.Channel == ReleaseChannel.Alpha && !string.IsNullOrEmpty(BuildDate)
+            if (!string.IsNullOrEmpty(BuildDate)
                 && DateTimeOffset.TryParse(BuildDate, null,
                     System.Globalization.DateTimeStyles.RoundtripKind, out var dt))
                 return $"{Version}-alpha.{dt:yyyyMMdd}";
EOF
@@ -29,7 +29,7 @@
if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix))
return Version;

if (ChannelInfo.Channel == ReleaseChannel.Alpha && !string.IsNullOrEmpty(BuildDate)
if (!string.IsNullOrEmpty(BuildDate)
&& DateTimeOffset.TryParse(BuildDate, null,
System.Globalization.DateTimeStyles.RoundtripKind, out var dt))
return $"{Version}-alpha.{dt:yyyyMMdd}";
Copilot is powered by AI and may make mistakes. Always verify output.
if (string.IsNullOrEmpty(ChannelInfo.VersionSuffix))
return Version;

if (ChannelInfo.Channel == ReleaseChannel.Alpha && !string.IsNullOrEmpty(BuildDate)

Check warning

Code scanning / CodeQL

Constant condition Warning

Condition always evaluates to 'true'.

Copilot Autofix

AI 4 days ago

In general, to fix this constant condition we must ensure BuildDate is not a compile‑time constant. Changing it from const to a runtime‑assigned value (e.g., static readonly or a normal static field) prevents the compiler from treating it as a constant and allows string.IsNullOrEmpty(BuildDate) to depend on the CI‑provided value as intended.

The single best, minimal‑behavior‑change fix here is:

  • Change public const string BuildDate = ""; to public static readonly string BuildDate = "";.

This preserves the public API surface (a static string field named BuildDate), keeps the default empty value in source, and allows the CI process or configuration system to override it without fighting constant inlining. After this change, string.IsNullOrEmpty(BuildDate) on line 32 (and line 49) will no longer be a constant expression, so the alpha/version and expiry logic will work as designed.

No new methods or special imports are needed; we only adjust the field declaration for BuildDate in src/PortPane/BrandingInfo.cs.

Suggested changeset 1
src/PortPane/BrandingInfo.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/BrandingInfo.cs b/src/PortPane/BrandingInfo.cs
--- a/src/PortPane/BrandingInfo.cs
+++ b/src/PortPane/BrandingInfo.cs
@@ -15,7 +15,7 @@
     /// ISO 8601 UTC build timestamp. Empty string in source — patched by CI at
     /// publish time. Used by App.xaml.cs to enforce ChannelInfo.BuildExpiryDays.
     /// </summary>
-    public const string BuildDate         = "";
+    public static readonly string BuildDate = "";
 
     /// <summary>
     /// Full version string for display, logging, and telemetry.
EOF
@@ -15,7 +15,7 @@
/// ISO 8601 UTC build timestamp. Empty string in source — patched by CI at
/// publish time. Used by App.xaml.cs to enforce ChannelInfo.BuildExpiryDays.
/// </summary>
public const string BuildDate = "";
public static readonly string BuildDate = "";

/// <summary>
/// Full version string for display, logging, and telemetry.
Copilot is powered by AI and may make mistakes. Always verify output.

// ── Channel / build info (alpha/beta only) ────────────────────────────────

public bool IsPreRelease => ChannelInfo.Channel != ReleaseChannel.Stable;

Check warning

Code scanning / CodeQL

Comparison is constant Warning

This comparison is always true.

Copilot Autofix

AI 4 days ago

In general, a “comparison is constant” issue should be fixed either by (a) changing the types/values so the comparison can be both true and false, if that’s the intended behavior, or (b) removing/replacing the comparison with a simpler expression that reflects the real invariant behavior. Here, CodeQL indicates that ChannelInfo.Channel != ReleaseChannel.Stable is always true under the project’s assumptions, so the IsPreRelease property is effectively “always true.” The safest fix that does not alter runtime behavior is to rewrite IsPreRelease so it clearly expresses the actual behavior without a redundant comparison.

The single best minimal-change fix is to replace ChannelInfo.Channel != ReleaseChannel.Stable with a direct constant true, accompanied by a comment explaining the pre-release-only nature of this build, or—better—express it as a positive check for the known channels used in this build (e.g., ChannelInfo.Channel is ReleaseChannel.Alpha or ReleaseChannel.Beta) if we want to keep some semantic link. However, since we must not assume unshown enum values and we must preserve existing behavior, the most reliable option is to set IsPreRelease to true unconditionally. That keeps the property’s public contract unchanged (it was always true at runtime per CodeQL) while eliminating the trivially true comparison.

Concretely, in src/PortPane/ViewModels/MainViewModel.cs, we will modify the IsPreRelease property declaration at line 93. No new imports, methods, or other definitions are required: it is a one-line change within the shown snippet.

Suggested changeset 1
src/PortPane/ViewModels/MainViewModel.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/ViewModels/MainViewModel.cs b/src/PortPane/ViewModels/MainViewModel.cs
--- a/src/PortPane/ViewModels/MainViewModel.cs
+++ b/src/PortPane/ViewModels/MainViewModel.cs
@@ -90,7 +90,7 @@
 
     // ── Channel / build info (alpha/beta only) ────────────────────────────────
 
-    public bool   IsPreRelease     => ChannelInfo.Channel != ReleaseChannel.Stable;
+    public bool   IsPreRelease     => true; // This build only targets pre-release channels.
     public string ChannelBadgeText => ChannelInfo.Channel == ReleaseChannel.Alpha ? "ALPHA" : "BETA";
     public string BuildExpiryText
     {
EOF
@@ -90,7 +90,7 @@

// ── Channel / build info (alpha/beta only) ────────────────────────────────

public bool IsPreRelease => ChannelInfo.Channel != ReleaseChannel.Stable;
public bool IsPreRelease => true; // This build only targets pre-release channels.
public string ChannelBadgeText => ChannelInfo.Channel == ReleaseChannel.Alpha ? "ALPHA" : "BETA";
public string BuildExpiryText
{
Copilot is powered by AI and may make mistakes. Always verify output.
// ── Channel / build info (alpha/beta only) ────────────────────────────────

public bool IsPreRelease => ChannelInfo.Channel != ReleaseChannel.Stable;
public string ChannelBadgeText => ChannelInfo.Channel == ReleaseChannel.Alpha ? "ALPHA" : "BETA";

Check warning

Code scanning / CodeQL

Constant condition Warning

Condition always evaluates to 'true'.

Copilot Autofix

AI 4 days ago

  • In general, constant conditions should be removed or refactored so that the code explicitly reflects the only possible outcome instead of pretending to branch.
  • Here, since the analyzer sees ChannelInfo.Channel as always equal to ReleaseChannel.Alpha, the ternary operator in ChannelBadgeText can be simplified to just return "ALPHA". This preserves existing functionality (the expression already always evaluates to "ALPHA" in the analyzed configuration) and removes the constant condition.
  • Concretely, in src/PortPane/ViewModels/MainViewModel.cs, replace the line public string ChannelBadgeText => ChannelInfo.Channel == ReleaseChannel.Alpha ? "ALPHA" : "BETA"; with public string ChannelBadgeText => "ALPHA";.
  • No new methods, imports, or other definitions are needed; this is a simple expression replacement.
Suggested changeset 1
src/PortPane/ViewModels/MainViewModel.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/ViewModels/MainViewModel.cs b/src/PortPane/ViewModels/MainViewModel.cs
--- a/src/PortPane/ViewModels/MainViewModel.cs
+++ b/src/PortPane/ViewModels/MainViewModel.cs
@@ -91,7 +91,7 @@
     // ── Channel / build info (alpha/beta only) ────────────────────────────────
 
     public bool   IsPreRelease     => ChannelInfo.Channel != ReleaseChannel.Stable;
-    public string ChannelBadgeText => ChannelInfo.Channel == ReleaseChannel.Alpha ? "ALPHA" : "BETA";
+    public string ChannelBadgeText => "ALPHA";
     public string BuildExpiryText
     {
         get
EOF
@@ -91,7 +91,7 @@
// ── Channel / build info (alpha/beta only) ────────────────────────────────

public bool IsPreRelease => ChannelInfo.Channel != ReleaseChannel.Stable;
public string ChannelBadgeText => ChannelInfo.Channel == ReleaseChannel.Alpha ? "ALPHA" : "BETA";
public string ChannelBadgeText => "ALPHA";
public string BuildExpiryText
{
get
Copilot is powered by AI and may make mistakes. Always verify output.
// ── Reset flag (wipes all user data and relaunches fresh) ─────────────
if (e.Args.Contains("--reset", StringComparer.OrdinalIgnoreCase))
{
bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));

Check notice

Code scanning / CodeQL

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments Note

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments.

Copilot Autofix

AI 4 days ago

General fix: When composing paths where the intent is “base directory + relative subpath/file”, prefer Path.Join instead of Path.Combine. Path.Join does not treat later absolute segments in a way that discards earlier ones; it simply concatenates them with directory separators as needed. For cases where absolute paths are expected and must override earlier segments, Path.Combine may still be appropriate, but here we clearly want to append "portable.txt" to the base directory.

Best fix for this code: Replace the single usage of Path.Combine at line 54 with Path.Join, leaving the rest of the logic unchanged. This keeps functionality identical—AppContext.BaseDirectory plus "portable.txt"—but removes the specific CodeQL‑flagged pattern. No extra imports are needed because Path is in System.IO, which is already available in the base class library; and the file already references other System.* namespaces, so adding using System.IO; is optional for this edit (the existing code compiles because Path is fully qualified via System.IO.Path implicitly from mscorlib/System.Private.CoreLib). We only touch the shown snippet inside src/PortPane/App.xaml.cs.

Concretely:

  • In OnStartup, in the --reset block, update:
bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));

to:

bool isPortable = File.Exists(Path.Join(AppContext.BaseDirectory, "portable.txt"));

No other code changes are required.


Suggested changeset 1
src/PortPane/App.xaml.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/App.xaml.cs b/src/PortPane/App.xaml.cs
--- a/src/PortPane/App.xaml.cs
+++ b/src/PortPane/App.xaml.cs
@@ -51,7 +51,7 @@
         // ── Reset flag (wipes all user data and relaunches fresh) ─────────────
         if (e.Args.Contains("--reset", StringComparer.OrdinalIgnoreCase))
         {
-            bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));
+            bool isPortable = File.Exists(Path.Join(AppContext.BaseDirectory, "portable.txt"));
             string dataDir = isPortable
                 ? Path.Combine(AppContext.BaseDirectory, "PortPane-Data")
                 : Path.Combine(
EOF
@@ -51,7 +51,7 @@
// ── Reset flag (wipes all user data and relaunches fresh) ─────────────
if (e.Args.Contains("--reset", StringComparer.OrdinalIgnoreCase))
{
bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));
bool isPortable = File.Exists(Path.Join(AppContext.BaseDirectory, "portable.txt"));
string dataDir = isPortable
? Path.Combine(AppContext.BaseDirectory, "PortPane-Data")
: Path.Combine(
Copilot is powered by AI and may make mistakes. Always verify output.
{
bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));
string dataDir = isPortable
? Path.Combine(AppContext.BaseDirectory, "PortPane-Data")

Check notice

Code scanning / CodeQL

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments Note

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments.

Copilot Autofix

AI 4 days ago

In general, to avoid Path.Combine silently discarding earlier segments when later segments may be absolute, you should either ensure all later segments are relative, or use Path.Join, which concatenates segments without treating absolute segments specially. Here, we want “base directory + fixed subfolder name” semantics, and the best fix is to use Path.Join instead of Path.Combine for those paths that are conceptually “base + child” and not meant to re-root.

Concretely, in src/PortPane/App.xaml.cs within the --reset handling block, change:

  • Path.Combine(AppContext.BaseDirectory, "PortPane-Data") to Path.Join(AppContext.BaseDirectory, "PortPane-Data").

This preserves the effective path today (because "PortPane-Data" is relative), but removes the risk of dropping the base directory if that string ever becomes absolute. No additional imports are required because Path.Join is in System.IO.Path, already in scope via System / System.IO being part of the BCL; the file already uses File and Directory, so the runtime environment clearly supports these APIs. Other Path.Combine calls in the snippet (those involving Environment.GetFolderPath and branding strings) are correctly using Combine to handle rooted paths and should remain unchanged.

Suggested changeset 1
src/PortPane/App.xaml.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/App.xaml.cs b/src/PortPane/App.xaml.cs
--- a/src/PortPane/App.xaml.cs
+++ b/src/PortPane/App.xaml.cs
@@ -53,7 +53,7 @@
         {
             bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));
             string dataDir = isPortable
-                ? Path.Combine(AppContext.BaseDirectory, "PortPane-Data")
+                ? Path.Join(AppContext.BaseDirectory, "PortPane-Data")
                 : Path.Combine(
                     Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                     BrandingInfo.SuiteName, BrandingInfo.AppName);
EOF
@@ -53,7 +53,7 @@
{
bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));
string dataDir = isPortable
? Path.Combine(AppContext.BaseDirectory, "PortPane-Data")
? Path.Join(AppContext.BaseDirectory, "PortPane-Data")
: Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
BrandingInfo.SuiteName, BrandingInfo.AppName);
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +57 to +59
: Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
BrandingInfo.SuiteName, BrandingInfo.AppName);

Check notice

Code scanning / CodeQL

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments Note

Call to 'System.IO.Path.Combine' may silently drop its earlier arguments.

Copilot Autofix

AI 4 days ago

In general, to avoid Path.Combine silently dropping earlier arguments when later ones are absolute, use Path.Join when you are concatenating path segments and you do not want absolute later segments to override earlier ones. Path.Join will concatenate segments using the directory separator without giving precedence to any argument.

For this specific case, replace the use of Path.Combine for dataDir’s non-portable path with Path.Join. This preserves the intended behavior for normal relative SuiteName and AppName values while preventing them from unexpectedly overriding the base %AppData% path if they were ever set to absolute paths. No additional imports are required because System.IO.Path is already available in the base class library.

Concretely:

  • In src/PortPane/App.xaml.cs, around lines 55–59, change:
    • : Path.Combine(Environment.GetFolderPath(...), BrandingInfo.SuiteName, BrandingInfo.AppName);
    • to:
    • : Path.Join(Environment.GetFolderPath(...), BrandingInfo.SuiteName, BrandingInfo.AppName);
  • Leave the other Path.Combine calls unchanged, as they combine a base directory with known, simple file or subdirectory names and are not part of the flagged issue.
Suggested changeset 1
src/PortPane/App.xaml.cs

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/PortPane/App.xaml.cs b/src/PortPane/App.xaml.cs
--- a/src/PortPane/App.xaml.cs
+++ b/src/PortPane/App.xaml.cs
@@ -54,7 +54,7 @@
             bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));
             string dataDir = isPortable
                 ? Path.Combine(AppContext.BaseDirectory, "PortPane-Data")
-                : Path.Combine(
+                : Path.Join(
                     Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                     BrandingInfo.SuiteName, BrandingInfo.AppName);
             if (Directory.Exists(dataDir))
EOF
@@ -54,7 +54,7 @@
bool isPortable = File.Exists(Path.Combine(AppContext.BaseDirectory, "portable.txt"));
string dataDir = isPortable
? Path.Combine(AppContext.BaseDirectory, "PortPane-Data")
: Path.Combine(
: Path.Join(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
BrandingInfo.SuiteName, BrandingInfo.AppName);
if (Directory.Exists(dataDir))
Copilot is powered by AI and may make mistakes. Always verify output.
@Computer-Tsu Computer-Tsu merged commit 8da0710 into main Mar 31, 2026
10 of 11 checks passed
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.

2 participants