Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .gitignore
Binary file not shown.
43 changes: 43 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,49 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.49.0] - 2026-05-13

### Fixed
- **Binding: BatteryInfo** — add NotifyPropertyChangedFor on DesignCapacityMWh,
FullChargeCapacityMWh, EstimatedRuntimeMinutes for computed properties
HealthPercent, WearPercent, RuntimeDisplay (BIND-001).
- **Binding: DiskHealthReport** — add NotifyPropertyChangedFor on HealthStatus,
TemperatureC, WearPercent, PowerOnHours, ReadErrors, WriteErrors for 6+
computed properties (BIND-002).
- **Binding: FriendlyEventEntry** — add NotifyPropertyChangedFor on Timestamp
and Severity for RelativeTime, FullTimestamp, SeverityIcon, SeverityColor
(BIND-003).
- **Binding: PerformanceProfile** — add NotifyPropertyChangedFor on
ActivePlanName and ActivePlanGuid for ProfileSummary (BIND-004).
- **Binding: ProcessEntry** — add NotifyPropertyChangedFor on MemoryBytes for
MemoryDisplay (BIND-005).
- **Binding: DiskUsageEntry** — add NotifyPropertyChangedFor on SizeBytes for
SizeDisplay (BIND-006).
- **Binding: InstalledApp** — add NotifyPropertyChangedFor on SizeBytes for
SizeDisplay (BIND-007).
- **Memory: DeepCleanupViewModel** — replace anonymous PropertyChanged lambda
with named handler, unsubscribe on rescan and Dispose (MEM-006).
- **Memory: ShortcutCleanerViewModel** — replace anonymous PropertyChanged
lambda with named handler, unsubscribe on rescan and Dispose (MEM-007).
- **Bug: MemoryTestService** — set ReverseDirection=true on EventLogQuery so
the cutoff break works correctly with newest-first ordering (BUG-002).
- **Bug: PerformanceService** — fix CreateRestorePointAsync by embedding
description directly in script instead of using AddParameter which doesn't
create script-scope variables (BUG-003).
- **Bug: SpeedTestView/CleanupView/DeepCleanupView/NetworkRepairView/
SystemHealthView/TracerouteView/AboutView** — replace FlexVis converter
misuse on IsEnabled with dedicated BoolInverterConverter (BUG-004, BUG-005).
- **Security: ServiceManagerService** — replace weak quote-only validation with
strict allowlist regex for sc.exe service name arguments (SEC-006).
- **Performance: LogsViewModel** — use CollectionView.Count directly instead of
iterating entire filtered view via Cast/Count (PERF-002).
- **Performance: NetworkSharedState** — simplify buffer trimming to remove from
front sequentially (PERF-003).
- **Performance: MarkdownTextBlock** — use static compiled Regex instead of
creating new state machine on every parse call (PERF-004).
- **Performance: DiskAnalyzerService** — use StringComparison.OrdinalIgnoreCase
instead of allocating ToLowerInvariant copy on every path (PERF-006).

## [0.48.0] - 2026-05-13

### Fixed
Expand Down
1 change: 1 addition & 0 deletions SysManager/SysManager/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<!-- ============================================================ -->
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<h:FlexibleBoolToVisibilityConverter x:Key="FlexVis"/>
<h:BoolInverterConverter x:Key="BoolInvert"/>
<h:HexToBrushConverter x:Key="HexBrush"/>
<h:OutputKindToBrushConverter x:Key="KindBrush"/>
<h:ProcessStatusToBrushConverter x:Key="StatusBrush"/>
Expand Down
6 changes: 5 additions & 1 deletion SysManager/SysManager/Helpers/MarkdownTextBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,18 @@ private static void OnMarkdownChanged(DependencyObject d, DependencyPropertyChan
}
}

// PERF-004: Pre-compiled regex avoids creating a new state machine on every call.
private static readonly Regex InlineFormattingRegex = new(
@"\*\*(.+?)\*\*|`([^`]+)`", RegexOptions.Compiled);

/// <summary>
/// Parses a single line for **bold** and `code` inline formatting
/// and appends the resulting Inlines to the TextBlock.
/// </summary>
private static void AddFormattedText(TextBlock tb, string text)
{
// Merge bold and code patterns, process left-to-right
var combined = Regex.Matches(text, @"\*\*(.+?)\*\*|`([^`]+)`");
var combined = InlineFormattingRegex.Matches(text);
var pos = 0;

foreach (Match m in combined)
Expand Down
14 changes: 14 additions & 0 deletions SysManager/SysManager/Helpers/OutputKindToBrushConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@
=> throw new NotSupportedException();
}

/// <summary>
/// Inverts a boolean value. Use for IsEnabled bindings where the source
/// property indicates a "busy" state and the target should be disabled.
/// </summary>
[ValueConversion(typeof(bool), typeof(bool))]
public sealed class BoolInverterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> value is bool b ? !b : true;

Check notice

Code scanning / CodeQL

Unnecessarily complex Boolean expression Note

The expression 'A ? B : true' can be simplified to '!A || B'.

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> value is bool b ? !b : true;

Check notice

Code scanning / CodeQL

Unnecessarily complex Boolean expression Note

The expression 'A ? B : true' can be simplified to '!A || B'.
}

public class BoolToElevationBadgeBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
Expand Down
15 changes: 12 additions & 3 deletions SysManager/SysManager/Models/BatteryInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@ public partial class BatteryInfo : ObservableObject
[ObservableProperty] private string _name = "";
[ObservableProperty] private string _status = ""; // Charging / Discharging / Full / AC (no battery)
[ObservableProperty] private int _chargePercent; // 0-100
[ObservableProperty] private uint _designCapacityMWh; // milliwatt-hours
[ObservableProperty] private uint _fullChargeCapacityMWh;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HealthPercent))]
[NotifyPropertyChangedFor(nameof(WearPercent))]
private uint _designCapacityMWh; // milliwatt-hours

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HealthPercent))]
[NotifyPropertyChangedFor(nameof(WearPercent))]
private uint _fullChargeCapacityMWh;
[ObservableProperty] private int _cycleCount;
[ObservableProperty] private int _estimatedRuntimeMinutes; // -1 = unlimited (AC)
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(RuntimeDisplay))]
private int _estimatedRuntimeMinutes; // -1 = unlimited (AC)
[ObservableProperty] private string _chemistry = ""; // LiIon, NiMH, etc.
[ObservableProperty] private string _manufacturer = "";

Expand Down
38 changes: 32 additions & 6 deletions SysManager/SysManager/Models/DiskHealthReport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,39 @@ public partial class DiskHealthReport : ObservableObject
[ObservableProperty] private string _mediaType = ""; // HDD / SSD / NVMe
[ObservableProperty] private string _busType = "";
[ObservableProperty] private double _sizeGB;
[ObservableProperty] private string _healthStatus = ""; // Healthy / Warning / Unhealthy
[ObservableProperty] private double? _temperatureC;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HealthPercent))]
[NotifyPropertyChangedFor(nameof(HealthPercentColorHex))]
private string _healthStatus = ""; // Healthy / Warning / Unhealthy
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HealthPercent))]
[NotifyPropertyChangedFor(nameof(HealthPercentColorHex))]
[NotifyPropertyChangedFor(nameof(TemperatureColorHex))]
[NotifyPropertyChangedFor(nameof(TemperatureGauge))]
private double? _temperatureC;

[ObservableProperty] private double? _temperatureMaxC;
[ObservableProperty] private int? _wearPercent; // 0 = new, 100 = worn out (SSD only)
[ObservableProperty] private long? _powerOnHours;
[ObservableProperty] private long? _readErrors;
[ObservableProperty] private long? _writeErrors;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HealthPercent))]
[NotifyPropertyChangedFor(nameof(HealthPercentColorHex))]
[NotifyPropertyChangedFor(nameof(WearGauge))]
[NotifyPropertyChangedFor(nameof(WearColorHex))]
private int? _wearPercent; // 0 = new, 100 = worn out (SSD only)

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(PowerOnDisplay))]
private long? _powerOnHours;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HealthPercent))]
[NotifyPropertyChangedFor(nameof(HealthPercentColorHex))]
private long? _readErrors;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(HealthPercent))]
[NotifyPropertyChangedFor(nameof(HealthPercentColorHex))]
private long? _writeErrors;
[ObservableProperty] private long? _startStopCount;
[ObservableProperty] private string _verdict = ""; // plain-English summary
[ObservableProperty] private string _verdictColorHex = "#9AA0A6";
Expand Down
4 changes: 3 additions & 1 deletion SysManager/SysManager/Models/DiskUsageEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ public partial class DiskUsageEntry : ObservableObject
{
[ObservableProperty] private string _name = "";
[ObservableProperty] private string _fullPath = "";
[ObservableProperty] private long _sizeBytes;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(SizeDisplay))]
private long _sizeBytes;
[ObservableProperty] private double _percentage;
[ObservableProperty] private int _fileCount;
[ObservableProperty] private int _folderCount;
Expand Down
10 changes: 8 additions & 2 deletions SysManager/SysManager/Models/FriendlyEventEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ namespace SysManager.Models;
/// </summary>
public partial class FriendlyEventEntry : ObservableObject
{
[ObservableProperty] private DateTime _timestamp;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(RelativeTime))]
[NotifyPropertyChangedFor(nameof(FullTimestamp))]
private DateTime _timestamp;
[ObservableProperty] private string _logName = ""; // System / Application / Security / Setup
[ObservableProperty] private string _providerName = ""; // source
[ObservableProperty] private int _eventId;
[ObservableProperty] private EventSeverity _severity;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(SeverityIcon))]
[NotifyPropertyChangedFor(nameof(SeverityColor))]
private EventSeverity _severity;
[ObservableProperty] private string _severityLabel = "";
[ObservableProperty] private string _message = ""; // first line / summary
[ObservableProperty] private string _fullMessage = ""; // full rendered text
Expand Down
4 changes: 3 additions & 1 deletion SysManager/SysManager/Models/InstalledApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ public partial class InstalledApp : ObservableObject
[ObservableProperty] private string _version = "";
[ObservableProperty] private string _source = "";
[ObservableProperty] private string _status = "";
[ObservableProperty] private long _sizeBytes;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(SizeDisplay))]
private long _sizeBytes;
[ObservableProperty] private string _publisher = "";
[ObservableProperty] private ImageSource? _icon;
[ObservableProperty] private string _uninstallString = "";
Expand Down
9 changes: 7 additions & 2 deletions SysManager/SysManager/Models/PerformanceProfile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ namespace SysManager.Models;
public partial class PerformanceProfile : ObservableObject
{
// ── Power plan ──
[ObservableProperty] private string _activePlanName = "";
[ObservableProperty] private string _activePlanGuid = "";
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ProfileSummary))]
private string _activePlanName = "";

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ProfileSummary))]
private string _activePlanGuid = "";

// ── Visual effects ──
[ObservableProperty] private bool _visualEffectsReduced;
Expand Down
4 changes: 3 additions & 1 deletion SysManager/SysManager/Models/ProcessEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ public partial class ProcessEntry : ObservableObject
[ObservableProperty] private string _name = "";
[ObservableProperty] private string _description = "";
[ObservableProperty] private double _cpuPercent;
[ObservableProperty] private long _memoryBytes;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(MemoryDisplay))]
private long _memoryBytes;
[ObservableProperty] private string _status = "";
[ObservableProperty] private string _userName = "";
[ObservableProperty] private DateTime _startTime;
Expand Down
4 changes: 2 additions & 2 deletions SysManager/SysManager/Services/DiskAnalyzerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ private static (long size, int files, int folders) MeasureFolder(string path, Ca

private static bool ShouldSkip(string path)
{
var lower = path.ToLowerInvariant();
return SkipSegments.Any(seg => lower.Contains(seg));
// PERF-006: Use OrdinalIgnoreCase instead of allocating a lowercase copy.
return SkipSegments.Any(seg => path.Contains(seg, StringComparison.OrdinalIgnoreCase));
}
}
3 changes: 2 additions & 1 deletion SysManager/SysManager/Services/MemoryTestService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public async Task<MemoryErrorSummary> CheckErrorLogsAsync(CancellationToken ct =
using var reader = new System.Diagnostics.Eventing.Reader.EventLogReader(
new System.Diagnostics.Eventing.Reader.EventLogQuery("System",
System.Diagnostics.Eventing.Reader.PathType.LogName,
"*[System[Provider[@Name='Microsoft-Windows-WHEA-Logger' or @Name='Microsoft-Windows-MemoryDiagnostics-Results']]]"));
"*[System[Provider[@Name='Microsoft-Windows-WHEA-Logger' or @Name='Microsoft-Windows-MemoryDiagnostics-Results']]]")
{ ReverseDirection = true });

var cutoff = DateTime.Now.AddDays(-30);
System.Diagnostics.Eventing.Reader.EventRecord? rec;
Expand Down
8 changes: 5 additions & 3 deletions SysManager/SysManager/Services/PerformanceService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -511,9 +511,11 @@ await _ps.RunProcessAsync("powercfg.exe",
/// </summary>
public async Task<bool> CreateRestorePointAsync(string description, CancellationToken ct = default)
{
var script = "Checkpoint-Computer -Description $desc -RestorePointType 'MODIFY_SETTINGS'";
var parameters = new Dictionary<string, object?> { ["desc"] = description };
var results = await _ps.RunAsync(script, parameters, ct).ConfigureAwait(false);
// BUG-003: Embed description directly in the script with single-quote
// escaping. AddParameter doesn't create script-scope variables.
var safeDesc = (description ?? "SysManager Restore Point").Replace("'", "''");
var script = $"Checkpoint-Computer -Description '{safeDesc}' -RestorePointType 'MODIFY_SETTINGS'";
var results = await _ps.RunAsync(script, null, ct).ConfigureAwait(false);
return results != null;
}

Expand Down
7 changes: 5 additions & 2 deletions SysManager/SysManager/Services/ServiceManagerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ public static void StopService(string serviceName)
/// <summary>Change the startup type of a service via sc.exe. Requires admin.</summary>
public static async Task SetStartupTypeAsync(string serviceName, string startType, PowerShellRunner ps, CancellationToken ct = default)
{
// Validate inputs to prevent command injection via sc.exe arguments
if (serviceName.Contains('"') || serviceName.Contains('\0'))
// SEC-006: Strict allowlist for service names — alphanumeric, spaces,
// hyphens, underscores, dots, and dollar signs only (covers all valid
// Windows service names including instance names like MSSQL$INSTANCE).
if (string.IsNullOrWhiteSpace(serviceName) ||
!System.Text.RegularExpressions.Regex.IsMatch(serviceName, @"^[\w\s\-.$]+$"))
throw new ArgumentException("Invalid service name.", nameof(serviceName));
Comment on lines +126 to 131

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 | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In .NET regular expressions, does \smatch tabs/newlines (not just spaces), and is[A-Za-z0-9_.-$ ]+ a stricter way to allow only visible service-name characters plus spaces?

💡 Result:

Yes and (mostly) yes. 1) Does \s match tabs/newlines in.NET regex? In.NET regular expressions, \s matches any whitespace character, including tab (\t), newline (\n), carriage return (\r), vertical tab (\v), and form feed (\f), plus other Unicode whitespace/separator characters (e.g., Unicode separator categories like Zs, Zl, Zp). [1][2] 2) Is [A-Za-z0-9_.\-$ ]+ a stricter way to allow only visible service-name characters plus spaces? It is stricter than using \s, because it only allows: - ASCII letters A-Z/a-z - digits 0-9 - underscore _ - dot ., and - hyphen - - dollar $ - literal space and disallows all other whitespace/control characters (so no tabs/newlines). Character class membership is defined by the characters you list inside [...]. [3] However, it is not “Unicode-aware”: it allows only ASCII letters/digits. It also allows any number of those characters via +, but it does not enforce additional constraints beyond character set (for example, it allows spaces anywhere, and it doesn’t prevent consecutive spaces, leading/trailing spaces, etc.). If your goal is “service-name characters” that are typically “visible” plus spaces, your pattern is a reasonable approach, but you may want to tighten whitespace placement depending on your requirements (e.g., avoid leading/trailing spaces or collapse multiple spaces).

Citations:


🌐 Web query:

Windows service names allowed characters rules Microsoft documentation

💡 Result:

Windows service names (ServiceName passed to CreateService/CreateServiceW, also the “service key name” used by the SCM) are limited to 256 characters, comparisons are case-insensitive, the SCM does not treat “/” or “\” as valid characters in the service name, and it preserves the case that was originally provided [1]. Notes/related clarifications: - The “DisplayName” (what users see in UI) is separate from “ServiceName”, also limited to 256 characters, and is case-insensitive for comparisons [1]. - While the Microsoft Learn page explicitly calls out that forward-slash (/) and backslash () are not valid service name characters, it does not document additional forbidden characters beyond these in the cited text; for safety and portability, avoid “/” and “\” and keep the name within 256 characters [1].

Citations:


🏁 Script executed:

rg -i "SEC-006\|service.*name\|alphanumeric" SysManager/ -A 3 -B 3

Repository: laurentiu021/SystemManager

Length of output: 52


🏁 Script executed:

rg "ServiceManagerService" --type cs -B 2 -A 2

Repository: laurentiu021/SystemManager

Length of output: 8674


🏁 Script executed:

rg -i "servicename\s*=" SysManager/ --type cs | head -20

Repository: laurentiu021/SystemManager

Length of output: 52


🏁 Script executed:

sed -n '110,145p' SysManager/SysManager/Services/ServiceManagerService.cs

Repository: laurentiu021/SystemManager

Length of output: 1824


🏁 Script executed:

ast-grep --pattern 'public.*($serviceName' SysManager/SysManager/Services/ServiceManagerService.cs

Repository: laurentiu021/SystemManager

Length of output: 52


Narrow \s to match the stated policy of spaces only.

The regex at line 130 uses \s, which matches tabs, newlines, and other Unicode whitespace characters beyond spaces. The SEC-006 comment explicitly requires "spaces only." Replace \s with a literal space character and use explicit ASCII character ranges to ensure predictable, locale-independent behavior:

🔒 Proposed fix
-        if (string.IsNullOrWhiteSpace(serviceName) ||
-            !System.Text.RegularExpressions.Regex.IsMatch(serviceName, @"^[\w\s\-.$]+$"))
+        if (string.IsNullOrWhiteSpace(serviceName) ||
+            !System.Text.RegularExpressions.Regex.IsMatch(
+                serviceName,
+                @"^[A-Za-z0-9_.\-$ ]+$",
+                System.Text.RegularExpressions.RegexOptions.CultureInvariant))
             throw new ArgumentException("Invalid service name.", nameof(serviceName));
🤖 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 `@SysManager/SysManager/Services/ServiceManagerService.cs` around lines 126 -
131, The regex used in Regex.IsMatch for validating serviceName currently uses
\s and \w (which are Unicode/broad) so it allows tabs/other whitespace and
non-ASCII word chars; update the validation in the serviceName check to use a
regex that only permits ASCII letters and digits and the explicit characters
space, underscore, hyphen, dot and dollar sign (i.e. replace \w with an explicit
ASCII range for letters/digits/underscore and replace \s with a literal space),
keeping the same start/end anchors and overall structure in the Regex.IsMatch
call.


var allowedTypes = new[] { "auto", "delayed-auto", "demand", "disabled" };
Expand Down
26 changes: 16 additions & 10 deletions SysManager/SysManager/ViewModels/DeepCleanupViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,21 @@ private void AddLocation(string label, string path)
partial void OnIsCleaningChanged(bool value) => IsBusy = IsScanning || IsCleaning || IsLargeScanning;
partial void OnIsLargeScanningChanged(bool value) => IsBusy = IsScanning || IsCleaning || IsLargeScanning;

private void OnCategoryPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(CleanupCategory.IsSelected))
{
OnPropertyChanged(nameof(TotalSelectedBytes));
OnPropertyChanged(nameof(TotalSelectedDisplay));
}
}

protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var c in Categories)
c.PropertyChanged -= OnCategoryPropertyChanged;
_scanCts?.Dispose();
_cleanCts?.Dispose();
_largeCts?.Dispose();
Expand Down Expand Up @@ -154,19 +165,14 @@ private async Task ScanAsync()

// Batch-update the observable collection to avoid per-item UI
// re-renders that can stall the dispatcher on large lists.
// MEM-006: Unsubscribe from old categories before clearing to prevent
// PropertyChanged lambda leaks across rescans.
foreach (var old in Categories)
old.PropertyChanged -= OnCategoryPropertyChanged;
Categories.Clear();
var catList = cats.ToList();
foreach (var c in catList)
{
c.PropertyChanged += (_, e) =>
{
if (e.PropertyName == nameof(CleanupCategory.IsSelected))
{
OnPropertyChanged(nameof(TotalSelectedBytes));
OnPropertyChanged(nameof(TotalSelectedDisplay));
}
};
}
c.PropertyChanged += OnCategoryPropertyChanged;
foreach (var c in catList) Categories.Add(c);
var total = cats.Sum(c => c.TotalSizeBytes);
ScanSummary = $"Found {CleanupCategory.HumanSize(total)} across {cats.Count} categories. Untick anything you want to keep.";
Expand Down
4 changes: 3 additions & 1 deletion SysManager/SysManager/ViewModels/LogsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@

private void UpdateVisibleCount()
{
VisibleCount = EntriesView.Cast<object>().Count();
// PERF-002: Use CollectionView.Count directly instead of iterating
// the entire filtered collection via Cast<object>().Count().
VisibleCount = ((CollectionView)EntriesView).Count;

Check warning

Code scanning / CodeQL

Cast from abstract to concrete collection Warning

Questionable cast from abstract 'ICollectionView' to concrete implementation 'CollectionView'.
HasNoResults = Entries.Count > 0 && VisibleCount == 0;
}

Expand Down
11 changes: 5 additions & 6 deletions SysManager/SysManager/ViewModels/NetworkSharedState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -425,12 +425,11 @@
while (removeCount < buffer.Count && buffer[removeCount].DateTime < cutoff)
removeCount++;

// Batch removal from end-to-start avoids O(n²) shifting
if (removeCount > 0)
{
for (int i = removeCount - 1; i >= 0; i--)
buffer.RemoveAt(i);
}
// PERF-003: Remove stale items from the front. Removing from index 0
// repeatedly is O(n*removeCount) but unavoidable with ObservableCollection.
// We remove forward (always index 0) which is simpler and equivalent.
for (int i = 0; i < removeCount; i++)
buffer.RemoveAt(0);
}

internal void TrimAllBuffers()
Expand All @@ -451,7 +450,7 @@
Labeler = v => new DateTime((long)v).ToString("HH:mm:ss"),
TextSize = 12,
NamePaint = new SolidColorPaint(SKColor.Parse("A3ADBF")),
LabelsPaint = new SolidColorPaint(SKColor.Parse("E6E9EE")) { FontFamily = "Segoe UI" },

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 453 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'
SeparatorsPaint = new SolidColorPaint(SKColor.Parse("2A3244").WithAlpha(80))
};

Expand All @@ -460,8 +459,8 @@
Name = name,
MinLimit = 0,
TextSize = 13,
NamePaint = new SolidColorPaint(SKColor.Parse("E6E9EE")) { FontFamily = "Segoe UI" },

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 462 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'
LabelsPaint = new SolidColorPaint(SKColor.Parse("E6E9EE")) { FontFamily = "Segoe UI" },

Check warning on line 463 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 463 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 463 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 463 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 463 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 463 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'
SeparatorsPaint = new SolidColorPaint(SKColor.Parse("2A3244").WithAlpha(80)) { StrokeThickness = 1 },
Labeler = v => $"{v:F0} ms",
NameTextSize = 14,
Expand All @@ -475,7 +474,7 @@
MinStep = 1,
TextSize = 12,
NamePaint = new SolidColorPaint(SKColor.Parse("A3ADBF")),
LabelsPaint = new SolidColorPaint(SKColor.Parse("E6E9EE")) { FontFamily = "Segoe UI" },

Check warning on line 477 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 477 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 477 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 477 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / Build & unit tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 477 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'

Check warning on line 477 in SysManager/SysManager/ViewModels/NetworkSharedState.cs

View workflow job for this annotation

GitHub Actions / UI automation tests

'SkiaPaint.FontFamily' is obsolete: 'Use the SKTypeface property and assign it to SKTypeface.FromFamilyName(fontFamily, fontStyle)'
SeparatorsPaint = new SolidColorPaint(SKColor.Parse("2A3244").WithAlpha(80))
};

Expand Down
Loading
Loading