From 4a730e8fb9f92f25211dfa07536b6793e6de0248 Mon Sep 17 00:00:00 2001 From: Valentin Kindschi Date: Tue, 8 Sep 2026 11:26:06 +0200 Subject: [PATCH 1/8] Suppress VSTHRD010 project-wide (false-positive tainting) Logger.Output/Debug write via IVsOutputWindowPane.OutputStringThreadSafe, which is documented safe to call off the UI thread. The analyzer cannot express 'this specific call is verified safe' and instead taints every method in the call graph (all of SshTool/RemoteDebugger/LaunchBuilder's async SSH/deploy code) as UI-thread-only. Tried marshalling pane creation/Activate() through JoinableTaskFactory explicitly; the warning still propagated syntactically to every caller regardless of correct runtime behavior, so a scoped suppression is the accurate fix rather than churning ~15 call sites for no behavioral gain. --- src/.editorconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/.editorconfig b/src/.editorconfig index 1c08561..0248ba7 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -333,6 +333,14 @@ dotnet_diagnostic.CS0618.severity = error # CS1591: Missing XML comment for publicly visible type or member dotnet_diagnostic.CS1591.severity = silent +## VS Threading Analyzers +# VSTHRD010: Accessing X should only be done on the main thread. Suppressed project-wide: +# Logger.Output/Debug write via IVsOutputWindowPane.OutputStringThreadSafe, which is +# documented safe to call off the UI thread, but the analyzer taints every method in the +# call graph (SshTool/RemoteDebugger/LaunchBuilder's async SSH/deploy code) as if it were +# UI-thread-only, with no way to mark the actual call as verified safe. +dotnet_diagnostic.VSTHRD010.severity = none + ## StyleCop.Analyzers # SA1000: Keywords should be spaced correctly dotnet_diagnostic.SA1000.severity = error From c0e6ea4c5a2aa93a800e1e481429da1a5a8eaeb2 Mon Sep 17 00:00:00 2001 From: Valentin Kindschi Date: Tue, 8 Sep 2026 13:14:22 +0200 Subject: [PATCH 2/8] build: Upgraded target frameworks to net8.0 and net10.0 and updated NuGets --- .gitignore | 1 + nuget.config | 12 +++++++++ sandbox/{ConsoleNet6.sln => Console.sln} | 2 +- .../Console.csproj} | 3 +-- sandbox/{ConsoleNet6 => Console}/Program.cs | 0 sandbox/{GuiNet6.sln => Gui.sln} | 2 +- sandbox/{GuiNet6 => Gui}/App.axaml | 9 ++++--- sandbox/{GuiNet6 => Gui}/App.axaml.cs | 10 ++++---- .../{GuiNet6 => Gui}/Assets/avalonia-logo.ico | Bin sandbox/Gui/Gui.csproj | 24 ++++++++++++++++++ sandbox/{GuiNet6 => Gui}/Program.cs | 5 ++-- .../Properties/launchSettings.json | 2 +- sandbox/{GuiNet6 => Gui}/RegionNames.cs | 2 +- .../ViewModels/DashboardViewModel.cs | 2 +- .../ViewModels/SettingsViewModel.cs | 2 +- .../ViewModels/ShellWindowViewModel.cs | 2 +- .../ViewModels/SidebarViewModel.cs | 6 ++--- .../ViewModels/ViewModelBase.cs | 6 ++--- .../Views/DashboardView.axaml | 2 +- .../Views/DashboardView.axaml.cs | 2 +- .../{GuiNet6 => Gui}/Views/SettingsView.axaml | 2 +- .../Views/SettingsView.axaml.cs | 2 +- .../{GuiNet6 => Gui}/Views/ShellWindow.axaml | 6 ++--- .../Views/ShellWindow.axaml.cs | 2 +- .../{GuiNet6 => Gui}/Views/SidebarView.axaml | 2 +- .../Views/SidebarView.axaml.cs | 2 +- sandbox/GuiNet6/GuiNet6.csproj | 24 ------------------ sandbox/GuiNet6/nuget.config | 11 -------- src/LinuxDebugger.sln | 4 +-- src/VsLinuxDebugger/Core/RemoteDebugger.cs | 4 +++ src/VsLinuxDebugger/Core/SshTool.cs | 2 +- src/VsLinuxDebugger/VsLinuxDebugger.csproj | 8 +++--- 32 files changed, 84 insertions(+), 79 deletions(-) create mode 100644 nuget.config rename sandbox/{ConsoleNet6.sln => Console.sln} (87%) rename sandbox/{ConsoleNet6/ConsoleNet6.csproj => Console/Console.csproj} (69%) rename sandbox/{ConsoleNet6 => Console}/Program.cs (100%) rename sandbox/{GuiNet6.sln => Gui.sln} (88%) rename sandbox/{GuiNet6 => Gui}/App.axaml (62%) rename sandbox/{GuiNet6 => Gui}/App.axaml.cs (93%) rename sandbox/{GuiNet6 => Gui}/Assets/avalonia-logo.ico (100%) create mode 100644 sandbox/Gui/Gui.csproj rename sandbox/{GuiNet6 => Gui}/Program.cs (89%) rename sandbox/{GuiNet6 => Gui}/Properties/launchSettings.json (92%) rename sandbox/{GuiNet6 => Gui}/RegionNames.cs (95%) rename sandbox/{GuiNet6 => Gui}/ViewModels/DashboardViewModel.cs (92%) rename sandbox/{GuiNet6 => Gui}/ViewModels/SettingsViewModel.cs (78%) rename sandbox/{GuiNet6 => Gui}/ViewModels/ShellWindowViewModel.cs (81%) rename sandbox/{GuiNet6 => Gui}/ViewModels/SidebarViewModel.cs (89%) rename sandbox/{GuiNet6 => Gui}/ViewModels/ViewModelBase.cs (95%) rename sandbox/{GuiNet6 => Gui}/Views/DashboardView.axaml (93%) rename sandbox/{GuiNet6 => Gui}/Views/DashboardView.axaml.cs (91%) rename sandbox/{GuiNet6 => Gui}/Views/SettingsView.axaml (92%) rename sandbox/{GuiNet6 => Gui}/Views/SettingsView.axaml.cs (91%) rename sandbox/{GuiNet6 => Gui}/Views/ShellWindow.axaml (92%) rename sandbox/{GuiNet6 => Gui}/Views/ShellWindow.axaml.cs (92%) rename sandbox/{GuiNet6 => Gui}/Views/SidebarView.axaml (95%) rename sandbox/{GuiNet6 => Gui}/Views/SidebarView.axaml.cs (91%) delete mode 100644 sandbox/GuiNet6/GuiNet6.csproj delete mode 100644 sandbox/GuiNet6/nuget.config diff --git a/.gitignore b/.gitignore index 0e1fbb7..1f4ba34 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ bld/ [Oo]bj/ [Ll]og/ .vs/ +.vscode/ # NuGet Packages *.nupkg diff --git a/nuget.config b/nuget.config new file mode 100644 index 0000000..128d95e --- /dev/null +++ b/nuget.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/sandbox/ConsoleNet6.sln b/sandbox/Console.sln similarity index 87% rename from sandbox/ConsoleNet6.sln rename to sandbox/Console.sln index f2928a1..6313930 100644 --- a/sandbox/ConsoleNet6.sln +++ b/sandbox/Console.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.2.32317.152 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleNet6", "ConsoleNet6\ConsoleNet6.csproj", "{F181E39A-CCE1-45C1-B196-0CE759BB86AB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Console", "Console\Console.csproj", "{F181E39A-CCE1-45C1-B196-0CE759BB86AB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/sandbox/ConsoleNet6/ConsoleNet6.csproj b/sandbox/Console/Console.csproj similarity index 69% rename from sandbox/ConsoleNet6/ConsoleNet6.csproj rename to sandbox/Console/Console.csproj index 4d39f1d..58ef372 100644 --- a/sandbox/ConsoleNet6/ConsoleNet6.csproj +++ b/sandbox/Console/Console.csproj @@ -2,10 +2,9 @@ Exe - net6.0 + net8.0;net10.0 enable enable - false portable diff --git a/sandbox/ConsoleNet6/Program.cs b/sandbox/Console/Program.cs similarity index 100% rename from sandbox/ConsoleNet6/Program.cs rename to sandbox/Console/Program.cs diff --git a/sandbox/GuiNet6.sln b/sandbox/Gui.sln similarity index 88% rename from sandbox/GuiNet6.sln rename to sandbox/Gui.sln index e6d2b2f..56d2724 100644 --- a/sandbox/GuiNet6.sln +++ b/sandbox/Gui.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.3.32804.467 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GuiNet6", "GuiNet6\GuiNet6.csproj", "{98F164D1-51CF-4BC1-8F9D-8DFB541E57F5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gui", "Gui\Gui.csproj", "{98F164D1-51CF-4BC1-8F9D-8DFB541E57F5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/sandbox/GuiNet6/App.axaml b/sandbox/Gui/App.axaml similarity index 62% rename from sandbox/GuiNet6/App.axaml rename to sandbox/Gui/App.axaml index d3a6f62..0c37777 100644 --- a/sandbox/GuiNet6/App.axaml +++ b/sandbox/Gui/App.axaml @@ -1,13 +1,14 @@ + xmlns:local="using:Gui" + x:Class="Gui.App" + RequestedThemeVariant="Light"> - - + + \ No newline at end of file diff --git a/sandbox/GuiNet6/App.axaml.cs b/sandbox/Gui/App.axaml.cs similarity index 93% rename from sandbox/GuiNet6/App.axaml.cs rename to sandbox/Gui/App.axaml.cs index 4f5aaa8..59acd15 100644 --- a/sandbox/GuiNet6/App.axaml.cs +++ b/sandbox/Gui/App.axaml.cs @@ -1,13 +1,13 @@ using Avalonia; using Avalonia.Markup.Xaml; -using GuiNet6.ViewModels; -using GuiNet6.Views; +using Gui.ViewModels; +using Gui.Views; using Prism.DryIoc; using Prism.Ioc; using Prism.Modularity; -using Prism.Regions; +using Prism.Navigation.Regions; -namespace GuiNet6; +namespace Gui; public class App : PrismApplication { @@ -31,7 +31,7 @@ protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) /// User interface entry point, called after Register and ConfigureModules. /// Startup View. - protected override IAvaloniaObject CreateShell() + protected override AvaloniaObject CreateShell() { return this.Container.Resolve(); } diff --git a/sandbox/GuiNet6/Assets/avalonia-logo.ico b/sandbox/Gui/Assets/avalonia-logo.ico similarity index 100% rename from sandbox/GuiNet6/Assets/avalonia-logo.ico rename to sandbox/Gui/Assets/avalonia-logo.ico diff --git a/sandbox/Gui/Gui.csproj b/sandbox/Gui/Gui.csproj new file mode 100644 index 0000000..05fe692 --- /dev/null +++ b/sandbox/Gui/Gui.csproj @@ -0,0 +1,24 @@ + + + WinExe + net8.0;net10.0 + enable + + + + + + + + + + + + + + + + ShellWindow.axaml + + + diff --git a/sandbox/GuiNet6/Program.cs b/sandbox/Gui/Program.cs similarity index 89% rename from sandbox/GuiNet6/Program.cs rename to sandbox/Gui/Program.cs index 490f7ad..d2f84a7 100644 --- a/sandbox/GuiNet6/Program.cs +++ b/sandbox/Gui/Program.cs @@ -2,7 +2,7 @@ using Avalonia; using Avalonia.ReactiveUI; -namespace GuiNet6; +namespace Gui; internal class Program { @@ -17,8 +17,7 @@ public static AppBuilder BuildAvaloniaApp() => AppBuilder }) .With(new Win32PlatformOptions { - EnableMultitouch = true, - AllowEglInitialization = true, + RenderingMode = [Win32RenderingMode.AngleEgl, Win32RenderingMode.Software], }) .UseSkia() .UseReactiveUI() diff --git a/sandbox/GuiNet6/Properties/launchSettings.json b/sandbox/Gui/Properties/launchSettings.json similarity index 92% rename from sandbox/GuiNet6/Properties/launchSettings.json rename to sandbox/Gui/Properties/launchSettings.json index da8521b..efdff85 100644 --- a/sandbox/GuiNet6/Properties/launchSettings.json +++ b/sandbox/Gui/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "GuiNet6": { + "Gui": { "commandName": "Project" }, "WSL": { diff --git a/sandbox/GuiNet6/RegionNames.cs b/sandbox/Gui/RegionNames.cs similarity index 95% rename from sandbox/GuiNet6/RegionNames.cs rename to sandbox/Gui/RegionNames.cs index 3a323ed..4fe546c 100644 --- a/sandbox/GuiNet6/RegionNames.cs +++ b/sandbox/Gui/RegionNames.cs @@ -1,4 +1,4 @@ -namespace GuiNet6; +namespace Gui; public static class RegionNames { diff --git a/sandbox/GuiNet6/ViewModels/DashboardViewModel.cs b/sandbox/Gui/ViewModels/DashboardViewModel.cs similarity index 92% rename from sandbox/GuiNet6/ViewModels/DashboardViewModel.cs rename to sandbox/Gui/ViewModels/DashboardViewModel.cs index 353e370..959c832 100644 --- a/sandbox/GuiNet6/ViewModels/DashboardViewModel.cs +++ b/sandbox/Gui/ViewModels/DashboardViewModel.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; using Prism.Commands; -namespace GuiNet6.ViewModels; +namespace Gui.ViewModels; public class DashboardViewModel : ViewModelBase { diff --git a/sandbox/GuiNet6/ViewModels/SettingsViewModel.cs b/sandbox/Gui/ViewModels/SettingsViewModel.cs similarity index 78% rename from sandbox/GuiNet6/ViewModels/SettingsViewModel.cs rename to sandbox/Gui/ViewModels/SettingsViewModel.cs index 986ee0c..7339060 100644 --- a/sandbox/GuiNet6/ViewModels/SettingsViewModel.cs +++ b/sandbox/Gui/ViewModels/SettingsViewModel.cs @@ -1,4 +1,4 @@ -namespace GuiNet6.ViewModels; +namespace Gui.ViewModels; public class SettingsViewModel : ViewModelBase { diff --git a/sandbox/GuiNet6/ViewModels/ShellWindowViewModel.cs b/sandbox/Gui/ViewModels/ShellWindowViewModel.cs similarity index 81% rename from sandbox/GuiNet6/ViewModels/ShellWindowViewModel.cs rename to sandbox/Gui/ViewModels/ShellWindowViewModel.cs index 557358f..da74489 100644 --- a/sandbox/GuiNet6/ViewModels/ShellWindowViewModel.cs +++ b/sandbox/Gui/ViewModels/ShellWindowViewModel.cs @@ -1,4 +1,4 @@ -namespace GuiNet6.ViewModels; +namespace Gui.ViewModels; public class ShellWindowViewModel : ViewModelBase { diff --git a/sandbox/GuiNet6/ViewModels/SidebarViewModel.cs b/sandbox/Gui/ViewModels/SidebarViewModel.cs similarity index 89% rename from sandbox/GuiNet6/ViewModels/SidebarViewModel.cs rename to sandbox/Gui/ViewModels/SidebarViewModel.cs index 2f6beca..1c03270 100644 --- a/sandbox/GuiNet6/ViewModels/SidebarViewModel.cs +++ b/sandbox/Gui/ViewModels/SidebarViewModel.cs @@ -1,9 +1,9 @@ -using GuiNet6.Views; +using Gui.Views; using Prism.Commands; using Prism.Events; -using Prism.Regions; +using Prism.Navigation.Regions; -namespace GuiNet6.ViewModels; +namespace Gui.ViewModels; public class SidebarViewModel : ViewModelBase { diff --git a/sandbox/GuiNet6/ViewModels/ViewModelBase.cs b/sandbox/Gui/ViewModels/ViewModelBase.cs similarity index 95% rename from sandbox/GuiNet6/ViewModels/ViewModelBase.cs rename to sandbox/Gui/ViewModels/ViewModelBase.cs index f72b5b6..cad9c57 100644 --- a/sandbox/GuiNet6/ViewModels/ViewModelBase.cs +++ b/sandbox/Gui/ViewModels/ViewModelBase.cs @@ -1,11 +1,11 @@ using System; using System.Linq.Expressions; using Prism.Mvvm; -using Prism.Regions; +using Prism.Navigation.Regions; -namespace GuiNet6.ViewModels; +namespace Gui.ViewModels; -public class ViewModelBase : BindableBase, INavigationAware +public class ViewModelBase : BindableBase, IRegionAware { private string _title = string.Empty; diff --git a/sandbox/GuiNet6/Views/DashboardView.axaml b/sandbox/Gui/Views/DashboardView.axaml similarity index 93% rename from sandbox/GuiNet6/Views/DashboardView.axaml rename to sandbox/Gui/Views/DashboardView.axaml index 91ec7a2..28938ae 100644 --- a/sandbox/GuiNet6/Views/DashboardView.axaml +++ b/sandbox/Gui/Views/DashboardView.axaml @@ -5,7 +5,7 @@ xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="300" - x:Class="GuiNet6.Views.DashboardView"> + x:Class="Gui.Views.DashboardView"> diff --git a/sandbox/GuiNet6/Views/DashboardView.axaml.cs b/sandbox/Gui/Views/DashboardView.axaml.cs similarity index 91% rename from sandbox/GuiNet6/Views/DashboardView.axaml.cs rename to sandbox/Gui/Views/DashboardView.axaml.cs index f8f7764..c0a25b0 100644 --- a/sandbox/GuiNet6/Views/DashboardView.axaml.cs +++ b/sandbox/Gui/Views/DashboardView.axaml.cs @@ -2,7 +2,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace GuiNet6.Views; +namespace Gui.Views; public partial class DashboardView : UserControl { diff --git a/sandbox/GuiNet6/Views/SettingsView.axaml b/sandbox/Gui/Views/SettingsView.axaml similarity index 92% rename from sandbox/GuiNet6/Views/SettingsView.axaml rename to sandbox/Gui/Views/SettingsView.axaml index 985fea5..e9a1e56 100644 --- a/sandbox/GuiNet6/Views/SettingsView.axaml +++ b/sandbox/Gui/Views/SettingsView.axaml @@ -5,7 +5,7 @@ xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="GuiNet6.Views.SettingsView"> + x:Class="Gui.Views.SettingsView"> > diff --git a/sandbox/GuiNet6/Views/SettingsView.axaml.cs b/sandbox/Gui/Views/SettingsView.axaml.cs similarity index 91% rename from sandbox/GuiNet6/Views/SettingsView.axaml.cs rename to sandbox/Gui/Views/SettingsView.axaml.cs index 7f4faf3..f927790 100644 --- a/sandbox/GuiNet6/Views/SettingsView.axaml.cs +++ b/sandbox/Gui/Views/SettingsView.axaml.cs @@ -2,7 +2,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace GuiNet6.Views; +namespace Gui.Views; public partial class SettingsView : UserControl { diff --git a/sandbox/GuiNet6/Views/ShellWindow.axaml b/sandbox/Gui/Views/ShellWindow.axaml similarity index 92% rename from sandbox/GuiNet6/Views/ShellWindow.axaml rename to sandbox/Gui/Views/ShellWindow.axaml index f39c719..5161826 100644 --- a/sandbox/GuiNet6/Views/ShellWindow.axaml +++ b/sandbox/Gui/Views/ShellWindow.axaml @@ -2,12 +2,12 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:core="clr-namespace:GuiNet6;assembly=GuiNet6" - xmlns:local="using:GuiNet6.Views" + xmlns:core="clr-namespace:Gui;assembly=Gui" + xmlns:local="using:Gui.Views" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="350" - x:Class="GuiNet6.Views.ShellWindow" + x:Class="Gui.Views.ShellWindow" Background="Transparent" Icon="/Assets/avalonia-logo.ico" Width="600" Height="300" diff --git a/sandbox/GuiNet6/Views/ShellWindow.axaml.cs b/sandbox/Gui/Views/ShellWindow.axaml.cs similarity index 92% rename from sandbox/GuiNet6/Views/ShellWindow.axaml.cs rename to sandbox/Gui/Views/ShellWindow.axaml.cs index 5dad51e..2a3924e 100644 --- a/sandbox/GuiNet6/Views/ShellWindow.axaml.cs +++ b/sandbox/Gui/Views/ShellWindow.axaml.cs @@ -2,7 +2,7 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace GuiNet6.Views; +namespace Gui.Views; public partial class ShellWindow : Window { diff --git a/sandbox/GuiNet6/Views/SidebarView.axaml b/sandbox/Gui/Views/SidebarView.axaml similarity index 95% rename from sandbox/GuiNet6/Views/SidebarView.axaml rename to sandbox/Gui/Views/SidebarView.axaml index 640f616..efb7d0e 100644 --- a/sandbox/GuiNet6/Views/SidebarView.axaml +++ b/sandbox/Gui/Views/SidebarView.axaml @@ -5,7 +5,7 @@ xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" - x:Class="GuiNet6.Views.SidebarView"> + x:Class="Gui.Views.SidebarView"> - - WinExe - net6.0 - enable - false - - - - - - - - - - - - - - - ShellWindow.axaml - - - diff --git a/sandbox/GuiNet6/nuget.config b/sandbox/GuiNet6/nuget.config deleted file mode 100644 index 6c273ab..0000000 --- a/sandbox/GuiNet6/nuget.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/src/LinuxDebugger.sln b/src/LinuxDebugger.sln index 823c99e..c462ca1 100644 --- a/src/LinuxDebugger.sln +++ b/src/LinuxDebugger.sln @@ -13,9 +13,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{42E06A3F-6EF1-49EC-910E-A89C15BC6E91}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GuiNet6", "..\sandbox\GuiNet6\GuiNet6.csproj", "{2F01BAAF-DF1E-406C-A46D-127896C203F1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gui", "..\sandbox\Gui\Gui.csproj", "{2F01BAAF-DF1E-406C-A46D-127896C203F1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleNet6", "..\sandbox\ConsoleNet6\ConsoleNet6.csproj", "{D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console", "..\sandbox\Console\Console.csproj", "{D108A8EB-E4CD-4A84-A92B-DAD4DB6CA983}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/VsLinuxDebugger/Core/RemoteDebugger.cs b/src/VsLinuxDebugger/Core/RemoteDebugger.cs index f3c1209..4144350 100644 --- a/src/VsLinuxDebugger/Core/RemoteDebugger.cs +++ b/src/VsLinuxDebugger/Core/RemoteDebugger.cs @@ -55,7 +55,11 @@ public async Task BeginAsync(BuildOptions buildOptions) { BuildBegin(); +#pragma warning disable VSTHRD003 // _buildTask is a TaskCompletionSource completed by a + // DTE build-event callback (BuildEvents_OnBuildDone), not a cold task started + // elsewhere; awaiting it here cannot deadlock. await _buildTask.Task; +#pragma warning restore VSTHRD003 // Work completed if (!_buildSuccessful) diff --git a/src/VsLinuxDebugger/Core/SshTool.cs b/src/VsLinuxDebugger/Core/SshTool.cs index d8d20dd..7ae6d2f 100644 --- a/src/VsLinuxDebugger/Core/SshTool.cs +++ b/src/VsLinuxDebugger/Core/SshTool.cs @@ -433,7 +433,7 @@ await Task.Run(() => { try { - using (var tarGzWriter = WriterFactory.Open(tarGzStream, ArchiveType.Tar, CompressionType.GZip)) + using (var tarGzWriter = WriterFactory.OpenWriter(tarGzStream, ArchiveType.Tar, WriterOptions.ForTar(CompressionType.GZip))) { using (MemoryStream fileStream = new MemoryStream()) { diff --git a/src/VsLinuxDebugger/VsLinuxDebugger.csproj b/src/VsLinuxDebugger/VsLinuxDebugger.csproj index 58643c2..40e581e 100644 --- a/src/VsLinuxDebugger/VsLinuxDebugger.csproj +++ b/src/VsLinuxDebugger/VsLinuxDebugger.csproj @@ -95,16 +95,16 @@ - - + + - 0.30.1 + 0.50.4 2023.0.1 - 6.0.10 + 10.0.11 From ae9a795906c6144100e86b777f0a1c5916b2279e Mon Sep 17 00:00:00 2001 From: Valentin Kindschi Date: Tue, 8 Sep 2026 13:28:45 +0200 Subject: [PATCH 3/8] build(sandbox): Updated to Avalonia 12.x --- sandbox/Gui/App.axaml | 29 +++++--- sandbox/Gui/App.axaml.cs | 68 ++++++------------- sandbox/Gui/Gui.csproj | 12 ++-- sandbox/Gui/Program.cs | 4 +- sandbox/Gui/RegionNames.cs | 16 ----- sandbox/Gui/ViewModels/DashboardViewModel.cs | 18 ++--- sandbox/Gui/ViewModels/SettingsViewModel.cs | 2 +- .../Gui/ViewModels/ShellWindowViewModel.cs | 30 +++++++- sandbox/Gui/ViewModels/SidebarViewModel.cs | 28 +++----- sandbox/Gui/ViewModels/ViewModelBase.cs | 50 +------------- sandbox/Gui/Views/DashboardView.axaml | 8 +-- sandbox/Gui/Views/SettingsView.axaml | 12 ++-- sandbox/Gui/Views/ShellWindow.axaml | 16 ++--- sandbox/Gui/Views/ShellWindow.axaml.cs | 3 - sandbox/Gui/Views/SidebarView.axaml | 8 +-- src/VsLinuxDebugger/VsLinuxDebugger.csproj | 8 ++- 16 files changed, 123 insertions(+), 189 deletions(-) delete mode 100644 sandbox/Gui/RegionNames.cs diff --git a/sandbox/Gui/App.axaml b/sandbox/Gui/App.axaml index 0c37777..0f926dc 100644 --- a/sandbox/Gui/App.axaml +++ b/sandbox/Gui/App.axaml @@ -1,14 +1,21 @@ - - - + + + + + + + + + - + - - \ No newline at end of file + + diff --git a/sandbox/Gui/App.axaml.cs b/sandbox/Gui/App.axaml.cs index 59acd15..0d69487 100644 --- a/sandbox/Gui/App.axaml.cs +++ b/sandbox/Gui/App.axaml.cs @@ -1,63 +1,37 @@ -using Avalonia; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Gui.ViewModels; using Gui.Views; -using Prism.DryIoc; -using Prism.Ioc; -using Prism.Modularity; -using Prism.Navigation.Regions; namespace Gui; -public class App : PrismApplication +public class App : Application { /// App entry point. public override void Initialize() { AvaloniaXamlLoader.Load(this); - base.Initialize(); +#if DEBUG + this.AttachDeveloperTools(); +#endif } - /// Prism Module Registration. - /// - /// Module Catalog. - protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) + /// Called once the Avalonia framework has finished initializing. + public override void OnFrameworkInitializationCompleted() { - base.ConfigureModuleCatalog(moduleCatalog); - - // Wire-up modules for Region Manager - //// moduleCatalog.AddModule(); - } - - /// User interface entry point, called after Register and ConfigureModules. - /// Startup View. - protected override AvaloniaObject CreateShell() - { - return this.Container.Resolve(); - } - - /// Called after Initialize. - protected override void OnInitialized() - { - // Register Views to Region it will appear in. Don't register them in the ViewModel. - var regionManager = Container.Resolve(); - regionManager.RegisterViewWithRegion(RegionNames.ContentRegion, typeof(DashboardView)); - regionManager.RegisterViewWithRegion(RegionNames.SidebarRegion, typeof(SidebarView)); - } - - /// Register views and Services. - /// IOC Container. - protected override void RegisterTypes(IContainerRegistry containerRegistry) - { - // Services - // ... - - // Views - Generic views - containerRegistry.Register(); - containerRegistry.Register(); - - // Views - Region Navigation - containerRegistry.RegisterForNavigation(); - containerRegistry.RegisterForNavigation(); + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + // Compose views/view-models directly - no DI container, no region manager. + var shellViewModel = new ShellWindowViewModel(); + var shellWindow = new ShellWindow + { + DataContext = shellViewModel, + }; + + desktop.MainWindow = shellWindow; + } + + base.OnFrameworkInitializationCompleted(); } } diff --git a/sandbox/Gui/Gui.csproj b/sandbox/Gui/Gui.csproj index 05fe692..80702a8 100644 --- a/sandbox/Gui/Gui.csproj +++ b/sandbox/Gui/Gui.csproj @@ -8,13 +8,11 @@ - - - - - - - + + + + + diff --git a/sandbox/Gui/Program.cs b/sandbox/Gui/Program.cs index d2f84a7..5c194d5 100644 --- a/sandbox/Gui/Program.cs +++ b/sandbox/Gui/Program.cs @@ -1,6 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Avalonia; -using Avalonia.ReactiveUI; +using ReactiveUI.Avalonia; namespace Gui; @@ -20,7 +20,7 @@ public static AppBuilder BuildAvaloniaApp() => AppBuilder RenderingMode = [Win32RenderingMode.AngleEgl, Win32RenderingMode.Software], }) .UseSkia() - .UseReactiveUI() + .UseReactiveUI(rxui => { }) .LogToTrace(); // Initialization code. Don't use any Avalonia, third-party APIs or any diff --git a/sandbox/Gui/RegionNames.cs b/sandbox/Gui/RegionNames.cs deleted file mode 100644 index 4fe546c..0000000 --- a/sandbox/Gui/RegionNames.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Gui; - -public static class RegionNames -{ - /// Main Window's content region - public const string ContentRegion = "ContentRegion"; - - /// Main Window's Footer Status Bar. - public const string FooterRegion = "FooterRegion"; - - /// Main Window's right side bar. - public const string RightSidebarRegion = "RightSidebarRegion"; - - /// Main Window's side bar. - public const string SidebarRegion = "SidebarRegion"; -} diff --git a/sandbox/Gui/ViewModels/DashboardViewModel.cs b/sandbox/Gui/ViewModels/DashboardViewModel.cs index 959c832..171f859 100644 --- a/sandbox/Gui/ViewModels/DashboardViewModel.cs +++ b/sandbox/Gui/ViewModels/DashboardViewModel.cs @@ -1,5 +1,5 @@ -using Microsoft.CodeAnalysis; -using Prism.Commands; +using System.Windows.Input; +using ReactiveUI; namespace Gui.ViewModels; @@ -8,12 +8,14 @@ public class DashboardViewModel : ViewModelBase public DashboardViewModel() { Title = "Dashboard View!"; + + CmdBreakPoint = ReactiveCommand.Create(() => + { + // Force a breakpoint + System.Diagnostics.Debug.WriteLine("Breakpoint triggering"); + System.Diagnostics.Debugger.Break(); + }); } - public DelegateCommand CmdBreakPoint => new(() => - { - // Force a breakpoint - System.Diagnostics.Debug.WriteLine("Breakpoint triggering"); - System.Diagnostics.Debugger.Break(); - }); + public ICommand CmdBreakPoint { get; } } diff --git a/sandbox/Gui/ViewModels/SettingsViewModel.cs b/sandbox/Gui/ViewModels/SettingsViewModel.cs index 7339060..662872c 100644 --- a/sandbox/Gui/ViewModels/SettingsViewModel.cs +++ b/sandbox/Gui/ViewModels/SettingsViewModel.cs @@ -1,4 +1,4 @@ -namespace Gui.ViewModels; +namespace Gui.ViewModels; public class SettingsViewModel : ViewModelBase { diff --git a/sandbox/Gui/ViewModels/ShellWindowViewModel.cs b/sandbox/Gui/ViewModels/ShellWindowViewModel.cs index da74489..28473d4 100644 --- a/sandbox/Gui/ViewModels/ShellWindowViewModel.cs +++ b/sandbox/Gui/ViewModels/ShellWindowViewModel.cs @@ -1,9 +1,35 @@ -namespace Gui.ViewModels; +using ReactiveUI; + +namespace Gui.ViewModels; public class ShellWindowViewModel : ViewModelBase { + private ViewModelBase _currentPage; + public ShellWindowViewModel() { - Title = "Sample Prism.Avalonia - Navigation"; + Title = "Sample Avalonia - Navigation"; + + Sidebar = new SidebarViewModel(NavigateTo); + + var dashboard = new DashboardViewModel(); + _currentPage = dashboard; + } + + /// Gets the sidebar view model hosted in this shell. + public SidebarViewModel Sidebar { get; } + + /// Gets or sets the view model currently displayed in the shell's content area. + public ViewModelBase CurrentPage + { + get => _currentPage; + set => this.RaiseAndSetIfChanged(ref _currentPage, value); + } + + /// Switches the content area to the given view model. + /// View model to display. + private void NavigateTo(ViewModelBase viewModel) + { + CurrentPage = viewModel; } } diff --git a/sandbox/Gui/ViewModels/SidebarViewModel.cs b/sandbox/Gui/ViewModels/SidebarViewModel.cs index 1c03270..0ed6502 100644 --- a/sandbox/Gui/ViewModels/SidebarViewModel.cs +++ b/sandbox/Gui/ViewModels/SidebarViewModel.cs @@ -1,30 +1,24 @@ -using Gui.Views; -using Prism.Commands; -using Prism.Events; -using Prism.Navigation.Regions; +using System; +using System.Windows.Input; +using ReactiveUI; namespace Gui.ViewModels; public class SidebarViewModel : ViewModelBase { - private IEventAggregator _eventAggregator; - private IRegionManager _regionManager; + private readonly Action _navigate; - public SidebarViewModel(IRegionManager regionManager, IEventAggregator ea) + public SidebarViewModel(Action navigate) { - _regionManager = regionManager; - _eventAggregator = ea; + _navigate = navigate; Title = "Navigation"; + + CmdDashboard = ReactiveCommand.Create(() => _navigate(new DashboardViewModel())); + CmdSettings = ReactiveCommand.Create(() => _navigate(new SettingsViewModel())); } - public DelegateCommand CmdDashboard => new(() => - { - _regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(DashboardView)); - }); + public ICommand CmdDashboard { get; } - public DelegateCommand CmdSettings => new(() => - { - _regionManager.RequestNavigate(RegionNames.ContentRegion, nameof(SettingsView)); - }); + public ICommand CmdSettings { get; } } diff --git a/sandbox/Gui/ViewModels/ViewModelBase.cs b/sandbox/Gui/ViewModels/ViewModelBase.cs index cad9c57..59888be 100644 --- a/sandbox/Gui/ViewModels/ViewModelBase.cs +++ b/sandbox/Gui/ViewModels/ViewModelBase.cs @@ -1,11 +1,8 @@ -using System; -using System.Linq.Expressions; -using Prism.Mvvm; -using Prism.Navigation.Regions; +using ReactiveUI; namespace Gui.ViewModels; -public class ViewModelBase : BindableBase, IRegionAware +public class ViewModelBase : ReactiveObject { private string _title = string.Empty; @@ -13,47 +10,6 @@ public class ViewModelBase : BindableBase, IRegionAware public string Title { get => _title; - set => SetProperty(ref _title, value); - } - - /// - /// Called to determine if this instance can handle the navigation request. - /// Don't call this directly, use . - /// - /// The navigation context. - /// if this instance accepts the navigation request; otherwise, . - public virtual bool IsNavigationTarget(NavigationContext navigationContext) - { - // Auto-allow navigation - return OnNavigatingTo(navigationContext); - } - - /// Called when the implementer is being navigated away from. - /// The navigation context. - public virtual void OnNavigatedFrom(NavigationContext navigationContext) - { - } - - /// Called when the implementer has been navigated to. - /// The navigation context. - public virtual void OnNavigatedTo(NavigationContext navigationContext) - { - } - - /// Navigation validation checker. - /// Override for Prism 7.2's IsNavigationTarget. - /// The navigation context. - /// if this instance accepts the navigation request; otherwise, . - public virtual bool OnNavigatingTo(NavigationContext navigationContext) - { - return true; - } - - // Note from Prism.Mvvm.BindableBase - [Obsolete("Please use RaisePropertyChanged(nameof(PropertyName)) from Prism.Mvvm.BindableBase instead. Expressions are slower, and the new nameof feature eliminates the magic strings.")] - protected void RaisePropertyChanged(Expression> propertyExpression) - { - var propertyName = PropertySupport.ExtractPropertyName(propertyExpression); - RaisePropertyChanged(propertyName); + set => this.RaiseAndSetIfChanged(ref _title, value); } } diff --git a/sandbox/Gui/Views/DashboardView.axaml b/sandbox/Gui/Views/DashboardView.axaml index 28938ae..2e5a1e5 100644 --- a/sandbox/Gui/Views/DashboardView.axaml +++ b/sandbox/Gui/Views/DashboardView.axaml @@ -2,13 +2,13 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:prism="http://prismlibrary.com/" - prism:ViewModelLocator.AutoWireViewModel="True" + xmlns:vm="using:Gui.ViewModels" mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="300" - x:Class="Gui.Views.DashboardView"> + x:Class="Gui.Views.DashboardView" + x:DataType="vm:DashboardViewModel"> - + - + + - --> - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/VsLinuxDebugger/OptionsPages/LocalOptionsControl.xaml.cs b/src/VsLinuxDebugger/OptionsPages/LocalOptionsControl.xaml.cs new file mode 100644 index 0000000..bbb8cf9 --- /dev/null +++ b/src/VsLinuxDebugger/OptionsPages/LocalOptionsControl.xaml.cs @@ -0,0 +1,14 @@ +using System.Windows.Controls; + +namespace Xeno.VsLinuxDebug.OptionsPages +{ + /// WPF UI for . + public partial class LocalOptionsControl : UserControl + { + public LocalOptionsControl(LocalOptionsPage page) + { + InitializeComponent(); + DataContext = page; + } + } +} diff --git a/src/VsLinuxDebugger/OptionsPages/LocalOptionsPage.cs b/src/VsLinuxDebugger/OptionsPages/LocalOptionsPage.cs new file mode 100644 index 0000000..3b939b2 --- /dev/null +++ b/src/VsLinuxDebugger/OptionsPages/LocalOptionsPage.cs @@ -0,0 +1,77 @@ +using System.ComponentModel; +using System.Windows; +using Microsoft.VisualStudio.Shell; +using VsLinuxDebugger; + +namespace Xeno.VsLinuxDebug.OptionsPages +{ + /// Local-machine settings. + public class LocalOptionsPage : UIElementDialogPage, INotifyPropertyChanged + { + private bool _useSSHExeEnabled = true; + private string _plinkPath = ""; + private bool _deleteLaunchJsonAfterBuild = false; + private bool _autoSwitchLinuxDbgOutput = true; + private bool _forceKillOnStop = false; + + public event PropertyChangedEventHandler PropertyChanged; + + [Category("Local Settings")] + [DisplayName("Use SSH.exe instead of PLINK")] + [Description("Use SSH.exe (supports OpenSSH keys directly) instead of PLINK (needs a '.ppk' key).")] + public bool UseSSHExeEnabled + { + get => _useSSHExeEnabled; + set { _useSSHExeEnabled = value; OnPropertyChanged(nameof(UseSSHExeEnabled)); } + } + + [Category("Local Settings")] + [DisplayName("PLink Local Path (blank to use embedded)")] + [Description("Full path to local PLINK.EXE, used when 'Use SSH.exe' is unchecked.")] + public string PLinkPath + { + get => _plinkPath; + set { _plinkPath = value; OnPropertyChanged(nameof(PLinkPath)); } + } + + [Category("Local Settings")] + [DisplayName("Delete 'launch.json' after build.")] + [Description(@"The `launch.json` is generated in your build folder. You may keep this for debugging.")] + public bool DeleteLaunchJsonAfterBuild + { + get => _deleteLaunchJsonAfterBuild; + set { _deleteLaunchJsonAfterBuild = value; OnPropertyChanged(nameof(DeleteLaunchJsonAfterBuild)); } + } + + [Category("Local Settings")] + [DisplayName("Switch to LinuxDbg Output on Build")] + [Description("Automatically show output for Linux Debugger on build (default = true).")] + public bool SwitchLinuxDbgOutput + { + get => _autoSwitchLinuxDbgOutput; + set + { + Logger.AutoSwitchToLinuxDbgOutput = value; + _autoSwitchLinuxDbgOutput = value; + OnPropertyChanged(nameof(SwitchLinuxDbgOutput)); + } + } + + [Category("Local Settings")] + [DisplayName("Force kill on Stop (interrupt mid-command)")] + [Description("When Stop is used to cancel an in-progress build/deploy, also immediately " + + "close the SSH connection even if a remote command is currently running (default = false, " + + "which instead lets the current step finish before stopping, avoiding a half-uploaded or " + + "half-restarted remote state).")] + public bool ForceKillOnStop + { + get => _forceKillOnStop; + set { _forceKillOnStop = value; OnPropertyChanged(nameof(ForceKillOnStop)); } + } + + protected override UIElement Child => new LocalOptionsControl(this); + + private void OnPropertyChanged(string propertyName) + => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } +} diff --git a/src/VsLinuxDebugger/OptionsPages/MultilineEditDialog.xaml b/src/VsLinuxDebugger/OptionsPages/MultilineEditDialog.xaml new file mode 100644 index 0000000..c823ca4 --- /dev/null +++ b/src/VsLinuxDebugger/OptionsPages/MultilineEditDialog.xaml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + +