Skip to content

Commit f968f2a

Browse files
Merge pull request #183 from logicallysynced/chromatics-4.0.x
v4.0.154–4.0.156: Sentry triage + Dependabot rollup + Sharlayan bump
2 parents 95e76c5 + dac210c commit f968f2a

14 files changed

Lines changed: 399 additions & 237 deletions

CHANGELOG.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
All notable changes to Chromatics are documented here.
44

5-
## 4.0.28
5+
## 4.0.156
66

7-
_Release notes coming soon._
7+
- Fixed an issue where Chromatics could close itself with a crash dialog if a connected device (Hue, OpenRGB, etc.) had a brief network hiccup. These transient errors are now ignored.
8+
- Saving your layer setup is more reliable: if antivirus, OneDrive, or Dropbox briefly locks the file, Chromatics will retry rather than failing the save.
9+
- Quietened expected startup messages — if iCUE or OpenRGB isn't running, the app no longer treats it as a crash report.
10+
- Updated dependency libraries to latest version
11+
12+
## 4.0.153
13+
14+
Initial public release of Chromatics 4.0

Chromatics.DecoratorHarnessUI/Chromatics.DecoratorHarnessUI.csproj

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,11 @@
1616
</ItemGroup>
1717

1818
<ItemGroup>
19-
<PackageReference Include="Avalonia" Version="12.0.1" />
20-
<PackageReference Include="Avalonia.Desktop" Version="12.0.1" />
21-
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.1" />
22-
<PackageReference Include="Avalonia.Controls.ColorPicker" Version="12.0.1" />
19+
<PackageReference Include="Avalonia" Version="12.0.2" />
20+
<PackageReference Include="Avalonia.Desktop" Version="12.0.2" />
21+
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.2" />
22+
<PackageReference Include="Avalonia.Controls.ColorPicker" Version="12.0.2" />
2323
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
2424
</ItemGroup>
2525

26-
<ItemGroup>
27-
<Reference Include="Sharlayan">
28-
<HintPath>..\Build Dependencies\Sharlayan\Sharlayan.dll</HintPath>
29-
</Reference>
30-
</ItemGroup>
31-
32-
<ItemGroup>
33-
<None Include="..\Build Dependencies\Sharlayan\*.dll" Exclude="..\Build Dependencies\Sharlayan\Sharlayan.dll">
34-
<Link>%(Filename)%(Extension)</Link>
35-
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
36-
<Visible>false</Visible>
37-
</None>
38-
</ItemGroup>
39-
4026
</Project>

Chromatics.Tests/Chromatics.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14-
<PackageReference Include="coverlet.collector" Version="6.0.4" />
15-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
14+
<PackageReference Include="coverlet.collector" Version="10.0.0" />
15+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
1616
<PackageReference Include="xunit" Version="2.9.3" />
1717
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
1818
</ItemGroup>
@@ -26,7 +26,7 @@
2626
</ItemGroup>
2727

2828
<ItemGroup>
29-
<PackageReference Include="Sharlayan" Version="9.0.22-prerelease.33" />
29+
<PackageReference Include="Sharlayan" Version="9.0.24-prerelease.35" />
3030
</ItemGroup>
3131

3232
</Project>

Chromatics/Chromatics.csproj

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<OutputType>WinExe</OutputType>
55
<TargetFramework>net10.0-windows7.0</TargetFramework>
66
<StartupObject>Chromatics.Program</StartupObject>
7-
<Version>4.0.153.0</Version>
7+
<Version>4.0.156.0</Version>
88
<Authors>Danielle Thompson</Authors>
99
<ApplicationManifest>app.manifest</ApplicationManifest>
1010
<Copyright>logicallysynced 2026</Copyright>
@@ -48,12 +48,12 @@
4848
</ItemGroup>
4949

5050
<ItemGroup>
51-
<PackageReference Include="Avalonia" Version="12.0.1" />
52-
<PackageReference Include="Avalonia.Desktop" Version="12.0.1" />
53-
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.1" />
54-
<PackageReference Include="Avalonia.Fonts.Inter" Version="12.0.1" />
51+
<PackageReference Include="Avalonia" Version="12.0.2" />
52+
<PackageReference Include="Avalonia.Desktop" Version="12.0.2" />
53+
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.0.2" />
54+
<PackageReference Include="Avalonia.Fonts.Inter" Version="12.0.2" />
5555
<PackageReference Include="Avalonia.Controls.DataGrid" Version="12.0.0" />
56-
<PackageReference Include="Avalonia.Controls.ColorPicker" Version="12.0.1" />
56+
<PackageReference Include="Avalonia.Controls.ColorPicker" Version="12.0.2" />
5757
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
5858
<PackageReference Include="Velopack" Version="0.0.1298" />
5959
<PackageReference Include="Sentry" Version="6.4.1" />
@@ -83,7 +83,7 @@
8383
<PackageReference Include="RGB.NET.HID" Version="3.2.0" />
8484
<PackageReference Include="RGB.NET.Layout" Version="3.2.0" />
8585
<PackageReference Include="RGB.NET.Presets" Version="3.2.0" />
86-
<PackageReference Include="Sharlayan" Version="9.0.22-prerelease.33" />
86+
<PackageReference Include="Sharlayan" Version="9.0.24-prerelease.35" />
8787
</ItemGroup>
8888

8989
<ItemGroup>

Chromatics/Core/Logger.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,16 @@ public static void SetLogDirectory(string directory)
3434
_logDirectory = directory;
3535
}
3636

37-
public static void WriteConsole(LoggerTypes type, string message)
37+
public static void WriteConsole(LoggerTypes type, string message, bool forwardToSentry = true)
3838
{
3939
var color = (Color)EnumExtensions.GetAttribute<DefaultValueAttribute>(type).Value;
4040
var timestamp = DateTime.Now.ToString("MM-dd HH:mm:ss");
4141
var args = new OnConsoleLoggedEventArgs($"[{timestamp}] {message}", color);
4242

4343
AppendToVerboseLog($"[{timestamp}] [{type}] {message}");
4444

45-
ForwardToSentry(type, message);
45+
if (forwardToSentry)
46+
ForwardToSentry(type, message);
4647

4748
lock (_gate)
4849
{

Chromatics/Core/RGBController.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ public static void Setup()
9999
var appSettings = AppSettings.GetSettings();
100100

101101
//Setup Exception Events
102-
surfaceExceptionEventHandler = args_ => Logger.WriteConsole(Enums.LoggerTypes.Error, $"Device Error: {args_.Exception.Message}");
103-
deviceExceptionEventHandler = (sender, e) => Logger.WriteConsole(Enums.LoggerTypes.Error, $"Device Error: {e.Exception.Message}");
102+
surfaceExceptionEventHandler = args_ => Logger.WriteConsole(Enums.LoggerTypes.Error, $"Device Error: {args_.Exception.Message}", forwardToSentry: false);
103+
deviceExceptionEventHandler = (sender, e) => Logger.WriteConsole(Enums.LoggerTypes.Error, $"Device Error: {e.Exception.Message}", forwardToSentry: false);
104104

105105
surface.Exception += surfaceExceptionEventHandler;
106106

Chromatics/Core/SentryService.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Sentry.Profiling;
55
using System;
66
using System.Diagnostics;
7+
using System.Linq;
78
using System.Reflection;
89

910
namespace Chromatics.Core
@@ -188,6 +189,18 @@ public static void Initialize()
188189
// drops everything else when consent is off.
189190
o.SetBeforeSend((evt, _) =>
190191
{
192+
// Drop unobserved-task SocketException noise (Hue/OpenRGB
193+
// shutdown, transient network resets) before consent gating.
194+
// These are auto-captured by Sentry's UnobservedTaskException
195+
// integration and would otherwise spam the Issues tab; our
196+
// own UnobservedTaskExceptionHandler already filters them
197+
// from the crash flow on the same criteria.
198+
if (evt.Exception != null && IsBenignBackgroundException(evt.Exception))
199+
{
200+
Logger.WriteVerbose($"[Sentry] BeforeSend dropped event {evt.EventId} — benign background exception ({evt.Exception.GetType().Name})");
201+
return null;
202+
}
203+
191204
var s = _settings;
192205
bool consent = s == null || s.enableCrashReports;
193206
bool isCrash = evt.Exception?.Data.Contains("Chromatics.HandledByCrashDialog") == true
@@ -571,6 +584,39 @@ public static void Shutdown()
571584
_sdkHandle?.Dispose();
572585
_initialized = false;
573586
}
587+
588+
// Mirror of Program.IsBenignBackgroundException — kept private to
589+
// SentryService so the BeforeSend filter doesn't take a hard dep on
590+
// Program. Both must agree on what's "benign" or events get dropped
591+
// here while the crash flow still kills the app, or vice versa.
592+
private static bool IsBenignBackgroundException(Exception ex)
593+
{
594+
if (ex is null) return false;
595+
if (ex is AggregateException agg)
596+
{
597+
var flat = agg.Flatten();
598+
return flat.InnerExceptions.Count > 0 && flat.InnerExceptions.All(IsBenignBackgroundException);
599+
}
600+
return ex switch
601+
{
602+
System.Net.Sockets.SocketException se => IsBenignSocketError(se.SocketErrorCode),
603+
System.IO.IOException io => io.InnerException is System.Net.Sockets.SocketException ise && IsBenignSocketError(ise.SocketErrorCode),
604+
ObjectDisposedException => true,
605+
OperationCanceledException => true,
606+
_ => false,
607+
};
608+
}
609+
610+
private static bool IsBenignSocketError(System.Net.Sockets.SocketError code) => code switch
611+
{
612+
System.Net.Sockets.SocketError.OperationAborted => true,
613+
System.Net.Sockets.SocketError.ConnectionReset => true,
614+
System.Net.Sockets.SocketError.ConnectionAborted => true,
615+
System.Net.Sockets.SocketError.Interrupted => true,
616+
System.Net.Sockets.SocketError.Shutdown => true,
617+
System.Net.Sockets.SocketError.NetworkReset => true,
618+
_ => false,
619+
};
574620
}
575621

576622
/// <summary>

Chromatics/Helpers/FileOperationsHelper.cs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Net.Http;
1818
using System.Reflection;
1919
using System.Text;
20+
using System.Threading;
2021
using System.Threading.Tasks;
2122
using System.Xml.Serialization;
2223
using static System.Net.WebRequestMethods;
@@ -313,10 +314,37 @@ private static void WriteJsonAtomic(string path, object payload)
313314
sw.Flush();
314315
}
315316

316-
if (File.Exists(path))
317-
File.Replace(tmp, path, null);
318-
else
319-
File.Move(tmp, path);
317+
// File.Replace can throw "Unable to remove the file to be
318+
// replaced" when AV / OneDrive / Dropbox is briefly holding
319+
// the destination open. Retry with backoff before giving up.
320+
ReplaceWithRetry(tmp, path);
321+
}
322+
}
323+
324+
private static void ReplaceWithRetry(string tmp, string path)
325+
{
326+
const int maxAttempts = 5;
327+
int delayMs = 50;
328+
for (int attempt = 1; ; attempt++)
329+
{
330+
try
331+
{
332+
if (File.Exists(path))
333+
File.Replace(tmp, path, null);
334+
else
335+
File.Move(tmp, path);
336+
return;
337+
}
338+
catch (IOException) when (attempt < maxAttempts)
339+
{
340+
Thread.Sleep(delayMs);
341+
delayMs *= 2;
342+
}
343+
catch (UnauthorizedAccessException) when (attempt < maxAttempts)
344+
{
345+
Thread.Sleep(delayMs);
346+
delayMs *= 2;
347+
}
320348
}
321349
}
322350

Chromatics/Program.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,48 @@ private static void UnobservedTaskExceptionHandler(object sender, UnobservedTask
207207
// UnobservedTaskExceptionIntegration captured the event but our
208208
// code did nothing to surface or terminate.
209209
e.SetObserved();
210+
211+
// Exception to that rule: socket aborts during shutdown (Hue
212+
// entertainment client, OpenRGB TCP client, AutoUpdater HTTPS)
213+
// surface as unobserved SocketException with WSAEINTR /
214+
// WSAECONNABORTED / OperationAborted. They're cosmetic — the
215+
// dependent task is already on its way out. Crashing the whole
216+
// app for them spams users with the crash dialog.
217+
if (IsBenignBackgroundException(e.Exception))
218+
return;
219+
210220
CrashHandler.HandleCrash(e.Exception);
211221
}
212222

223+
private static bool IsBenignBackgroundException(Exception ex)
224+
{
225+
if (ex is null) return false;
226+
if (ex is AggregateException agg)
227+
{
228+
var flat = agg.Flatten();
229+
return flat.InnerExceptions.Count > 0 && flat.InnerExceptions.All(IsBenignBackgroundException);
230+
}
231+
return ex switch
232+
{
233+
System.Net.Sockets.SocketException se => IsBenignSocketError(se.SocketErrorCode),
234+
System.IO.IOException io => io.InnerException is System.Net.Sockets.SocketException ise && IsBenignSocketError(ise.SocketErrorCode),
235+
ObjectDisposedException => true,
236+
OperationCanceledException => true,
237+
_ => false,
238+
};
239+
}
240+
241+
private static bool IsBenignSocketError(System.Net.Sockets.SocketError code) => code switch
242+
{
243+
System.Net.Sockets.SocketError.OperationAborted => true,
244+
System.Net.Sockets.SocketError.ConnectionReset => true,
245+
System.Net.Sockets.SocketError.ConnectionAborted => true,
246+
System.Net.Sockets.SocketError.Interrupted => true,
247+
System.Net.Sockets.SocketError.Shutdown => true,
248+
System.Net.Sockets.SocketError.NetworkReset => true,
249+
_ => false,
250+
};
251+
213252
private static bool ThereCanOnlyBeOne()
214253
{
215254
// Try to claim the single-instance mutex. createdNew is true iff

Chromatics/obj/Chromatics.csproj.nuget.dgspec.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
},
66
"projects": {
77
"D:\\Git Projects\\roxaskeyheart\\Chromatics Workbench\\Chromatics\\Chromatics\\Chromatics.csproj": {
8-
"version": "4.0.153",
8+
"version": "4.0.156",
99
"restore": {
1010
"projectUniqueName": "D:\\Git Projects\\roxaskeyheart\\Chromatics Workbench\\Chromatics\\Chromatics\\Chromatics.csproj",
1111
"projectName": "Chromatics",
@@ -55,27 +55,27 @@
5555
"dependencies": {
5656
"Avalonia": {
5757
"target": "Package",
58-
"version": "[12.0.1, )"
58+
"version": "[12.0.2, )"
5959
},
6060
"Avalonia.Controls.ColorPicker": {
6161
"target": "Package",
62-
"version": "[12.0.1, )"
62+
"version": "[12.0.2, )"
6363
},
6464
"Avalonia.Controls.DataGrid": {
6565
"target": "Package",
6666
"version": "[12.0.0, )"
6767
},
6868
"Avalonia.Desktop": {
6969
"target": "Package",
70-
"version": "[12.0.1, )"
70+
"version": "[12.0.2, )"
7171
},
7272
"Avalonia.Fonts.Inter": {
7373
"target": "Package",
74-
"version": "[12.0.1, )"
74+
"version": "[12.0.2, )"
7575
},
7676
"Avalonia.Themes.Fluent": {
7777
"target": "Package",
78-
"version": "[12.0.1, )"
78+
"version": "[12.0.2, )"
7979
},
8080
"CommunityToolkit.Mvvm": {
8181
"target": "Package",
@@ -175,7 +175,7 @@
175175
},
176176
"Sharlayan": {
177177
"target": "Package",
178-
"version": "[9.0.22-prerelease.33, )"
178+
"version": "[9.0.24-prerelease.35, )"
179179
},
180180
"System.Drawing.Common": {
181181
"target": "Package",
@@ -202,7 +202,7 @@
202202
"privateAssets": "all"
203203
}
204204
},
205-
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.202/PortableRuntimeIdentifierGraph.json",
205+
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.203/PortableRuntimeIdentifierGraph.json",
206206
"packagesToPrune": {
207207
"Microsoft.CSharp": "(,4.7.32767]",
208208
"Microsoft.VisualBasic": "(,10.4.32767]",

0 commit comments

Comments
 (0)