v0.3.0: CI dotnet 8.0.424 pinned with isolated install
CI / build (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / release (push) Canceled after 0s

- Pin .NET SDK to 8.0.424 in all CI jobs to avoid floating channel drift

- Install SDK to runner temp via DOTNET_INSTALL_DIR to bypass

  C:\\Program Files\\dotnet overwrite/extract conflicts on Gitea Windows runners

- Simplify release job to dotnet publish + upload-artifact

- Exclude memory-bank/ via .gitignore (internal dev memory stays local)
This commit is contained in:
fristy committed 2026-09-05 00:22:09 +03:00
commit acc34d9543
18 files changed
+4122

No files matched your search

@@ -0,0 +1,140 @@
using System.IO;
using System.Text.Json;
using RgbLedStripController.Core;
using MediaColor = System.Windows.Media.Color;
namespace RgbLedStripController.Services;
public sealed class SettingsStore
{
private readonly string _settingsPath;
private readonly SemaphoreSlim _writeLock = new(1, 1);
private readonly JsonSerializerOptions _jsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
public SettingsStore()
{
var settingsDirectory = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"RgbLedStripController");
_settingsPath = Path.Combine(settingsDirectory, "settings.json");
}
public async Task<SettingsSnapshot> LoadAsync(CancellationToken cancellationToken = default)
{
if (!File.Exists(_settingsPath))
{
return CreateDefaultSnapshot();
}
try
{
await using var stream = File.OpenRead(_settingsPath);
var stored = await JsonSerializer.DeserializeAsync<StoredSettings>(stream, _jsonOptions, cancellationToken);
return new SettingsSnapshot(
stored?.KnownDevices ?? [],
new Dictionary<string, string>(stored?.DeviceAliases ?? new Dictionary<string, string>(), StringComparer.OrdinalIgnoreCase),
ParseColor(stored?.SelectedColorHex),
stored?.Brightness ?? 100,
stored?.EffectSpeed ?? 50,
stored?.CurrentMode ?? LightingMode.Rgb,
stored?.GrayscaleLevel ?? 100,
stored?.TemperatureLevel ?? 50,
stored?.SelectedEffectId ?? 0,
stored?.IsPoweredOn ?? false);
}
catch
{
return CreateDefaultSnapshot();
}
}
public async Task SaveAsync(SettingsSnapshot snapshot, CancellationToken cancellationToken = default)
{
await _writeLock.WaitAsync(cancellationToken);
try
{
var directory = Path.GetDirectoryName(_settingsPath);
if (!string.IsNullOrWhiteSpace(directory))
{
Directory.CreateDirectory(directory);
}
await using var stream = File.Create(_settingsPath);
var stored = new StoredSettings
{
KnownDevices = snapshot.KnownDevices.ToList(),
DeviceAliases = new Dictionary<string, string>(snapshot.DeviceAliases, StringComparer.OrdinalIgnoreCase),
SelectedColorHex = $"#{snapshot.SelectedColor.R:X2}{snapshot.SelectedColor.G:X2}{snapshot.SelectedColor.B:X2}",
Brightness = snapshot.Brightness,
EffectSpeed = snapshot.EffectSpeed,
CurrentMode = snapshot.CurrentMode,
GrayscaleLevel = snapshot.GrayscaleLevel,
TemperatureLevel = snapshot.TemperatureLevel,
SelectedEffectId = snapshot.SelectedEffectId,
IsPoweredOn = snapshot.IsPoweredOn
};
await JsonSerializer.SerializeAsync(stream, stored, _jsonOptions, cancellationToken);
}
finally
{
_writeLock.Release();
}
}
private static SettingsSnapshot CreateDefaultSnapshot()
=> new(
[],
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase),
MediaColor.FromRgb(255, 106, 78),
100,
50,
LightingMode.Rgb,
100,
50,
0,
false);
private static MediaColor ParseColor(string? value)
{
if (string.IsNullOrWhiteSpace(value) || value.Length != 7 || value[0] != '#')
{
return MediaColor.FromRgb(255, 106, 78);
}
return MediaColor.FromRgb(
Convert.ToByte(value.Substring(1, 2), 16),
Convert.ToByte(value.Substring(3, 2), 16),
Convert.ToByte(value.Substring(5, 2), 16));
}
private sealed class StoredSettings
{
public List<DeviceProfile> KnownDevices { get; set; } = [];
public Dictionary<string, string> DeviceAliases { get; set; } = new(StringComparer.OrdinalIgnoreCase);
public string? SelectedColorHex { get; set; }
public byte Brightness { get; set; }
public byte EffectSpeed { get; set; }
public LightingMode CurrentMode { get; set; }
public byte GrayscaleLevel { get; set; }
public byte TemperatureLevel { get; set; }
public byte SelectedEffectId { get; set; }
public bool IsPoweredOn { get; set; }
}
}