From 9bf05873ffb95d0e367361c746ceed288068a6aa Mon Sep 17 00:00:00 2001 From: Mark McDow <55188587+Computer-Tsu@users.noreply.github.com> Date: Sun, 20 Sep 2026 20:21:03 -0400 Subject: [PATCH 01/15] Fix compile errors from first CI run and add manifest and placeholder icon Moves DiagnosticResult into Models where TestResult can see it, adds the HelpTopic type and HelpContent articles the diagnosis engine referenced, and creates the main window code-behind the XAML declared. Adds the ViewModel members the XAML binds to, drops DI registrations for types that do not exist yet, and registers the settings service as an app resource for the window size bindings. Adds app.manifest (asInvoker, PerMonitorV2) and a placeholder icon so the csproj references resolve. --- App.xaml.cs | 6 ++--- Assets/rigcheck.ico | Bin 0 -> 5457 bytes CHANGELOG.md | 17 ++++++++++++ Models/Models.cs | 40 +++++++++++++++++++++------ Services/DiagnosisEngine.cs | 25 ----------------- Services/HelpContent.cs | 38 ++++++++++++++++++++++++++ ViewModels/MainViewModel.cs | 26 +++++++++++++++++- Views/MainWindow.xaml | 8 +++--- Views/MainWindow.xaml.cs | 52 ++++++++++++++++++++++++++++++++++++ app.manifest | 27 +++++++++++++++++++ 10 files changed, 197 insertions(+), 42 deletions(-) create mode 100644 Assets/rigcheck.ico create mode 100644 Services/HelpContent.cs create mode 100644 Views/MainWindow.xaml.cs create mode 100644 app.manifest diff --git a/App.xaml.cs b/App.xaml.cs index 9ff3eb2..07525ca 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -27,6 +27,8 @@ protected override async void OnStartup(StartupEventArgs e) await _host.StartAsync(); + Resources["Settings"] = _host.Services.GetRequiredService(); + var mainWindow = _host.Services.GetRequiredService(); mainWindow.Show(); @@ -59,7 +61,6 @@ private static void RegisterServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddTransient(); - services.AddTransient(); // Domain services.AddSingleton(); @@ -73,9 +74,6 @@ private static void RegisterServices(IServiceCollection services) services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); - services.AddSingleton(); - services.AddSingleton(); // Views services.AddTransient(); diff --git a/Assets/rigcheck.ico b/Assets/rigcheck.ico new file mode 100644 index 0000000000000000000000000000000000000000..5a9888cc5a7c846469b506a1aaf9358e9c60a6b1 GIT binary patch literal 5457 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 E0X7Peb^rhX literal 0 HcmV?d00001 diff --git a/CHANGELOG.md b/CHANGELOG.md index 140ee0e..76794ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to RigCheck are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version numbers: patch (x.x.1) for landed features and fixes, minor (x.1.0) for milestones. +## [Unreleased] + +### Added +- CI build workflow producing the self-contained exe and its SHA-256 as an artifact +- `app.manifest` — runs as standard user, per-monitor DPI aware +- Placeholder application icon (to be replaced with final artwork) +- Help topics shown alongside timeout and no-response failures +- Main window code-behind: window position persistence, console Enter/Up/Down key handling +- Raw console command history persists across sessions + +### Changed +- `DiagnosticResult` and `HelpTopic` moved to the Models namespace +- Zoom buttons bind to ViewModel commands instead of code-behind handlers + +### Removed +- Dependency-injection registrations for services and ViewModels not yet implemented + ## [0.6.0] - 2026-09-20 Project structure and licensing milestone. No functional changes to the application. diff --git a/Models/Models.cs b/Models/Models.cs index afc5069..6539570 100644 --- a/Models/Models.cs +++ b/Models/Models.cs @@ -155,11 +155,35 @@ public record ComPortInfo( public bool HasRadioHint => RadioFamily is not null; } -// ── Diagnostic result (referenced by TestResult) ────────────────────────── -// Defined in DiagnosisEngine.cs — declared here for the record type reference -// to avoid circular namespace issues. The concrete record is in Services. -// Re-exported here so Models namespace is self-contained for consumers. - -// NOTE: DiagnosticResult is defined in RigCheck.Services to keep service -// logic together. TestResult holds a nullable reference to it. -// No re-export needed — consumers reference RigCheck.Services directly. +// ── Diagnosis ───────────────────────────────────────────────────────────── + +/// +/// A short help article surfaced alongside a failed test. +/// +public record HelpTopic(string Title, string Body); + +/// +/// Plain-English explanation of a failed test: what happened, what to check, +/// and an optional command or link that may fix it. +/// +public record DiagnosticResult( + string Summary, + string[] Checks, + string? FixCommand, + string? LearnMoreUrl, + string? RawError = null, + HelpTopic[] HelpTopics = null!) +{ + public HelpTopic[] HelpTopics { get; init; } = HelpTopics ?? []; + + public static readonly DiagnosticResult Ok = new( + Summary: string.Empty, + Checks: [], + FixCommand: null, + LearnMoreUrl: null); + + public bool HasChecks => Checks.Length > 0; + public bool HasFixCommand => FixCommand is not null; + public bool HasHelpTopics => HelpTopics.Length > 0; + public bool IsOk => string.IsNullOrEmpty(Summary); +} diff --git a/Services/DiagnosisEngine.cs b/Services/DiagnosisEngine.cs index 14b32a5..77d8abd 100644 --- a/Services/DiagnosisEngine.cs +++ b/Services/DiagnosisEngine.cs @@ -137,28 +137,3 @@ public DiagnosticResult Diagnose(RigctlResult result, ConnectionConfig cfg) LearnMoreUrl: BrandingInfo.IssueUrl, RawError: rawMessage); } - -// ── Result type ─────────────────────────────────────────────────────────── - -public record DiagnosticResult( - string Summary, - string[] Checks, - string? FixCommand, - string? LearnMoreUrl, - string? RawError = null, - HelpTopic[] HelpTopics = null!) -{ - // Ensure HelpTopics is never null - public HelpTopic[] HelpTopics { get; init; } = HelpTopics ?? []; - - public static readonly DiagnosticResult Ok = new( - Summary: string.Empty, - Checks: [], - FixCommand: null, - LearnMoreUrl: null); - - public bool HasChecks => Checks.Length > 0; - public bool HasFixCommand => FixCommand is not null; - public bool HasHelpTopics => HelpTopics.Length > 0; - public bool IsOk => string.IsNullOrEmpty(Summary); -} diff --git a/Services/HelpContent.cs b/Services/HelpContent.cs new file mode 100644 index 0000000..bddc611 --- /dev/null +++ b/Services/HelpContent.cs @@ -0,0 +1,38 @@ +using RigCheck.Models; + +namespace RigCheck.Services; + +/// +/// Help articles shown alongside specific failure types, aimed at operators +/// who have never configured CAT control before. +/// +public static class HelpContent +{ + public static HelpTopic[] TopicsForError(RigctlError error) => error switch + { + RigctlError.Timeout => [EnableCat, BaudRateMatch], + RigctlError.NoResponse => [BaudRateMatch, CivAddress], + _ => [], + }; + + private static readonly HelpTopic EnableCat = new( + "Enabling CAT control on your radio", + "Most radios ship with CAT control disabled or set to a default speed. " + + "Look in the radio's menu for a setting named CAT, CI-V, RS-232, or PC control " + + "and make sure it is turned on. Icom radios call this CI-V; Yaesu and Kenwood " + + "call it CAT. The exact menu number is in your radio's manual under \"remote control\"."); + + private static readonly HelpTopic BaudRateMatch = new( + "Matching the baud rate", + "The baud rate set in RigCheck must match the rate set inside the radio. " + + "Common defaults: Icom IC-7300 and IC-705 use 19200 or 115200; Yaesu FT-991A and " + + "FT-DX10 use 38400; Kenwood TS-590SG uses 115200; Elecraft K3 uses 38400. " + + "If unsure, try 9600 first — nearly every radio supports it."); + + private static readonly HelpTopic CivAddress = new( + "Icom CI-V address", + "Icom radios each answer to a CI-V address, shown in the radio's menu as a hex " + + "number such as 94h. Hamlib uses the default address for the selected model. " + + "If the address has been changed in the radio, either reset it to the default " + + "or add the matching address in the advanced serial settings."); +} diff --git a/ViewModels/MainViewModel.cs b/ViewModels/MainViewModel.cs index 19e1bad..8414f24 100644 --- a/ViewModels/MainViewModel.cs +++ b/ViewModels/MainViewModel.cs @@ -28,11 +28,15 @@ public partial class MainViewModel : ObservableObject [ObservableProperty] private bool _isRunning; [ObservableProperty] private bool _rawConsoleVisible; + [ObservableProperty] private bool _chromeVisible; [ObservableProperty] private bool _alwaysOnTop; [ObservableProperty] private double _scaleFactor = 1.0; [ObservableProperty] private string _statusMessage = string.Empty; [ObservableProperty] private string _hamlibStatus = string.Empty; + public bool IsHamlibAvailable => _hamlib.IsAvailable; + public bool IsHamlibMissing => !_hamlib.IsAvailable; + // Title shown in window chrome public string WindowTitle => $"{BrandingInfo.FullName} {BrandingInfo.Version}"; @@ -140,10 +144,26 @@ private async Task ExportLogAsync() StatusMessage = "Log export failed — check the application log for details."; } + [RelayCommand] + private Task SendRawCommandAsync() => + RawConsole.SendCommandCommand.ExecuteAsync(Connection.BuildConfig()); + + [RelayCommand] + private void OpenHamlibDownload() => + System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo + { + FileName = BrandingInfo.HamlibDownloadUrl, + UseShellExecute = true, + }); + [RelayCommand] private void ToggleRawConsole() => RawConsoleVisible = !RawConsoleVisible; + [RelayCommand] + private void ToggleChrome() => + ChromeVisible = !ChromeVisible; + [RelayCommand] private void ScaleUp() => ScaleFactor = Math.Min(ScaleFactor + 0.1, 2.0); @@ -155,13 +175,16 @@ private void ToggleRawConsole() => // ── Window lifecycle ────────────────────────────────────────────────── - public void OnWindowClosing() + public void OnWindowClosing(double windowLeft, double windowTop) { _settings.Update(s => { s.RawConsoleOpen = RawConsoleVisible; s.ScaleFactor = ScaleFactor; s.AlwaysOnTop = AlwaysOnTop; + s.WindowLeft = windowLeft; + s.WindowTop = windowTop; + s.CommandHistory = RawConsole.GetHistory(); Connection.SaveTo(s); }); } @@ -174,6 +197,7 @@ private void LoadSettings() RawConsoleVisible = s.RawConsoleOpen; ScaleFactor = s.ScaleFactor; AlwaysOnTop = s.AlwaysOnTop; + RawConsole.LoadHistory(s.CommandHistory); Connection.LoadFrom(s); } diff --git a/Views/MainWindow.xaml b/Views/MainWindow.xaml index 8ec267a..7d9d5ef 100644 --- a/Views/MainWindow.xaml +++ b/Views/MainWindow.xaml @@ -38,11 +38,11 @@ Style="{StaticResource ChromeTitleStyle}" />