From 81ecc4cf225a74ac3ff9352caf0de495f8a25f85 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sun, 31 May 2026 00:13:57 +0200 Subject: [PATCH 001/106] AA05_CommandParCalc --- .../Avln_Marble.Browser/wwwroot/app.css | 43 +++++++++++++++++++ .../Avln_Marble.Browser/wwwroot/index.html | 17 ++++++++ .../Avln_Marble.Browser/wwwroot/main.js | 13 ++++++ 3 files changed, 73 insertions(+) create mode 100644 Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/app.css create mode 100644 Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/index.html create mode 100644 Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/main.js diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/app.css b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/app.css new file mode 100644 index 000000000..384ed8f93 --- /dev/null +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/app.css @@ -0,0 +1,43 @@ +/* HTML styles for the splash screen */ +.avalonia-splash { + position: absolute; + height: 100%; + width: 100%; + background: white; + font-family: 'Outfit', sans-serif; + justify-content: center; + align-items: center; + display: flex; + pointer-events: none; +} + +@media (prefers-color-scheme: light) { + .avalonia-splash { + background: white; + } + + .avalonia-splash h2 { + color: #1b2a4e; + } +} + +@media (prefers-color-scheme: dark) { + .avalonia-splash { + background: #1b2a4e; + } + + .avalonia-splash h2 { + color: white; + } +} + +.avalonia-splash h2 { + font-weight: 400; + font-size: 1.5rem; +} + +.avalonia-splash.splash-close { + transition: opacity 200ms, display 200ms; + display: none; + opacity: 0; +} diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/index.html b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/index.html new file mode 100644 index 000000000..3ef8833c7 --- /dev/null +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/index.html @@ -0,0 +1,17 @@ + + + + Avln_Marble.Browser + + + + + +
+
+

Avln Marble is loading...

+
+
+ + + diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/main.js b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/main.js new file mode 100644 index 000000000..bf1555e43 --- /dev/null +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/wwwroot/main.js @@ -0,0 +1,13 @@ +import { dotnet } from './_framework/dotnet.js' + +const is_browser = typeof window != "undefined"; +if (!is_browser) throw new Error(`Expected to be running in a browser`); + +const dotnetRuntime = await dotnet + .withDiagnosticTracing(false) + .withApplicationArgumentsFromQuery() + .create(); + +const config = dotnetRuntime.getConfig(); + +await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]); From dee5dee61b888911d09fdabbc1177ac77fd4ffbf Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sun, 31 May 2026 00:16:59 +0200 Subject: [PATCH 002/106] Avln_Marble.Browser --- .../Properties/launchSettings.json | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Properties/launchSettings.json b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Properties/launchSettings.json index a152e5fc6..ae8a109a4 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Properties/launchSettings.json +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Properties/launchSettings.json @@ -7,6 +7,21 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:60447;http://localhost:60448" + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + }, + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:62275/", + "sslPort": 44328 } } } \ No newline at end of file From 2987e443c6c5e237b6d8b4198d3edcee5bc45319 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sun, 31 May 2026 00:17:02 +0200 Subject: [PATCH 003/106] Avln_Marble.Display --- .../ViewModels/MarbleViewModel.cs | 4 + .../Avln_Marble.Display/Views/BoardView.axaml | 69 ++++++++------ .../Views/BoardView.axaml.cs | 20 ++-- .../Views/BrowserMainView.axaml | 93 +++++++++++++------ .../Views/BrowserMainView.axaml.cs | 4 +- 5 files changed, 123 insertions(+), 67 deletions(-) diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/ViewModels/MarbleViewModel.cs b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/ViewModels/MarbleViewModel.cs index e83990cd0..2823eb030 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/ViewModels/MarbleViewModel.cs +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/ViewModels/MarbleViewModel.cs @@ -55,6 +55,10 @@ public MarbleViewModel( public double Diameter { get; } + public double SelectionRingDiameter => Diameter + 4.0; + + public double SelectionRingOffset => -2.0; + public IBrush FillBrush { get; } public IBrush FormShadowBrush { get; } diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml index f7ee80186..e8a35eb7b 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml @@ -19,15 +19,17 @@ - - + + @@ -45,6 +47,7 @@ + + - - - - - - - - - + + + + + + + + + + + + + - - - - - - + + + + + + diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml.cs b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml.cs index 7ee115963..f1e828817 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml.cs +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BoardView.axaml.cs @@ -5,6 +5,7 @@ using Avalonia.VisualTree; using Avln_Marble.Display.ViewModels; using MarbleBoard.Engine.Models; +using System; namespace Avln_Marble.Display.Views; @@ -13,7 +14,10 @@ namespace Avln_Marble.Display.Views; /// public partial class BoardView : UserControl { + private const string BoardSurfaceControlName = "BoardSurface"; + private bool _isPointerCaptured; + private readonly Grid _boardSurface; /// /// Initializes a new instance of the class. @@ -21,7 +25,9 @@ public partial class BoardView : UserControl public BoardView() { InitializeComponent(); - AttachedToVisualTree += (_, _) => BoardSurface.Focus(); + _boardSurface = this.FindControl(BoardSurfaceControlName) + ?? throw new InvalidOperationException($"Required control '{BoardSurfaceControlName}' was not found."); + AttachedToVisualTree += (_, _) => _boardSurface.Focus(); } private BoardViewModel Board => (BoardViewModel)DataContext!; @@ -32,7 +38,7 @@ private void BoardSurface_OnPointerPressed(object? sender, PointerPressedEventAr { if (Board.BeginPointerDrag(marble.Coordinate)) { - e.Pointer.Capture(BoardSurface); + e.Pointer.Capture(_boardSurface); _isPointerCaptured = true; e.Handled = true; } @@ -40,11 +46,11 @@ private void BoardSurface_OnPointerPressed(object? sender, PointerPressedEventAr return; } - Point point = e.GetPosition(BoardSurface); + Point point = e.GetPosition(_boardSurface); if (Board.TryGetCoordinate(point, out BoardCoordinate coordinate)) { Board.Select(coordinate); - BoardSurface.Focus(); + _boardSurface.Focus(); e.Handled = true; } } @@ -56,7 +62,7 @@ private void BoardSurface_OnPointerMoved(object? sender, PointerEventArgs e) return; } - Board.UpdatePointer(e.GetPosition(BoardSurface)); + Board.UpdatePointer(e.GetPosition(_boardSurface)); e.Handled = true; } @@ -67,10 +73,10 @@ private void BoardSurface_OnPointerReleased(object? sender, PointerReleasedEvent return; } - Board.CompletePointerDrag(e.GetPosition(BoardSurface)); + Board.CompletePointerDrag(e.GetPosition(_boardSurface)); e.Pointer.Capture(null); _isPointerCaptured = false; - BoardSurface.Focus(); + _boardSurface.Focus(); e.Handled = true; } diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml index 4f06edc7b..8c7acfb9f 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml @@ -10,38 +10,71 @@ Focusable="True" KeyDown="BrowserMainView_OnKeyDown" mc:Ignorable="d"> - - - - - - - - + + + + + + - + - + + + + + + + - - - - - - + + + + + + + + + + + + + + + + + diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml.cs b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml.cs index 24d2ed0b9..01a6dae81 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml.cs +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Views/BrowserMainView.axaml.cs @@ -13,9 +13,11 @@ public partial class BrowserMainView : UserControl /// /// Initializes a new instance of the class. /// - public BrowserMainView() + /// The root view model. + public BrowserMainView(MainWindowViewModel viewModel) { InitializeComponent(); + DataContext = viewModel; AttachedToVisualTree += (_, _) => Focus(); } From f00e19e23f0c48a644e8606341f01bcc5cca2b71 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sun, 31 May 2026 00:17:26 +0200 Subject: [PATCH 004/106] Avalonia_Apps --- .../Avln_Marble-Migration-to-Avalonia.md | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Avalonia_Apps/DevOps/Features/Avln_Marble-Migration-to-Avalonia.md b/Avalonia_Apps/DevOps/Features/Avln_Marble-Migration-to-Avalonia.md index 1d8943d9b..37a08588f 100644 --- a/Avalonia_Apps/DevOps/Features/Avln_Marble-Migration-to-Avalonia.md +++ b/Avalonia_Apps/DevOps/Features/Avln_Marble-Migration-to-Avalonia.md @@ -40,16 +40,23 @@ Create a new `Avln_Marble` Avalonia application set that ports the existing WPF ## Open Questions - Which visual effects require simplification because of Avalonia or browser rendering differences? -- Should a dedicated test project be added immediately, or can validation begin with build coverage and later focused tests? +- A dedicated test project has not been added yet; current validation is based on targeted project builds and should be extended with focused tests in a later increment. ## Tasks - [x] Inspect existing Avalonia shared, desktop, and browser project patterns -- [ ] Create `Avln_Marble` project structure -- [ ] Port shared view models and board interaction logic -- [ ] Port shared Avalonia views and styling -- [ ] Add desktop host bootstrap -- [ ] Add browser host bootstrap -- [ ] Validate builds and targeted tests +- [x] Create `Avln_Marble` project structure +- [x] Port shared view models and board interaction logic +- [x] Port shared Avalonia views and styling +- [x] Add desktop host bootstrap +- [x] Add browser host bootstrap +- [x] Validate builds and targeted tests + +## Validation Notes +- `Avln_Marble.Display` builds successfully on `net10.0`. +- `Avln_Marble.Desktop` builds successfully on `net10.0`. +- `Avln_Marble.Browser` builds successfully on `net10.0-browser`. +- A full workspace build is currently not a reliable quality gate for this feature because unrelated existing projects fail with workload, import, and Edit-and-Continue errors. +- The reported `BoardView` startup `NullReferenceException` was fixed by resolving the `BoardSurface` canvas explicitly instead of relying on a generated field. ## Status -- In Progress +- Completed From 3e40d1d11a03645566e1e81fcc4ecbc9c865564e Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:00 +0200 Subject: [PATCH 005/106] AA05_CommandParCalc --- .../AA05_CommandParCalc.csproj | 1 + Avalonia_Apps/AA98_AvlnCodeStudio.slnx | 32 +++++ .../AA98_AvlnCodeStudio.Base.AI.Tests.csproj | 30 +++++ .../AA98_AvlnCodeStudio.Base.AI.csproj | 19 +++ .../Models/AICompletionOptions.cs | 22 ++++ .../Models/AICompletionRequest.cs | 42 ++++++ .../Models/AICompletionResponse.cs | 23 ++++ .../Models/AIMessage.cs | 35 +++++ .../Models/AIMessageRole.cs | 22 ++++ .../Services/IAIClient.cs | 19 +++ .../Services/IAIClientFactory.cs | 13 ++ .../AA98_AvlnCodeStudio.Base.OS.csproj | 19 +++ .../Services/ITextDocumentStorageService.cs | 27 ++++ .../AA98_AvlnCodeStudio.Base.UI.csproj | 19 +++ .../Services/IEditorFileDialogService.cs | 27 ++++ .../AA98_AvlnCodeStudio.Editor.Tests.csproj | 35 +++++ .../FileEditorDocumentFactoryTests.cs | 28 ++++ .../Services/EditorWorkflowTests.cs | 120 ++++++++++++++++++ .../AA98_AvlnCodeStudio.Editor.csproj | 22 ++++ .../Components/IEditorComponent.cs | 14 ++ .../Components/IEditorComponentFactory.cs | 13 ++ .../Services/EditorOperationKind.cs | 32 +++++ .../Services/EditorOperationResult.cs | 75 +++++++++++ .../Services/EditorOperationStatus.cs | 17 +++ .../Services/EditorWorkflow.cs | 106 ++++++++++++++++ .../Services/EditorWorkflowFactory.cs | 37 ++++++ .../Services/IEditorWorkflow.cs | 50 ++++++++ .../Services/IEditorWorkflowFactory.cs | 13 ++ .../Documents/FileEditorDocumentFactory.cs | 13 ++ .../Documents/IFileEditorDocument.cs | 51 ++++++++ .../Documents/IFileEditorDocumentFactory.cs | 13 ++ .../Components/AvaloniaEditorComponent.cs | 32 +++++ .../AvaloniaEditorComponentFactory.cs | 45 +++++++ .../Components/IAvaloniaEditorComponent.cs | 20 +++ .../IAvaloniaEditorComponentFactory.cs | 15 +++ .../Services/AvaloniaEditorViewFactory.cs | 29 +++++ .../Services/IEditorViewFactory.cs | 16 +++ .../ViewModels/EditorViewModelFactory.cs | 15 +++ .../ViewModels/IEditorViewModelFactory.cs | 16 +++ 39 files changed, 1177 insertions(+) create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio.slnx create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI.Tests/AA98_AvlnCodeStudio.Base.AI.Tests.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionOptions.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionRequest.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionResponse.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessage.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessageRole.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClient.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClientFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/Services/ITextDocumentStorageService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/Services/IEditorFileDialogService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/AA98_AvlnCodeStudio.Editor.Tests.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Documents/FileEditorDocumentFactoryTests.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Services/EditorWorkflowTests.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/AA98_AvlnCodeStudio.Editor.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponent.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponentFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationKind.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationResult.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationStatus.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflow.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflowFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflow.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflowFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocumentFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocument.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocumentFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponent.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponentFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponent.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponentFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorViewFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/IEditorViewFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModelFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/IEditorViewModelFactory.cs diff --git a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj index 1fd76f755..468dcf623 100644 --- a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj +++ b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc/AA05_CommandParCalc.csproj @@ -4,6 +4,7 @@ net8.0 enable true + Debug;Release;DesktopOnly diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio.slnx b/Avalonia_Apps/AA98_AvlnCodeStudio.slnx new file mode 100644 index 000000000..499cc6547 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio.slnx @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI.Tests/AA98_AvlnCodeStudio.Base.AI.Tests.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI.Tests/AA98_AvlnCodeStudio.Base.AI.Tests.csproj new file mode 100644 index 000000000..d2961f07c --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI.Tests/AA98_AvlnCodeStudio.Base.AI.Tests.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + true + false + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj new file mode 100644 index 000000000..2a515800a --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionOptions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionOptions.cs new file mode 100644 index 000000000..3d8d7f55b --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionOptions.cs @@ -0,0 +1,22 @@ +namespace AA98_AvlnCodeStudio.Base.AI.Models; + +/// +/// Provides optional provider-agnostic settings for text generation requests. +/// +public sealed class AICompletionOptions +{ + /// + /// Gets or sets the optional model identifier. + /// + public string? ModelId { get; set; } + + /// + /// Gets or sets the optional temperature. + /// + public double? Temperature { get; set; } + + /// + /// Gets or sets the optional maximum token count. + /// + public int? MaxTokens { get; set; } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionRequest.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionRequest.cs new file mode 100644 index 000000000..b1abed12f --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionRequest.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace AA98_AvlnCodeStudio.Base.AI.Models; + +/// +/// Represents a provider-agnostic text generation request. +/// +public sealed class AICompletionRequest +{ + /// + /// Initializes a new instance of the class. + /// + /// The input messages. + /// The optional request options. + public AICompletionRequest(IEnumerable messages, AICompletionOptions? options = null) + { + if (messages is null) + { + throw new ArgumentNullException(nameof(messages)); + } + + Messages = messages.ToArray(); + if (Messages.Count == 0) + { + throw new ArgumentException("At least one message is required.", nameof(messages)); + } + + Options = options ?? new AICompletionOptions(); + } + + /// + /// Gets the input messages. + /// + public IReadOnlyList Messages { get; } + + /// + /// Gets the optional request options. + /// + public AICompletionOptions Options { get; } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionResponse.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionResponse.cs new file mode 100644 index 000000000..38e0a0721 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AICompletionResponse.cs @@ -0,0 +1,23 @@ +using System; + +namespace AA98_AvlnCodeStudio.Base.AI.Models; + +/// +/// Represents a provider-agnostic text generation response. +/// +public sealed class AICompletionResponse +{ + /// + /// Initializes a new instance of the class. + /// + /// The generated assistant message. + public AICompletionResponse(AIMessage message) + { + Message = message ?? throw new ArgumentNullException(nameof(message)); + } + + /// + /// Gets the generated assistant message. + /// + public AIMessage Message { get; } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessage.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessage.cs new file mode 100644 index 000000000..a07d55aa0 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessage.cs @@ -0,0 +1,35 @@ +using System; + +namespace AA98_AvlnCodeStudio.Base.AI.Models; + +/// +/// Represents a single provider-agnostic AI interaction message. +/// +public sealed class AIMessage +{ + /// + /// Initializes a new instance of the class. + /// + /// The message role. + /// The message content. + public AIMessage(AIMessageRole role, string content) + { + if (string.IsNullOrWhiteSpace(content)) + { + throw new ArgumentException("Message content is required.", nameof(content)); + } + + Role = role; + Content = content; + } + + /// + /// Gets the message role. + /// + public AIMessageRole Role { get; } + + /// + /// Gets the message content. + /// + public string Content { get; } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessageRole.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessageRole.cs new file mode 100644 index 000000000..64d788513 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Models/AIMessageRole.cs @@ -0,0 +1,22 @@ +namespace AA98_AvlnCodeStudio.Base.AI.Models; + +/// +/// Identifies the role of a message in an AI interaction. +/// +public enum AIMessageRole +{ + /// + /// Represents a system-level instruction. + /// + System, + + /// + /// Represents a user message. + /// + User, + + /// + /// Represents an assistant message. + /// + Assistant, +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClient.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClient.cs new file mode 100644 index 000000000..a038b1302 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClient.cs @@ -0,0 +1,19 @@ +using AA98_AvlnCodeStudio.Base.AI.Models; +using System.Threading; +using System.Threading.Tasks; + +namespace AA98_AvlnCodeStudio.Base.AI.Services; + +/// +/// Defines a provider-agnostic AI client for text generation operations. +/// +public interface IAIClient +{ + /// + /// Generates a completion for the supplied provider-agnostic request. + /// + /// The completion request. + /// The cancellation token. + /// The completion response. + Task CompleteAsync(AICompletionRequest request, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClientFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClientFactory.cs new file mode 100644 index 000000000..99f1dacf3 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/IAIClientFactory.cs @@ -0,0 +1,13 @@ +namespace AA98_AvlnCodeStudio.Base.AI.Services; + +/// +/// Creates provider-agnostic AI client instances. +/// +public interface IAIClientFactory +{ + /// + /// Creates an AI client instance. + /// + /// The created client. + IAIClient Create(); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj new file mode 100644 index 000000000..2a515800a --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/Services/ITextDocumentStorageService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/Services/ITextDocumentStorageService.cs new file mode 100644 index 000000000..424e1c21b --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/Services/ITextDocumentStorageService.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace AA98_AvlnCodeStudio.Base.OS.Services; + +/// +/// Defines asynchronous persistence operations for plain text documents. +/// +public interface ITextDocumentStorageService +{ + /// + /// Reads the complete content of a text file. + /// + /// The absolute path of the file to read. + /// The cancellation token. + /// The loaded text content. + Task ReadAllTextAsync(string filePath, CancellationToken cancellationToken = default); + + /// + /// Writes the complete content of a text file. + /// + /// The absolute path of the file to write. + /// The content to persist. + /// The cancellation token. + /// A task that completes when the file was written. + Task SaveAllTextAsync(string filePath, string content, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj new file mode 100644 index 000000000..2a515800a --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/Services/IEditorFileDialogService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/Services/IEditorFileDialogService.cs new file mode 100644 index 000000000..ef7832bb9 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/Services/IEditorFileDialogService.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace AA98_AvlnCodeStudio.Base.UI.Services; + +/// +/// Defines UI-agnostic file picker operations for the editor workflow. +/// +public interface IEditorFileDialogService +{ + /// + /// Opens a file picker for selecting an existing text file. + /// + /// The optional initial directory. + /// The cancellation token. + /// The selected file path, or when the dialog was canceled. + Task ShowOpenFileDialogAsync(string? initialDirectory, CancellationToken cancellationToken = default); + + /// + /// Opens a file picker for selecting the output path of a text file. + /// + /// The optional initial directory. + /// The optional initial file name. + /// The cancellation token. + /// The selected file path, or when the dialog was canceled. + Task ShowSaveFileDialogAsync(string? initialDirectory, string? initialFileName, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/AA98_AvlnCodeStudio.Editor.Tests.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/AA98_AvlnCodeStudio.Editor.Tests.csproj new file mode 100644 index 000000000..7b6e54a23 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/AA98_AvlnCodeStudio.Editor.Tests.csproj @@ -0,0 +1,35 @@ + + + + net8.0 + true + false + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Documents/FileEditorDocumentFactoryTests.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Documents/FileEditorDocumentFactoryTests.cs new file mode 100644 index 000000000..68172ee70 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Documents/FileEditorDocumentFactoryTests.cs @@ -0,0 +1,28 @@ +using AA98_AvlnCodeStudio.Model.Documents; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AA98_AvlnCodeStudio.Editor.Tests.Documents; + +/// +/// Verifies the default document factory for the editor component. +/// +[TestClass] +public class FileEditorDocumentFactoryTests +{ + /// + /// Verifies that the factory creates a fresh document instance each time. + /// + [TestMethod] + public void Create_ReturnsFreshDocumentInstance() + { + var factory = new FileEditorDocumentFactory(); + + var first = factory.Create(); + var second = factory.Create(); + + Assert.IsNotNull(first); + Assert.IsNotNull(second); + Assert.AreNotSame(first, second); + Assert.AreEqual("Untitled.txt", first.DisplayName); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Services/EditorWorkflowTests.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Services/EditorWorkflowTests.cs new file mode 100644 index 000000000..389d071f2 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor.Tests/Services/EditorWorkflowTests.cs @@ -0,0 +1,120 @@ +using AA98_AvlnCodeStudio.Base.OS.Services; +using AA98_AvlnCodeStudio.Base.UI.Services; +using AA98_AvlnCodeStudio.Editor.Services; +using AA98_AvlnCodeStudio.Model.Documents; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; +using System.Threading.Tasks; + +namespace AA98_AvlnCodeStudio.Editor.Tests.Services; + +/// +/// Verifies the UI-agnostic editor workflow behavior. +/// +[TestClass] +public class EditorWorkflowTests +{ + /// + /// Verifies that a new document resets the state and reports completion. + /// + [TestMethod] + public async Task NewDocumentAsync_ResetsDocument() + { + var document = new FileEditorDocument(); + document.Load(@"C:\Temp\sample.cs", "class C {}\n"); + var dialogService = Substitute.For(); + var storageService = Substitute.For(); + var workflow = new EditorWorkflow(document, dialogService, storageService); + + var result = await workflow.NewDocumentAsync(); + + Assert.IsTrue(result.IsCompleted); + Assert.AreEqual(EditorOperationKind.NewDocument, result.OperationKind); + Assert.AreEqual("Untitled.txt", document.DisplayName); + Assert.AreEqual(string.Empty, document.Content); + Assert.IsFalse(document.IsDirty); + } + + /// + /// Verifies that opening a document loads the selected content. + /// + [TestMethod] + public async Task OpenAsync_LoadsSelectedDocument() + { + var document = new FileEditorDocument(); + var dialogService = Substitute.For(); + var storageService = Substitute.For(); + dialogService.ShowOpenFileDialogAsync(Arg.Any(), default).Returns(@"C:\Temp\sample.md"); + storageService.ReadAllTextAsync(@"C:\Temp\sample.md", default).Returns("# sample"); + var workflow = new EditorWorkflow(document, dialogService, storageService); + + var result = await workflow.OpenAsync(); + + Assert.IsTrue(result.IsCompleted); + Assert.AreEqual(EditorOperationKind.Open, result.OperationKind); + Assert.AreEqual(@"C:\Temp\sample.md", document.FilePath); + Assert.AreEqual("# sample", document.Content); + Assert.IsFalse(document.IsDirty); + } + + /// + /// Verifies that save uses the existing file path without prompting. + /// + [TestMethod] + public async Task SaveAsync_UsesExistingFilePath() + { + var document = new FileEditorDocument(); + document.Load(@"C:\Temp\sample.cs", "class C {}\n"); + document.UpdateContent("class C { }\n"); + var dialogService = Substitute.For(); + var storageService = Substitute.For(); + var workflow = new EditorWorkflow(document, dialogService, storageService); + + var result = await workflow.SaveAsync(); + + Assert.IsTrue(result.IsCompleted); + Assert.AreEqual(EditorOperationKind.Save, result.OperationKind); + await storageService.Received(1).SaveAllTextAsync(@"C:\Temp\sample.cs", "class C { }\n", default); + Assert.IsFalse(document.IsDirty); + } + + /// + /// Verifies that save as prompts for a file path and persists the document. + /// + [TestMethod] + public async Task SaveAsAsync_PromptsAndSavesDocument() + { + var document = new FileEditorDocument(); + document.UpdateContent("notes"); + var dialogService = Substitute.For(); + var storageService = Substitute.For(); + dialogService.ShowSaveFileDialogAsync(Arg.Any(), "Untitled.txt", default).Returns(@"C:\Temp\notes.txt"); + var workflow = new EditorWorkflow(document, dialogService, storageService); + + var result = await workflow.SaveAsAsync(); + + Assert.IsTrue(result.IsCompleted); + Assert.AreEqual(EditorOperationKind.SaveAs, result.OperationKind); + Assert.AreEqual(@"C:\Temp\notes.txt", document.FilePath); + await storageService.Received(1).SaveAllTextAsync(@"C:\Temp\notes.txt", "notes", default); + } + + /// + /// Verifies that canceling open returns a canceled result. + /// + [TestMethod] + public async Task OpenAsync_ReturnsCanceledWhenDialogReturnsNoPath() + { + var document = new FileEditorDocument(); + var dialogService = Substitute.For(); + var storageService = Substitute.For(); + dialogService.ShowOpenFileDialogAsync(Arg.Any(), default).Returns((string?)null); + var workflow = new EditorWorkflow(document, dialogService, storageService); + + var result = await workflow.OpenAsync(); + + Assert.IsFalse(result.IsCompleted); + Assert.AreEqual(EditorOperationStatus.Canceled, result.Status); + await storageService.DidNotReceive().ReadAllTextAsync(Arg.Any(), default); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/AA98_AvlnCodeStudio.Editor.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/AA98_AvlnCodeStudio.Editor.csproj new file mode 100644 index 000000000..51deb011a --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/AA98_AvlnCodeStudio.Editor.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponent.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponent.cs new file mode 100644 index 000000000..aec4760d8 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponent.cs @@ -0,0 +1,14 @@ +using AA98_AvlnCodeStudio.Editor.Services; + +namespace AA98_AvlnCodeStudio.Editor.Components; + +/// +/// Represents a composed editor component with a UI-agnostic workflow core. +/// +public interface IEditorComponent +{ + /// + /// Gets the editor workflow core. + /// + IEditorWorkflow Workflow { get; } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponentFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponentFactory.cs new file mode 100644 index 000000000..c043fcfd7 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Components/IEditorComponentFactory.cs @@ -0,0 +1,13 @@ +namespace AA98_AvlnCodeStudio.Editor.Components; + +/// +/// Creates composed editor component instances. +/// +public interface IEditorComponentFactory +{ + /// + /// Creates a new editor component instance. + /// + /// The composed editor component. + IEditorComponent Create(); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationKind.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationKind.cs new file mode 100644 index 000000000..499f7ba8d --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationKind.cs @@ -0,0 +1,32 @@ +namespace AA98_AvlnCodeStudio.Editor.Services; + +/// +/// Identifies the workflow operation that produced a result. +/// +public enum EditorOperationKind +{ + /// + /// No workflow action was performed. + /// + None, + + /// + /// A new document was created. + /// + NewDocument, + + /// + /// A document was opened. + /// + Open, + + /// + /// A document was saved. + /// + Save, + + /// + /// A document was saved under a new path. + /// + SaveAs, +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationResult.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationResult.cs new file mode 100644 index 000000000..6736e776b --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationResult.cs @@ -0,0 +1,75 @@ +namespace AA98_AvlnCodeStudio.Editor.Services; + +/// +/// Describes the outcome of a UI-agnostic editor workflow operation. +/// +public sealed class EditorOperationResult +{ + /// + /// Initializes a new instance of the class. + /// + /// The executed operation kind. + /// The operation status. + /// The resulting document display name. + /// The resulting file path, if any. + public EditorOperationResult( + EditorOperationKind operationKind, + EditorOperationStatus status, + string? documentDisplayName, + string? filePath) + { + OperationKind = operationKind; + Status = status; + DocumentDisplayName = documentDisplayName; + FilePath = filePath; + } + + /// + /// Gets the executed operation kind. + /// + public EditorOperationKind OperationKind { get; } + + /// + /// Gets the operation status. + /// + public EditorOperationStatus Status { get; } + + /// + /// Gets the resulting document display name. + /// + public string? DocumentDisplayName { get; } + + /// + /// Gets the resulting file path. + /// + public string? FilePath { get; } + + /// + /// Gets a value indicating whether the operation completed successfully. + /// + public bool IsCompleted => Status == EditorOperationStatus.Completed; + + /// + /// Creates a completed workflow result. + /// + /// The executed operation kind. + /// The resulting document display name. + /// The resulting file path. + /// The completed result. + public static EditorOperationResult Completed(EditorOperationKind operationKind, string? documentDisplayName, string? filePath) + { + return new EditorOperationResult(operationKind, EditorOperationStatus.Completed, documentDisplayName, filePath); + } + + /// + /// Creates a canceled workflow result. + /// + /// The attempted operation kind. + /// The current document display name. + /// The current file path. + /// The canceled result. + public static EditorOperationResult Canceled(EditorOperationKind operationKind, string? documentDisplayName, string? filePath) + { + return new EditorOperationResult(operationKind, EditorOperationStatus.Canceled, documentDisplayName, filePath); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationStatus.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationStatus.cs new file mode 100644 index 000000000..e28623b65 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorOperationStatus.cs @@ -0,0 +1,17 @@ +namespace AA98_AvlnCodeStudio.Editor.Services; + +/// +/// Indicates whether a workflow operation completed or was canceled. +/// +public enum EditorOperationStatus +{ + /// + /// The workflow operation completed successfully. + /// + Completed, + + /// + /// The workflow operation was canceled. + /// + Canceled, +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflow.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflow.cs new file mode 100644 index 000000000..141b07174 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflow.cs @@ -0,0 +1,106 @@ +using AA98_AvlnCodeStudio.Base.OS.Services; +using AA98_AvlnCodeStudio.Base.UI.Services; +using AA98_AvlnCodeStudio.Model.Documents; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace AA98_AvlnCodeStudio.Editor.Services; + +/// +/// Implements the UI-agnostic workflow for a single text editor component. +/// +public sealed class EditorWorkflow : IEditorWorkflow +{ + private readonly IFileEditorDocument _document; + private readonly IEditorFileDialogService _fileDialogService; + private readonly ITextDocumentStorageService _storageService; + + /// + /// Initializes a new instance of the class. + /// + /// The document state. + /// The file dialog service. + /// The storage service. + public EditorWorkflow( + IFileEditorDocument document, + IEditorFileDialogService fileDialogService, + ITextDocumentStorageService storageService) + { + _document = document; + _fileDialogService = fileDialogService; + _storageService = storageService; + } + + /// + public IFileEditorDocument Document => _document; + + /// + public void UpdateText(string? text) + { + _document.UpdateContent(text ?? string.Empty); + } + + /// + public Task NewDocumentAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + _document.Reset(); + return Task.FromResult(EditorOperationResult.Completed(EditorOperationKind.NewDocument, _document.DisplayName, _document.FilePath)); + } + + /// + public async Task OpenAsync(CancellationToken cancellationToken = default) + { + var filePath = await _fileDialogService.ShowOpenFileDialogAsync(GetCurrentDirectory(), cancellationToken).ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(filePath)) + { + return EditorOperationResult.Canceled(EditorOperationKind.Open, _document.DisplayName, _document.FilePath); + } + + var content = await _storageService.ReadAllTextAsync(filePath, cancellationToken).ConfigureAwait(false); + _document.Load(filePath, content); + return EditorOperationResult.Completed(EditorOperationKind.Open, _document.DisplayName, _document.FilePath); + } + + /// + public async Task SaveAsync(CancellationToken cancellationToken = default) + { + var filePath = _document.FilePath; + if (string.IsNullOrWhiteSpace(filePath)) + { + filePath = await _fileDialogService.ShowSaveFileDialogAsync(GetCurrentDirectory(), _document.DisplayName, cancellationToken).ConfigureAwait(false); + } + + if (string.IsNullOrWhiteSpace(filePath)) + { + return EditorOperationResult.Canceled(EditorOperationKind.Save, _document.DisplayName, _document.FilePath); + } + + await _storageService.SaveAllTextAsync(filePath, _document.Content, cancellationToken).ConfigureAwait(false); + _document.MarkSaved(filePath); + return EditorOperationResult.Completed(EditorOperationKind.Save, _document.DisplayName, _document.FilePath); + } + + /// + public async Task SaveAsAsync(CancellationToken cancellationToken = default) + { + var filePath = await _fileDialogService.ShowSaveFileDialogAsync(GetCurrentDirectory(), _document.DisplayName, cancellationToken).ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(filePath)) + { + return EditorOperationResult.Canceled(EditorOperationKind.SaveAs, _document.DisplayName, _document.FilePath); + } + + await _storageService.SaveAllTextAsync(filePath, _document.Content, cancellationToken).ConfigureAwait(false); + _document.MarkSaved(filePath); + return EditorOperationResult.Completed(EditorOperationKind.SaveAs, _document.DisplayName, _document.FilePath); + } + + private string? GetCurrentDirectory() + { + return string.IsNullOrWhiteSpace(_document.FilePath) + ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + : Path.GetDirectoryName(_document.FilePath); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflowFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflowFactory.cs new file mode 100644 index 000000000..2d5dd5b07 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/EditorWorkflowFactory.cs @@ -0,0 +1,37 @@ +using AA98_AvlnCodeStudio.Base.OS.Services; +using AA98_AvlnCodeStudio.Base.UI.Services; +using AA98_AvlnCodeStudio.Model.Documents; + +namespace AA98_AvlnCodeStudio.Editor.Services; + +/// +/// Creates default editor workflow instances. +/// +public sealed class EditorWorkflowFactory : IEditorWorkflowFactory +{ + private readonly IFileEditorDocumentFactory _documentFactory; + private readonly IEditorFileDialogService _fileDialogService; + private readonly ITextDocumentStorageService _storageService; + + /// + /// Initializes a new instance of the class. + /// + /// The document factory. + /// The file dialog service. + /// The storage service. + public EditorWorkflowFactory( + IFileEditorDocumentFactory documentFactory, + IEditorFileDialogService fileDialogService, + ITextDocumentStorageService storageService) + { + _documentFactory = documentFactory; + _fileDialogService = fileDialogService; + _storageService = storageService; + } + + /// + public IEditorWorkflow Create() + { + return new EditorWorkflow(_documentFactory.Create(), _fileDialogService, _storageService); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflow.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflow.cs new file mode 100644 index 000000000..16c582527 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflow.cs @@ -0,0 +1,50 @@ +using AA98_AvlnCodeStudio.Model.Documents; +using System.Threading; +using System.Threading.Tasks; + +namespace AA98_AvlnCodeStudio.Editor.Services; + +/// +/// Defines the UI-agnostic workflow for a single text editor component. +/// +public interface IEditorWorkflow +{ + /// + /// Gets the underlying document state. + /// + IFileEditorDocument Document { get; } + + /// + /// Updates the current editor text. + /// + /// The new text value. + void UpdateText(string? text); + + /// + /// Creates a new empty document. + /// + /// The cancellation token. + /// The workflow result. + Task NewDocumentAsync(CancellationToken cancellationToken = default); + + /// + /// Opens an existing text document. + /// + /// The cancellation token. + /// The workflow result. + Task OpenAsync(CancellationToken cancellationToken = default); + + /// + /// Saves the current document. + /// + /// The cancellation token. + /// The workflow result. + Task SaveAsync(CancellationToken cancellationToken = default); + + /// + /// Saves the current document under a new file path. + /// + /// The cancellation token. + /// The workflow result. + Task SaveAsAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflowFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflowFactory.cs new file mode 100644 index 000000000..155183af1 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Editor/Services/IEditorWorkflowFactory.cs @@ -0,0 +1,13 @@ +namespace AA98_AvlnCodeStudio.Editor.Services; + +/// +/// Creates editor workflow instances for composed editor components. +/// +public interface IEditorWorkflowFactory +{ + /// + /// Creates a new editor workflow instance. + /// + /// The created workflow. + IEditorWorkflow Create(); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocumentFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocumentFactory.cs new file mode 100644 index 000000000..3098529ec --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocumentFactory.cs @@ -0,0 +1,13 @@ +namespace AA98_AvlnCodeStudio.Model.Documents; + +/// +/// Creates the default plain text editor document implementation. +/// +public sealed class FileEditorDocumentFactory : IFileEditorDocumentFactory +{ + /// + public IFileEditorDocument Create() + { + return new FileEditorDocument(); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocument.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocument.cs new file mode 100644 index 000000000..baddf9f08 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocument.cs @@ -0,0 +1,51 @@ +namespace AA98_AvlnCodeStudio.Model.Documents; + +/// +/// Defines the editable state contract for a single plain text document. +/// +public interface IFileEditorDocument +{ + /// + /// Gets the current file path. + /// + string? FilePath { get; } + + /// + /// Gets the current document content. + /// + string Content { get; } + + /// + /// Gets a value indicating whether the document has unsaved changes. + /// + bool IsDirty { get; } + + /// + /// Gets the display name that should be shown for the document. + /// + string DisplayName { get; } + + /// + /// Resets the document to a new empty state. + /// + void Reset(); + + /// + /// Loads a persisted text document into the current state. + /// + /// The source file path. + /// The loaded text content. + void Load(string filePath, string content); + + /// + /// Updates the current document content. + /// + /// The new document content. + void UpdateContent(string content); + + /// + /// Marks the document as saved. + /// + /// The persisted file path. + void MarkSaved(string filePath); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocumentFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocumentFactory.cs new file mode 100644 index 000000000..9d6a5d0d9 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/IFileEditorDocumentFactory.cs @@ -0,0 +1,13 @@ +namespace AA98_AvlnCodeStudio.Model.Documents; + +/// +/// Creates editor document instances for a component workflow. +/// +public interface IFileEditorDocumentFactory +{ + /// + /// Creates a new editor document instance. + /// + /// A new document instance. + IFileEditorDocument Create(); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponent.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponent.cs new file mode 100644 index 000000000..85d8caa24 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponent.cs @@ -0,0 +1,32 @@ +using AA98_AvlnCodeStudio.Editor.Services; +using Avalonia.Controls; + +namespace AA98_AvlnCodeStudio.UI.Components; + +/// +/// Stores the composed Avalonia editor component parts. +/// +public sealed class AvaloniaEditorComponent : IAvaloniaEditorComponent +{ + /// + /// Initializes a new instance of the class. + /// + /// The editor workflow. + /// The editor view model. + /// The editor view. + public AvaloniaEditorComponent(IEditorWorkflow workflow, ViewModels.EditorViewModel editorViewModel, Control view) + { + Workflow = workflow; + EditorViewModel = editorViewModel; + View = view; + } + + /// + public IEditorWorkflow Workflow { get; } + + /// + public ViewModels.EditorViewModel EditorViewModel { get; } + + /// + public Control View { get; } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponentFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponentFactory.cs new file mode 100644 index 000000000..b32a08105 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/AvaloniaEditorComponentFactory.cs @@ -0,0 +1,45 @@ +using AA98_AvlnCodeStudio.Editor.Components; +using AA98_AvlnCodeStudio.Editor.Services; + +namespace AA98_AvlnCodeStudio.UI.Components; + +/// +/// Creates the first explicit Avalonia editor component. +/// +public sealed class AvaloniaEditorComponentFactory : IAvaloniaEditorComponentFactory, IEditorComponentFactory +{ + private readonly Services.IEditorViewFactory _editorViewFactory; + private readonly ViewModels.IEditorViewModelFactory _editorViewModelFactory; + private readonly IEditorWorkflowFactory _workflowFactory; + + /// + /// Initializes a new instance of the class. + /// + /// The workflow factory. + /// The editor view model factory. + /// The editor view factory. + public AvaloniaEditorComponentFactory( + IEditorWorkflowFactory workflowFactory, + ViewModels.IEditorViewModelFactory editorViewModelFactory, + Services.IEditorViewFactory editorViewFactory) + { + _workflowFactory = workflowFactory; + _editorViewModelFactory = editorViewModelFactory; + _editorViewFactory = editorViewFactory; + } + + /// + public IAvaloniaEditorComponent Create() + { + var workflow = _workflowFactory.Create(); + var editorViewModel = _editorViewModelFactory.Create(workflow); + var view = _editorViewFactory.Create(editorViewModel); + return new AvaloniaEditorComponent(workflow, editorViewModel, view); + } + + /// + IEditorComponent IEditorComponentFactory.Create() + { + return Create(); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponent.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponent.cs new file mode 100644 index 000000000..a97eaf231 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponent.cs @@ -0,0 +1,20 @@ +using AA98_AvlnCodeStudio.Editor.Components; +using Avalonia.Controls; + +namespace AA98_AvlnCodeStudio.UI.Components; + +/// +/// Represents the Avalonia-hosted editor component. +/// +public interface IAvaloniaEditorComponent : IEditorComponent +{ + /// + /// Gets the editor view model. + /// + ViewModels.EditorViewModel EditorViewModel { get; } + + /// + /// Gets the Avalonia view for the component. + /// + Control View { get; } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponentFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponentFactory.cs new file mode 100644 index 000000000..2fb3e09c9 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Components/IAvaloniaEditorComponentFactory.cs @@ -0,0 +1,15 @@ +using AA98_AvlnCodeStudio.Editor.Components; + +namespace AA98_AvlnCodeStudio.UI.Components; + +/// +/// Creates Avalonia-hosted editor component instances. +/// +public interface IAvaloniaEditorComponentFactory : IEditorComponentFactory +{ + /// + /// Creates the editor component. + /// + /// The created editor component. + IAvaloniaEditorComponent Create(); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorViewFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorViewFactory.cs new file mode 100644 index 000000000..abfb4123d --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorViewFactory.cs @@ -0,0 +1,29 @@ +using AA98_AvlnCodeStudio.UI.Controls; +using Avalonia.Controls; +using Avalonia.Data; + +namespace AA98_AvlnCodeStudio.UI.Services; + +/// +/// Creates Avalonia editor views and binds them to editor view models. +/// +public sealed class AvaloniaEditorViewFactory : IEditorViewFactory +{ + /// + public Control Create(ViewModels.EditorViewModel editorViewModel) + { + var editorView = new EditorTextArea + { + DataContext = editorViewModel, + HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch, + VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch, + }; + + editorView.Bind(EditorTextArea.TextProperty, new Binding(nameof(ViewModels.EditorViewModel.Text)) + { + Mode = BindingMode.TwoWay, + }); + + return editorView; + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/IEditorViewFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/IEditorViewFactory.cs new file mode 100644 index 000000000..5b840253c --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/IEditorViewFactory.cs @@ -0,0 +1,16 @@ +using Avalonia.Controls; + +namespace AA98_AvlnCodeStudio.UI.Services; + +/// +/// Creates Avalonia editor views for editor components. +/// +public interface IEditorViewFactory +{ + /// + /// Creates an editor view for the specified view model. + /// + /// The editor view model. + /// The created view. + Control Create(ViewModels.EditorViewModel editorViewModel); +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModelFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModelFactory.cs new file mode 100644 index 000000000..d1b6d396b --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModelFactory.cs @@ -0,0 +1,15 @@ +using AA98_AvlnCodeStudio.Editor.Services; + +namespace AA98_AvlnCodeStudio.UI.ViewModels; + +/// +/// Creates editor view models around a workflow core. +/// +public sealed class EditorViewModelFactory : IEditorViewModelFactory +{ + /// + public EditorViewModel Create(IEditorWorkflow workflow) + { + return new EditorViewModel(workflow); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/IEditorViewModelFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/IEditorViewModelFactory.cs new file mode 100644 index 000000000..22127beda --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/IEditorViewModelFactory.cs @@ -0,0 +1,16 @@ +using AA98_AvlnCodeStudio.Editor.Services; + +namespace AA98_AvlnCodeStudio.UI.ViewModels; + +/// +/// Creates editor view model instances for a workflow. +/// +public interface IEditorViewModelFactory +{ + /// + /// Creates an editor view model for the specified workflow. + /// + /// The editor workflow. + /// The created editor view model. + EditorViewModel Create(IEditorWorkflow workflow); +} \ No newline at end of file From 2e88764b38a4c6b9d3bdd984fa884a66435f90f5 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:01 +0200 Subject: [PATCH 006/106] AA05_CommandParCalc.Browser --- .../AA05_CommandParCalc.Browser.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Browser/AA05_CommandParCalc.Browser.csproj b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Browser/AA05_CommandParCalc.Browser.csproj index cdab5ea0b..9c14c0cd1 100644 --- a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Browser/AA05_CommandParCalc.Browser.csproj +++ b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Browser/AA05_CommandParCalc.Browser.csproj @@ -5,6 +5,7 @@ Exe true enable + Debug;Release;DesktopOnly From 340d9a6c25899ea56572a29149467d4c30adebae Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:01 +0200 Subject: [PATCH 007/106] AA05_CommandParCalc.Desktop --- .../AA05_CommandParCalc.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Desktop/AA05_CommandParCalc.Desktop.csproj b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Desktop/AA05_CommandParCalc.Desktop.csproj index e18da5b4c..9e29d2e38 100644 --- a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Desktop/AA05_CommandParCalc.Desktop.csproj +++ b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalc.Desktop/AA05_CommandParCalc.Desktop.csproj @@ -5,6 +5,7 @@ net8.0 true app.manifest + Debug;Release;DesktopOnly From 8f23317a0d8c2d4d5c6481a3e9c566794acbf30c Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:02 +0200 Subject: [PATCH 008/106] AA05_CommandParCalcTests --- .../AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj index a1dee2e91..9ceefa487 100644 --- a/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj +++ b/Avalonia_Apps/AA05_CommandParCalc/AA05_CommandParCalcTests/AA05_CommandParCalcTests.csproj @@ -4,6 +4,7 @@ net8.0 Library enable + Debug;Release;DesktopOnly From 00deeb137d3424ca6ac57be8e565d055dd3a5f78 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:02 +0200 Subject: [PATCH 009/106] AA06_Converters4 --- .../AA06_Converters4/AA06_Converters4.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj index 29a1da33f..a6245a00a 100644 --- a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj +++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4/AA06_Converters4.csproj @@ -6,6 +6,7 @@ enable true true + Debug;Release;DesktopOnly From 34c2826e4c7a9e4dcfa78531dd68ca671aea44b4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:02 +0200 Subject: [PATCH 010/106] AA06_Converters4Tests --- .../AA06_Converters4Tests/AA06_Converters4Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj index c8d62a3c0..fdfcc0311 100644 --- a/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj +++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_Converters4Tests/AA06_Converters4Tests.csproj @@ -3,6 +3,7 @@ net8.0 false + Debug;Release;DesktopOnly From 36885758b7f3af22e4b74f841ee6f964ae3396d9 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:03 +0200 Subject: [PATCH 011/106] AA06_ValueConverter2 --- .../AA06_ValueConverter2/AA06_ValueConverter2.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj index 8c3668546..6f02f3b73 100644 --- a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj +++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2/AA06_ValueConverter2.csproj @@ -6,6 +6,7 @@ true app.manifest true + Debug;Release;DesktopOnly From 4f52f704e12893f3ad715b963998aebb7b093d03 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:03 +0200 Subject: [PATCH 012/106] AA06_ValueConverter2Tests --- .../AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj index 23873033a..0c8b23b07 100644 --- a/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj +++ b/Avalonia_Apps/AA06_ValueConverter2/AA06_ValueConverter2Tests/AA06_ValueConverter2Tests.csproj @@ -4,6 +4,7 @@ net8.0 Library enable + Debug;Release;DesktopOnly From d64194cf0187dcb50e8a430e91a82da17a6a21cc Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:04 +0200 Subject: [PATCH 013/106] AA09_DialogBoxes --- .../AA09_DialogBoxes/AA09_DialogBoxes.csproj | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj index e117a1b32..14a12640d 100644 --- a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj +++ b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes/AA09_DialogBoxes.csproj @@ -3,18 +3,19 @@ net8.0 true + Debug;Release;DesktopOnly - - $(TargetFrameworks);net9.0 - - - $(TargetFrameworks);net10.0 - - - $(TargetFrameworks);net11.0 - + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + $(TargetFrameworks);net11.0 + $(TargetFrameworks);net11.0 @@ -46,7 +47,7 @@ - + @@ -55,19 +56,19 @@ Resources.resx - DialogView.axaml + DialogView.axaml - MainWindow.axaml + MainWindow.axaml - DialogWindow.axaml + DialogWindow.axaml - OverlayDialogControl.axaml + OverlayDialogControl.axaml - App.axaml + App.axaml @@ -76,5 +77,5 @@ Resources.Designer.cs - + From a4982524f7f1efb70df797eb30cd7e01a1ad0999 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:04 +0200 Subject: [PATCH 014/106] AA09_DialogBoxes.Browser --- .../AA09_DialogBoxes.Browser/AA09_DialogBoxes.Browser.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Browser/AA09_DialogBoxes.Browser.csproj b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Browser/AA09_DialogBoxes.Browser.csproj index c8d8f3394..6145dae03 100644 --- a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Browser/AA09_DialogBoxes.Browser.csproj +++ b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Browser/AA09_DialogBoxes.Browser.csproj @@ -5,6 +5,7 @@ Exe true enable + Debug;Release;DesktopOnly From d533ab8ba1c7d59c92bc59fc720066a91631ff01 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:05 +0200 Subject: [PATCH 015/106] AA09_DialogBoxes.Desktop --- .../AA09_DialogBoxes.Desktop/AA09_DialogBoxes.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Desktop/AA09_DialogBoxes.Desktop.csproj b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Desktop/AA09_DialogBoxes.Desktop.csproj index d1afb703c..3b5a04f6c 100644 --- a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Desktop/AA09_DialogBoxes.Desktop.csproj +++ b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxes.Desktop/AA09_DialogBoxes.Desktop.csproj @@ -5,6 +5,7 @@ net8.0 true app.manifest + Debug;Release;DesktopOnly From a224ffc35047a98336f78a80d098fc82656d8fc1 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:05 +0200 Subject: [PATCH 016/106] AA09_DialogBoxesTests --- .../AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj index 633f8e528..11a3f472e 100644 --- a/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj +++ b/Avalonia_Apps/AA09_DialogBoxes/AA09_DialogBoxesTests/AA09_DialogBoxes_Tests.csproj @@ -4,6 +4,7 @@ net8.0 true false + Debug;Release;DesktopOnly From 1e27dddff31d8675e31294fc0206645e1e2da100 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:05 +0200 Subject: [PATCH 017/106] AA14_ScreenX --- Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj b/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj index 87fc8753a..8580e6b87 100644 --- a/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj +++ b/Avalonia_Apps/AA14_ScreenX/AA14_ScreenX/AA14_ScreenX.csproj @@ -6,6 +6,7 @@ true enable enable + Debug;Release;DesktopOnly From 77b493a9dc717767c8fcacd23826b76cfa19831f Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:06 +0200 Subject: [PATCH 018/106] ScreenX.Base --- Avalonia_Apps/AA14_ScreenX/ScreenX.Base/ScreenX.Base.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA14_ScreenX/ScreenX.Base/ScreenX.Base.csproj b/Avalonia_Apps/AA14_ScreenX/ScreenX.Base/ScreenX.Base.csproj index e5cfffd53..931aed47a 100644 --- a/Avalonia_Apps/AA14_ScreenX/ScreenX.Base/ScreenX.Base.csproj +++ b/Avalonia_Apps/AA14_ScreenX/ScreenX.Base/ScreenX.Base.csproj @@ -4,6 +4,7 @@ net8.0;net9.0 enable enable + Debug;Release;DesktopOnly From d7393c96f23c0de70eccfc37b5627f1fd84c3967 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:06 +0200 Subject: [PATCH 019/106] ScreenX.BaseTests --- .../AA14_ScreenX/ScreenX.BaseTests/ScreenX.BaseTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA14_ScreenX/ScreenX.BaseTests/ScreenX.BaseTests.csproj b/Avalonia_Apps/AA14_ScreenX/ScreenX.BaseTests/ScreenX.BaseTests.csproj index 551b009ea..ed4d45e71 100644 --- a/Avalonia_Apps/AA14_ScreenX/ScreenX.BaseTests/ScreenX.BaseTests.csproj +++ b/Avalonia_Apps/AA14_ScreenX/ScreenX.BaseTests/ScreenX.BaseTests.csproj @@ -7,6 +7,7 @@ false true + Debug;Release;DesktopOnly From 831210d31df6bf0d23117d481b04a56f9a961a2a Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:07 +0200 Subject: [PATCH 020/106] AA15a_Treppen --- Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj b/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj index b7b2b8004..06c29f715 100644 --- a/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj +++ b/Avalonia_Apps/AA15_Labyrinth/AA15a_Treppen/AA15a_Treppen.csproj @@ -7,6 +7,7 @@ true app.manifest true + Debug;Release;DesktopOnly From bc546322601a319745cfe96e98e3238a073f0452 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:07 +0200 Subject: [PATCH 021/106] AA15_Labyrinth --- .../AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj b/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj index 050f76933..e0a23d683 100644 --- a/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj +++ b/Avalonia_Apps/AA15_Labyrinth/AA15_Labyrinth/AA15_Labyrinth.csproj @@ -4,6 +4,7 @@ WinExe net8.0;net9.0 true + Debug;Release;DesktopOnly From 473fe83ba6bbc7f6049129c02a9bd713c1c7debe Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:08 +0200 Subject: [PATCH 022/106] AA15_LabyrinthTests --- .../AA15_LabyrinthTests/AA15_LabyrinthTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj b/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj index 8e805a6f0..4398c9078 100644 --- a/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj +++ b/Avalonia_Apps/AA15_Labyrinth/AA15_LabyrinthTests/AA15_LabyrinthTests.csproj @@ -4,6 +4,7 @@ net8.0;net9.0 Library enable + Debug;Release;DesktopOnly From 7c56d3b346b31f194e80596aec3e1b4d698728d1 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:08 +0200 Subject: [PATCH 023/106] Treppen.Base --- Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj b/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj index 087d951ae..116ba9dc1 100644 --- a/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj +++ b/Avalonia_Apps/AA15_Labyrinth/Treppen.Base/Treppen.Base.csproj @@ -6,6 +6,7 @@ enable true Treppen.Base + Debug;Release;DesktopOnly From b998fc2481b59519efcf60cfab4caf9dc6bf2b8e Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:09 +0200 Subject: [PATCH 024/106] Treppen.BaseTests --- .../AA15_Labyrinth/Treppen.BaseTests/Treppen.BaseTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA15_Labyrinth/Treppen.BaseTests/Treppen.BaseTests.csproj b/Avalonia_Apps/AA15_Labyrinth/Treppen.BaseTests/Treppen.BaseTests.csproj index 397526083..f526406cc 100644 --- a/Avalonia_Apps/AA15_Labyrinth/Treppen.BaseTests/Treppen.BaseTests.csproj +++ b/Avalonia_Apps/AA15_Labyrinth/Treppen.BaseTests/Treppen.BaseTests.csproj @@ -3,6 +3,7 @@ net8.0;net9.0 false + Debug;Release;DesktopOnly From 99fe585f67de2e48ef06119fbaa62c6d53a34ee3 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:09 +0200 Subject: [PATCH 025/106] AA16_Usercontrol1 --- .../AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj index 31a5f7f0b..e84a1fc3e 100644 --- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj +++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol1/AA16_UserControl1.csproj @@ -7,6 +7,7 @@ enable true AA16_UserControl1 + Debug;Release;DesktopOnly @@ -23,6 +24,7 @@ + From 4d9bb88da46b65c5dbbcb41b1cc9a41800d115c4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:09 +0200 Subject: [PATCH 026/106] AA16_UserControl1Tests --- .../AA16_UserControl1Tests/AA16_UserControl1_Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/AA16_UserControl1_Tests.csproj b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/AA16_UserControl1_Tests.csproj index 7bcdd167e..59faf181a 100644 --- a/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/AA16_UserControl1_Tests.csproj +++ b/Avalonia_Apps/AA16_UserControl/AA16_UserControl1Tests/AA16_UserControl1_Tests.csproj @@ -4,6 +4,7 @@ net8.0;net9.0 true false + Debug;Release;DesktopOnly From bba56c0c059b5f43e40f110807670a3578f8657a Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:10 +0200 Subject: [PATCH 027/106] AA16_Usercontrol2 --- .../AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj index 7dc0853ec..ac9223384 100644 --- a/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj +++ b/Avalonia_Apps/AA16_UserControl/AA16_Usercontrol2/AA16_UserControl2.csproj @@ -6,6 +6,7 @@ enable true AA16_UserControl2 + Debug;Release;DesktopOnly @@ -30,6 +31,7 @@ + From ccb41a0b32983406353ca0a340ecc2280123d253 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:10 +0200 Subject: [PATCH 028/106] AA18_MultiConverters --- .../AA18_MultiConverters/AA18_MultiConverters.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj index 0d373bb9a..a8dfe3892 100644 --- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj +++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConverters/AA18_MultiConverters.csproj @@ -8,6 +8,7 @@ enable true AA18_MultiConverter + Debug;Release;DesktopOnly @@ -24,5 +25,6 @@ + From 9e1e37d761c16aa46384a03e6816ccffafb64426 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:11 +0200 Subject: [PATCH 029/106] AA18_MultiConvertersTests --- .../AA18_MultiConvertersTests/AA18_MultiConvertersTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/AA18_MultiConvertersTests.csproj b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/AA18_MultiConvertersTests.csproj index bdc8c6fc1..737238661 100644 --- a/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/AA18_MultiConvertersTests.csproj +++ b/Avalonia_Apps/AA18_MultiConverter/AA18_MultiConvertersTests/AA18_MultiConvertersTests.csproj @@ -3,6 +3,7 @@ net8.0;net9.0 false + Debug;Release;DesktopOnly From 24a43a5356b22c79651498b4ebb3e655090f9bf9 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:11 +0200 Subject: [PATCH 030/106] AA19_FilterLists --- .../AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj b/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj index 125e1e8fd..5c0162fa5 100644 --- a/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj +++ b/Avalonia_Apps/AA19_FilterLists/AA19_FilterLists/AA19_FilterLists.csproj @@ -8,6 +8,7 @@ enable true AA19_FilterLists + Debug;Release;DesktopOnly @@ -59,5 +60,6 @@ + From c711262649a306593957d6b5e67fe43e81becc1c Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:11 +0200 Subject: [PATCH 031/106] AA19_FilterListsTests --- .../AA19_FilterListsTests/AA19_FilterListsTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA19_FilterLists/AA19_FilterListsTests/AA19_FilterListsTests.csproj b/Avalonia_Apps/AA19_FilterLists/AA19_FilterListsTests/AA19_FilterListsTests.csproj index 9643beda8..ea3c95990 100644 --- a/Avalonia_Apps/AA19_FilterLists/AA19_FilterListsTests/AA19_FilterListsTests.csproj +++ b/Avalonia_Apps/AA19_FilterLists/AA19_FilterListsTests/AA19_FilterListsTests.csproj @@ -3,6 +3,7 @@ net8.0-windows;net9.0-windows true + Debug;Release;DesktopOnly From 838a9db83a5ed34c337dd910e71f5390ae05ba62 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:12 +0200 Subject: [PATCH 032/106] AA20a_CommonDialogs --- .../AA20a_CommonDialogs/AA20a_CommonDialogs.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA20_SysDialogs/AA20a_CommonDialogs/AA20a_CommonDialogs.csproj b/Avalonia_Apps/AA20_SysDialogs/AA20a_CommonDialogs/AA20a_CommonDialogs.csproj index 233aa7689..10bdb82c6 100644 --- a/Avalonia_Apps/AA20_SysDialogs/AA20a_CommonDialogs/AA20a_CommonDialogs.csproj +++ b/Avalonia_Apps/AA20_SysDialogs/AA20a_CommonDialogs/AA20a_CommonDialogs.csproj @@ -8,6 +8,7 @@ enable true AA20a_CommonDialogs + Debug;Release;DesktopOnly @@ -33,6 +34,7 @@ + From 526298f7f1c30a23defd24aef35de2980ae5840c Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:12 +0200 Subject: [PATCH 033/106] AA20_Sysdialogs --- .../AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj index 6af83957a..e43baca6a 100644 --- a/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj +++ b/Avalonia_Apps/AA20_SysDialogs/AA20_Sysdialogs/AA20_Sysdialogs.csproj @@ -8,6 +8,7 @@ enable true AA20_SysDialogs + Debug;Release;DesktopOnly @@ -36,6 +37,7 @@ + From cdd5a0489e673813a6713fe838b4e27e055ebeb6 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:13 +0200 Subject: [PATCH 034/106] AA20_SysdialogsTests --- .../AA20_SysdialogsTests/AA20_SysdialogsTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj b/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj index 03c23ccaa..d778f3576 100644 --- a/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj +++ b/Avalonia_Apps/AA20_SysDialogs/AA20_SysdialogsTests/AA20_SysdialogsTests.csproj @@ -3,6 +3,7 @@ net8.0 false + Debug;Release;DesktopOnly From 128fa395ca51a21c09e059fbedc73138c0c68867 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:13 +0200 Subject: [PATCH 035/106] AA21_Buttons --- Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj index a8feebbf0..0a6629e48 100644 --- a/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj +++ b/Avalonia_Apps/AA21_Buttons/AA21_Buttons/AA21_Buttons.csproj @@ -6,6 +6,7 @@ enable latest true + Debug;Release;DesktopOnly From 3ff10caec560650ceb6529704aca32fed4e2904d Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:14 +0200 Subject: [PATCH 036/106] AA22_AvlnCap --- Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj index 57db888f6..3b32b855e 100644 --- a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj +++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap/AA22_AvlnCap.csproj @@ -6,6 +6,7 @@ true app.manifest true + Debug;Release;DesktopOnly @@ -31,6 +32,7 @@ + From bb7d925abd6c7f15ded3e57b72cda543af890f5b Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:14 +0200 Subject: [PATCH 037/106] AA22_AvlnCap2 --- Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap2/AA22_AvlnCap2.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap2/AA22_AvlnCap2.csproj b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap2/AA22_AvlnCap2.csproj index 88101bfc0..6c045db7e 100644 --- a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap2/AA22_AvlnCap2.csproj +++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCap2/AA22_AvlnCap2.csproj @@ -6,6 +6,7 @@ true app.manifest true + Debug;Release;DesktopOnly From b0356a8bf2fcea68d6f7b2a383f07cc8e4fd66ab Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:15 +0200 Subject: [PATCH 038/106] AA22_AvlnCapTests --- .../AA22_AvlnCap/AA22_AvlnCapTests/AA22_AvlnCapTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCapTests/AA22_AvlnCapTests.csproj b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCapTests/AA22_AvlnCapTests.csproj index 6309a9acf..2133184d8 100644 --- a/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCapTests/AA22_AvlnCapTests.csproj +++ b/Avalonia_Apps/AA22_AvlnCap/AA22_AvlnCapTests/AA22_AvlnCapTests.csproj @@ -2,6 +2,7 @@ net8.0 + Debug;Release;DesktopOnly From 6a40039e49996d71d8ad02524ccdff328313bf19 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:15 +0200 Subject: [PATCH 039/106] AA25_RichTextEdit --- .../AA25_RichTextEdit/AA25_RichTextEdit.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEdit/AA25_RichTextEdit.csproj b/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEdit/AA25_RichTextEdit.csproj index 8fc822a3b..42f192736 100644 --- a/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEdit/AA25_RichTextEdit.csproj +++ b/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEdit/AA25_RichTextEdit.csproj @@ -8,6 +8,7 @@ enable true AA25_RichTextEdit + Debug;Release;DesktopOnly @@ -38,6 +39,7 @@ + From ec69cf343e3fdf2cde21f5eaf270674c896e6031 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:16 +0200 Subject: [PATCH 040/106] AA25_RichTextEditTests --- .../AA25_RichTextEditTests/AA25_RichTextEditTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEditTests/AA25_RichTextEditTests.csproj b/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEditTests/AA25_RichTextEditTests.csproj index a6d9fb6f8..2b7bf6b35 100644 --- a/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEditTests/AA25_RichTextEditTests.csproj +++ b/Avalonia_Apps/AA25_RichTextEdit/AA25_RichTextEditTests/AA25_RichTextEditTests.csproj @@ -4,6 +4,7 @@ net8.0;net9.0 true false + Debug;Release;DesktopOnly From a54521ed8f49a3685036b8919936c157bc739c69 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:16 +0200 Subject: [PATCH 041/106] Avln_RichTextEdit --- .../AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj b/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj index bacba7e74..0f4abadf9 100644 --- a/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj +++ b/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj @@ -6,6 +6,7 @@ true app.manifest true + Debug;Release;DesktopOnly From 0ed68827086e56a3cded95eaece281fdba87d258 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:16 +0200 Subject: [PATCH 042/106] Document.Base --- .../AA25_RichTextEdit/Document.Base/Document.Base.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA25_RichTextEdit/Document.Base/Document.Base.csproj b/Avalonia_Apps/AA25_RichTextEdit/Document.Base/Document.Base.csproj index d404a9e6f..66e81353a 100644 --- a/Avalonia_Apps/AA25_RichTextEdit/Document.Base/Document.Base.csproj +++ b/Avalonia_Apps/AA25_RichTextEdit/Document.Base/Document.Base.csproj @@ -5,6 +5,7 @@ enable enable preview + Debug;Release;DesktopOnly From 06fb197cf52e44e7daeba9a9b9e778aea8a3d4fc Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:17 +0200 Subject: [PATCH 043/106] Document.Flow --- .../AA25_RichTextEdit/Document.Flow/Document.Flow.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA25_RichTextEdit/Document.Flow/Document.Flow.csproj b/Avalonia_Apps/AA25_RichTextEdit/Document.Flow/Document.Flow.csproj index 2bd14fb80..3936966e7 100644 --- a/Avalonia_Apps/AA25_RichTextEdit/Document.Flow/Document.Flow.csproj +++ b/Avalonia_Apps/AA25_RichTextEdit/Document.Flow/Document.Flow.csproj @@ -4,6 +4,7 @@ net10.0 enable enable + Debug;Release;DesktopOnly From 12ceb9e0027c92c1fc85b38c40f1af9210cf203b Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:17 +0200 Subject: [PATCH 044/106] AA98_AvlnCodeStudio.Base --- .../AA98_AvlnCodeStudio.Base.csproj | 4 +++ .../Services/IEditorFileDialogService.cs | 27 ------------------- .../Services/ITextDocumentStorageService.cs | 27 ------------------- 3 files changed, 4 insertions(+), 54 deletions(-) delete mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/IEditorFileDialogService.cs delete mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/ITextDocumentStorageService.cs diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj index e48052ac3..9bde2a3e2 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj @@ -3,6 +3,7 @@ net8.0 enable + Debug;Release;DesktopOnly @@ -15,4 +16,7 @@ + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/IEditorFileDialogService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/IEditorFileDialogService.cs deleted file mode 100644 index 54cf01941..000000000 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/IEditorFileDialogService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace AA98_AvlnCodeStudio.Base.Services; - -/// -/// Defines UI-agnostic file picker operations for the editor workflow. -/// -public interface IEditorFileDialogService -{ - /// - /// Opens a file picker for selecting an existing text file. - /// - /// The optional initial directory. - /// The cancellation token. - /// The selected file path, or when the dialog was canceled. - Task ShowOpenFileDialogAsync(string? initialDirectory, CancellationToken cancellationToken = default); - - /// - /// Opens a file picker for selecting the output path of a text file. - /// - /// The optional initial directory. - /// The optional initial file name. - /// The cancellation token. - /// The selected file path, or when the dialog was canceled. - Task ShowSaveFileDialogAsync(string? initialDirectory, string? initialFileName, CancellationToken cancellationToken = default); -} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/ITextDocumentStorageService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/ITextDocumentStorageService.cs deleted file mode 100644 index 4a6a70694..000000000 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/ITextDocumentStorageService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; - -namespace AA98_AvlnCodeStudio.Base.Services; - -/// -/// Defines asynchronous persistence operations for plain text documents. -/// -public interface ITextDocumentStorageService -{ - /// - /// Reads the complete content of a text file. - /// - /// The absolute path of the file to read. - /// The cancellation token. - /// The loaded text content. - Task ReadAllTextAsync(string filePath, CancellationToken cancellationToken = default); - - /// - /// Writes the complete content of a text file. - /// - /// The absolute path of the file to write. - /// The content to persist. - /// The cancellation token. - /// A task that completes when the file was written. - Task SaveAllTextAsync(string filePath, string content, CancellationToken cancellationToken = default); -} From b152c38ef6f676954e90eaaeeb015bc1b330dba9 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:21 +0200 Subject: [PATCH 045/106] AA98_AvlnCodeStudio.Model --- .../AA98_AvlnCodeStudio.Model/AA98_AvlnCodeStudio.Model.csproj | 1 + .../AA98_AvlnCodeStudio.Model/Documents/FileEditorDocument.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/AA98_AvlnCodeStudio.Model.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/AA98_AvlnCodeStudio.Model.csproj index e98d24561..78c8d631c 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/AA98_AvlnCodeStudio.Model.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/AA98_AvlnCodeStudio.Model.csproj @@ -3,6 +3,7 @@ net8.0 enable + Debug;Release;DesktopOnly diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocument.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocument.cs index 2b5833cd9..117712d3f 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocument.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Model/Documents/FileEditorDocument.cs @@ -6,7 +6,7 @@ namespace AA98_AvlnCodeStudio.Model.Documents; /// /// Represents the editable state of a single plain text document. /// -public sealed class FileEditorDocument +public sealed class FileEditorDocument : IFileEditorDocument { private const string DefaultDocumentName = "Untitled.txt"; From e3d116c007c1f220f62907ea3300f8b15b2b40fe Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:21 +0200 Subject: [PATCH 046/106] AA98_AvlnCodeStudio.Tests --- .../AA98_AvlnCodeStudio.Tests.csproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj index c6efd1ac3..dfcaa66ff 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj @@ -1,9 +1,10 @@ - + net8.0 true false + Debug;Release;DesktopOnly @@ -29,4 +30,7 @@ + + + From 2dc06e1e4ef714953bae6f1a750ed37027813b05 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:22 +0200 Subject: [PATCH 047/106] AA98_AvlnCodeStudio.UI --- .../AA98_AvlnCodeStudio.UI.csproj | 4 + .../AA98_AvlnCodeStudio.UI/App.axaml.cs | 21 ++-- .../AvaloniaEditorFileDialogService.cs | 2 +- .../Services/DesignEditorFileDialogService.cs | 2 +- .../DesignTextDocumentStorageService.cs | 2 +- .../FileSystemTextDocumentStorageService.cs | 2 +- .../ViewModels/EditorViewModel.cs | 115 ++++++------------ .../ViewModels/MainWindowViewModel.cs | 18 ++- .../Views/MainWindow.axaml | 5 +- .../Views/MainWindow.axaml.cs | 11 ++ 10 files changed, 83 insertions(+), 99 deletions(-) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj index 00221ab2b..90da39d92 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj @@ -5,6 +5,7 @@ net8.0 enable true + Debug;Release;DesktopOnly @@ -38,6 +39,9 @@ + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs index d534d37e8..905ee748b 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs @@ -1,6 +1,9 @@ using AA98_AvlnCodeStudio.UI.ViewModels; using AA98_AvlnCodeStudio.UI.Views; -using AA98_AvlnCodeStudio.Base.Services; +using AA98_AvlnCodeStudio.Base.OS.Services; +using AA98_AvlnCodeStudio.Base.UI.Services; +using AA98_AvlnCodeStudio.Editor.Components; +using AA98_AvlnCodeStudio.Editor.Services; using AA98_AvlnCodeStudio.Model.Documents; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; @@ -39,21 +42,25 @@ private static IServiceProvider ConfigureServices(IClassicDesktopStyleApplicatio var services = new ServiceCollection(); services.AddAvaloniaCommonDialogs(() => desktop.MainWindow); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); + services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService()); + services.AddSingleton(serviceProvider => serviceProvider.GetRequiredService().Create()); services.AddSingleton(); + services.AddSingleton(); return services.BuildServiceProvider(); } private static MainWindow CreateMainWindow(IServiceProvider serviceProvider) { - return new MainWindow - { - DataContext = serviceProvider.GetRequiredService(), - }; + return serviceProvider.GetRequiredService(); } } } \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorFileDialogService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorFileDialogService.cs index 3986d2a2e..eec44cbe3 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorFileDialogService.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/AvaloniaEditorFileDialogService.cs @@ -1,4 +1,4 @@ -using AA98_AvlnCodeStudio.Base.Services; +using AA98_AvlnCodeStudio.Base.UI.Services; using Avln_CommonDialogs.Base.Interfaces; using System.IO; using System.Threading; diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignEditorFileDialogService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignEditorFileDialogService.cs index 285b1ed03..1cc666fb6 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignEditorFileDialogService.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignEditorFileDialogService.cs @@ -1,4 +1,4 @@ -using AA98_AvlnCodeStudio.Base.Services; +using AA98_AvlnCodeStudio.Base.UI.Services; using System.Threading; using System.Threading.Tasks; diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignTextDocumentStorageService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignTextDocumentStorageService.cs index 93d778dd5..f75aff1f4 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignTextDocumentStorageService.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/DesignTextDocumentStorageService.cs @@ -1,4 +1,4 @@ -using AA98_AvlnCodeStudio.Base.Services; +using AA98_AvlnCodeStudio.Base.OS.Services; using System.Threading; using System.Threading.Tasks; diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/FileSystemTextDocumentStorageService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/FileSystemTextDocumentStorageService.cs index 1aa263aed..15e441e92 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/FileSystemTextDocumentStorageService.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Services/FileSystemTextDocumentStorageService.cs @@ -1,4 +1,4 @@ -using AA98_AvlnCodeStudio.Base.Services; +using AA98_AvlnCodeStudio.Base.OS.Services; using System.IO; using System.Text; using System.Threading; diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModel.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModel.cs index 8d8a28088..734a83612 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModel.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/EditorViewModel.cs @@ -1,12 +1,9 @@ -using AA98_AvlnCodeStudio.Base.Services; using AA98_AvlnCodeStudio.Base.ViewModels; -using AA98_AvlnCodeStudio.Model.Documents; +using AA98_AvlnCodeStudio.Editor.Services; using AA98_AvlnCodeStudio.UI.Resources; using AA98_AvlnCodeStudio.UI.Services; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using System; -using System.IO; using System.Threading.Tasks; namespace AA98_AvlnCodeStudio.UI.ViewModels; @@ -16,26 +13,17 @@ namespace AA98_AvlnCodeStudio.UI.ViewModels; /// public partial class EditorViewModel : CodeStudioViewModelBase { - private readonly FileEditorDocument _document; - private readonly IEditorFileDialogService _fileDialogService; - private readonly ITextDocumentStorageService _storageService; + private readonly IEditorWorkflow _workflow; /// /// Initializes a new instance of the class. /// - /// The document state model. - /// The dialog service. - /// The persistence service. - public EditorViewModel( - FileEditorDocument document, - IEditorFileDialogService fileDialogService, - ITextDocumentStorageService storageService) + /// The editor workflow core. + public EditorViewModel(IEditorWorkflow workflow) { - _document = document; - _fileDialogService = fileDialogService; - _storageService = storageService; + _workflow = workflow; - _text = _document.Content; + _text = _workflow.Document.Content; _statusText = UiStrings.ReadyStatusText; _notificationText = UiStrings.ReadyStatusText; SynchronizeFromDocument(); @@ -45,7 +33,10 @@ public EditorViewModel( /// Initializes a new instance of the class for design-time usage. /// public EditorViewModel() - : this(new FileEditorDocument(), new DesignEditorFileDialogService(), new DesignTextDocumentStorageService()) + : this(new EditorWorkflow( + new Model.Documents.FileEditorDocument(), + new DesignEditorFileDialogService(), + new DesignTextDocumentStorageService())) { } @@ -74,11 +65,11 @@ public EditorViewModel() /// /// Gets the window title for the main editor shell. /// - public string WindowTitle => $"{UiStrings.ApplicationTitle} - {DocumentName ?? _document.DisplayName}{(IsDirty ? UiStrings.WindowTitleDirtySuffix : string.Empty)}"; + public string WindowTitle => $"{UiStrings.ApplicationTitle} - {DocumentName ?? _workflow.Document.DisplayName}{(IsDirty ? UiStrings.WindowTitleDirtySuffix : string.Empty)}"; partial void OnTextChanged(string value) { - _document.UpdateContent(value); + _workflow.UpdateText(value); SynchronizeFromDocument(); StatusText = IsDirty ? UiStrings.ModifiedStatusText : UiStrings.ReadyStatusText; NotificationText = IsDirty ? UiStrings.DocumentModifiedNotificationText : UiStrings.DocumentSynchronizedNotificationText; @@ -90,11 +81,10 @@ partial void OnTextChanged(string value) [RelayCommand] private void NewDocument() { - _document.Reset(); - Text = _document.Content; + var result = _workflow.NewDocumentAsync().GetAwaiter().GetResult(); + Text = _workflow.Document.Content; SynchronizeFromDocument(); - StatusText = UiStrings.NewDocumentStatusText; - NotificationText = UiStrings.CreatedNewDocumentNotificationText; + ApplyResult(result, UiStrings.NewDocumentStatusText, UiStrings.CreatedNewDocumentNotificationText, UiStrings.NewDocumentStatusText, UiStrings.CreatedNewDocumentNotificationText); } /// @@ -104,23 +94,10 @@ private void NewDocument() [RelayCommand] private async Task OpenAsync() { - var filePath = await _fileDialogService - .ShowOpenFileDialogAsync(GetCurrentDirectory()); - - if (string.IsNullOrWhiteSpace(filePath)) - { - StatusText = UiStrings.OpenCanceledStatusText; - NotificationText = UiStrings.OpenCanceledNotificationText; - return; - } - - var content = await _storageService.ReadAllTextAsync(filePath); - _document.Load(filePath, content); - - Text = _document.Content; + var result = await _workflow.OpenAsync(); + Text = _workflow.Document.Content; SynchronizeFromDocument(); - StatusText = string.Format(UiStrings.OpenedDocumentStatusFormat, _document.DisplayName); - NotificationText = string.Format(UiStrings.LoadedDocumentNotificationFormat, _document.DisplayName); + ApplyResult(result, string.Format(UiStrings.OpenedDocumentStatusFormat, _workflow.Document.DisplayName), string.Format(UiStrings.LoadedDocumentNotificationFormat, _workflow.Document.DisplayName), UiStrings.OpenCanceledStatusText, UiStrings.OpenCanceledNotificationText); } /// @@ -130,25 +107,9 @@ private async Task OpenAsync() [RelayCommand] private async Task SaveAsync() { - var filePath = _document.FilePath; - if (string.IsNullOrWhiteSpace(filePath)) - { - filePath = await _fileDialogService - .ShowSaveFileDialogAsync(GetCurrentDirectory(), _document.DisplayName); - } - - if (string.IsNullOrWhiteSpace(filePath)) - { - StatusText = UiStrings.SaveCanceledStatusText; - NotificationText = UiStrings.SaveCanceledNotificationText; - return; - } - - await _storageService.SaveAllTextAsync(filePath, Text ?? string.Empty); - _document.MarkSaved(filePath); + var result = await _workflow.SaveAsync(); SynchronizeFromDocument(); - StatusText = string.Format(UiStrings.SavedDocumentStatusFormat, _document.DisplayName); - NotificationText = string.Format(UiStrings.SavedDocumentNotificationFormat, _document.DisplayName); + ApplyResult(result, string.Format(UiStrings.SavedDocumentStatusFormat, _workflow.Document.DisplayName), string.Format(UiStrings.SavedDocumentNotificationFormat, _workflow.Document.DisplayName), UiStrings.SaveCanceledStatusText, UiStrings.SaveCanceledNotificationText); } /// @@ -158,35 +119,27 @@ private async Task SaveAsync() [RelayCommand] private async Task SaveAsAsync() { - var filePath = await _fileDialogService - .ShowSaveFileDialogAsync(GetCurrentDirectory(), _document.DisplayName); - - if (string.IsNullOrWhiteSpace(filePath)) - { - StatusText = UiStrings.SaveAsCanceledStatusText; - NotificationText = UiStrings.SaveAsCanceledNotificationText; - return; - } - - await _storageService.SaveAllTextAsync(filePath, Text ?? string.Empty); - _document.MarkSaved(filePath); + var result = await _workflow.SaveAsAsync(); SynchronizeFromDocument(); - StatusText = string.Format(UiStrings.SavedDocumentStatusFormat, _document.DisplayName); - NotificationText = string.Format(UiStrings.SavedDocumentNotificationFormat, _document.DisplayName); + ApplyResult(result, string.Format(UiStrings.SavedDocumentStatusFormat, _workflow.Document.DisplayName), string.Format(UiStrings.SavedDocumentNotificationFormat, _workflow.Document.DisplayName), UiStrings.SaveAsCanceledStatusText, UiStrings.SaveAsCanceledNotificationText); } - private string? GetCurrentDirectory() + private void SynchronizeFromDocument() { - return string.IsNullOrWhiteSpace(_document.FilePath) - ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) - : Path.GetDirectoryName(_document.FilePath); + DocumentName = _workflow.Document.DisplayName; + CurrentFilePath = _workflow.Document.FilePath; + IsDirty = _workflow.Document.IsDirty; } - private void SynchronizeFromDocument() + private void ApplyResult( + EditorOperationResult result, + string completedStatus, + string completedNotification, + string canceledStatus, + string canceledNotification) { - DocumentName = _document.DisplayName; - CurrentFilePath = _document.FilePath; - IsDirty = _document.IsDirty; + StatusText = result.IsCompleted ? completedStatus : canceledStatus; + NotificationText = result.IsCompleted ? completedNotification : canceledNotification; } } diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/MainWindowViewModel.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/MainWindowViewModel.cs index 5af3be0d8..c365fa837 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/MainWindowViewModel.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/ViewModels/MainWindowViewModel.cs @@ -1,21 +1,33 @@ using AA98_AvlnCodeStudio.UI.Resources; +using AA98_AvlnCodeStudio.Editor.Services; namespace AA98_AvlnCodeStudio.UI.ViewModels { public partial class MainWindowViewModel : ViewModelBase { + private readonly Components.IAvaloniaEditorComponent _editorComponent; + public MainWindowViewModel() - : this(new EditorViewModel()) + : this(new Components.AvaloniaEditorComponent( + new EditorWorkflow( + new Model.Documents.FileEditorDocument(), + new Services.DesignEditorFileDialogService(), + new Services.DesignTextDocumentStorageService()), + new EditorViewModel(), + new Controls.EditorTextArea())) { } - public MainWindowViewModel(EditorViewModel editor) + public MainWindowViewModel(Components.IAvaloniaEditorComponent editorComponent) { - Editor = editor; + _editorComponent = editorComponent; + Editor = editorComponent.EditorViewModel; } public EditorViewModel Editor { get; } + public Components.IAvaloniaEditorComponent EditorComponent => _editorComponent; + public string WindowTitle => Editor.WindowTitle; public string NavigationTitle => UiStrings.NavigationTitle; diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml index be7b8b12b..e19e6ef50 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml @@ -1,7 +1,6 @@ - + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml.cs index 5ae11328b..76c691d67 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/Views/MainWindow.axaml.cs @@ -8,5 +8,16 @@ public MainWindow() { InitializeComponent(); } + + public MainWindow(ViewModels.MainWindowViewModel viewModel) + : this() + { + DataContext = viewModel; + var editorHost = this.FindControl(nameof(EditorHost)); + if (editorHost is not null) + { + editorHost.Content = viewModel.EditorComponent.View; + } + } } } \ No newline at end of file From 0b475a25957afba9c9331a06b838ae1e342fc9d4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:22 +0200 Subject: [PATCH 048/106] Avalonia_App_01 --- .../Avalonia_App01/Avalonia_App_01/Avalonia_App_01.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01/Avalonia_App_01.csproj b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01/Avalonia_App_01.csproj index 25a159f91..c4b4be0da 100644 --- a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01/Avalonia_App_01.csproj +++ b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01/Avalonia_App_01.csproj @@ -4,6 +4,7 @@ net10.0 enable true + Debug;Release;DesktopOnly From 9152a6ad3bfa38cf462795ac3e4cac659c4fd0d0 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:22 +0200 Subject: [PATCH 049/106] Avalonia_App_01.Android --- .../Avalonia_App_01.Android/Avalonia_App_01.Android.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Android/Avalonia_App_01.Android.csproj b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Android/Avalonia_App_01.Android.csproj index 6ac786886..8829b4562 100644 --- a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Android/Avalonia_App_01.Android.csproj +++ b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Android/Avalonia_App_01.Android.csproj @@ -10,6 +10,7 @@ 1.0 apk false + Debug;Release From 0dd6ab199b1050d4b4806fdd2b5ba7730af57166 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:23 +0200 Subject: [PATCH 050/106] Avalonia_App_01.Browser --- .../Avalonia_App_01.Browser/Avalonia_App_01.Browser.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Browser/Avalonia_App_01.Browser.csproj b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Browser/Avalonia_App_01.Browser.csproj index 2df37f908..4a8673a81 100644 --- a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Browser/Avalonia_App_01.Browser.csproj +++ b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Browser/Avalonia_App_01.Browser.csproj @@ -5,6 +5,7 @@ Exe true enable + Debug;Release;DesktopOnly From ad48a18e8e581244abae4fd98417af6a6acc34ab Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:23 +0200 Subject: [PATCH 051/106] Avalonia_App_01.Desktop --- .../Avalonia_App_01.Desktop/Avalonia_App_01.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Desktop/Avalonia_App_01.Desktop.csproj b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Desktop/Avalonia_App_01.Desktop.csproj index 77adaa190..7ed1b297e 100644 --- a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Desktop/Avalonia_App_01.Desktop.csproj +++ b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.Desktop/Avalonia_App_01.Desktop.csproj @@ -7,6 +7,7 @@ net10.0 enable true + Debug;Release;DesktopOnly From 749527518d0c929715fada2697ddb47697243b23 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:24 +0200 Subject: [PATCH 052/106] Avalonia_App_01.iOS --- .../Avalonia_App_01.iOS/Avalonia_App_01.iOS.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.iOS/Avalonia_App_01.iOS.csproj b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.iOS/Avalonia_App_01.iOS.csproj index 1ba0feac0..744657e61 100644 --- a/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.iOS/Avalonia_App_01.iOS.csproj +++ b/Avalonia_Apps/Avalonia_App01/Avalonia_App_01.iOS/Avalonia_App_01.iOS.csproj @@ -5,6 +5,7 @@ net10.0-ios 13.0 enable + Debug;Release From dae9dca7eabbe15e84e6267e88ada89249c3f582 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:24 +0200 Subject: [PATCH 053/106] Avalonia_App02 --- .../Avalonia_App02/Avalonia_App02/Avalonia_App02.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avalonia_App02/Avalonia_App02/Avalonia_App02.csproj b/Avalonia_Apps/Avalonia_App02/Avalonia_App02/Avalonia_App02.csproj index d32cd7555..200280995 100644 --- a/Avalonia_Apps/Avalonia_App02/Avalonia_App02/Avalonia_App02.csproj +++ b/Avalonia_Apps/Avalonia_App02/Avalonia_App02/Avalonia_App02.csproj @@ -6,6 +6,7 @@ true app.manifest true + Debug;Release;DesktopOnly From d8c6a3427cb8de0aa099d27f7c247f5de80ddcd4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:25 +0200 Subject: [PATCH 054/106] Avalonia_App02Tests --- .../Avalonia_App02Tests/Avalonia_App02Tests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avalonia_App02/Avalonia_App02Tests/Avalonia_App02Tests.csproj b/Avalonia_Apps/Avalonia_App02/Avalonia_App02Tests/Avalonia_App02Tests.csproj index 0f568dac3..cccb5d288 100644 --- a/Avalonia_Apps/Avalonia_App02/Avalonia_App02Tests/Avalonia_App02Tests.csproj +++ b/Avalonia_Apps/Avalonia_App02/Avalonia_App02Tests/Avalonia_App02Tests.csproj @@ -4,6 +4,7 @@ net8.0;net9.0 Library enable + Debug;Release;DesktopOnly From ad3e3e8c09cd1270888314105cdd750c0e8455b4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:25 +0200 Subject: [PATCH 055/106] Avln_AnimationTiming --- .../Avln_AnimationTiming/Avln_AnimationTiming.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Avalonia_Apps/AvlnSamples/Avln_AnimationTiming/Avln_AnimationTiming.csproj b/Avalonia_Apps/AvlnSamples/Avln_AnimationTiming/Avln_AnimationTiming.csproj index 34d54e21b..e07120d31 100644 --- a/Avalonia_Apps/AvlnSamples/Avln_AnimationTiming/Avln_AnimationTiming.csproj +++ b/Avalonia_Apps/AvlnSamples/Avln_AnimationTiming/Avln_AnimationTiming.csproj @@ -6,6 +6,7 @@ enable app.manifest true + Debug;Release;DesktopOnly @@ -58,6 +59,9 @@ + + 11.3.17 + From e974a34d8973cc0fc067f5fc56319f5d482d1e14 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:26 +0200 Subject: [PATCH 056/106] Avln_Brushes --- Avalonia_Apps/AvlnSamples/Avln_Brushes/Avln_Brushes.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Avalonia_Apps/AvlnSamples/Avln_Brushes/Avln_Brushes.csproj b/Avalonia_Apps/AvlnSamples/Avln_Brushes/Avln_Brushes.csproj index cf7cf64a1..251ac2b5d 100644 --- a/Avalonia_Apps/AvlnSamples/Avln_Brushes/Avln_Brushes.csproj +++ b/Avalonia_Apps/AvlnSamples/Avln_Brushes/Avln_Brushes.csproj @@ -6,6 +6,7 @@ enable app.manifest true + Debug;Release;DesktopOnly @@ -42,6 +43,9 @@ + + 11.3.17 + From b65197f1fbfd5a8ad0118bb0bef9c6a136099f1a Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:29 +0200 Subject: [PATCH 057/106] Avln_Hello_World --- .../AvlnSamples/Avln_Hello_World/Avln_Hello_World.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Avalonia_Apps/AvlnSamples/Avln_Hello_World/Avln_Hello_World.csproj b/Avalonia_Apps/AvlnSamples/Avln_Hello_World/Avln_Hello_World.csproj index af1b86429..81404dc73 100644 --- a/Avalonia_Apps/AvlnSamples/Avln_Hello_World/Avln_Hello_World.csproj +++ b/Avalonia_Apps/AvlnSamples/Avln_Hello_World/Avln_Hello_World.csproj @@ -6,6 +6,7 @@ enable app.manifest true + Debug;Release;DesktopOnly @@ -32,6 +33,9 @@ + + 11.3.17 + From 1df517b66b0cde0c8e5446ac133c3c1d84d7bee8 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:31 +0200 Subject: [PATCH 058/106] Avln_IntegrationTestApp --- .../Avln_IntegrationTestApp/Avln_IntegrationTestApp.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AvlnSamples/Avln_IntegrationTestApp/Avln_IntegrationTestApp.csproj b/Avalonia_Apps/AvlnSamples/Avln_IntegrationTestApp/Avln_IntegrationTestApp.csproj index 90e8da614..0bedacfef 100644 --- a/Avalonia_Apps/AvlnSamples/Avln_IntegrationTestApp/Avln_IntegrationTestApp.csproj +++ b/Avalonia_Apps/AvlnSamples/Avln_IntegrationTestApp/Avln_IntegrationTestApp.csproj @@ -6,6 +6,7 @@ enable app.manifest true + Debug;Release;DesktopOnly From 851512fd95587f60e2ec91d27e370c6982724b19 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:32 +0200 Subject: [PATCH 059/106] Avln_RenderDemo --- .../AvlnSamples/Avln_RenderDemo/Avln_RenderDemo.csproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Avalonia_Apps/AvlnSamples/Avln_RenderDemo/Avln_RenderDemo.csproj b/Avalonia_Apps/AvlnSamples/Avln_RenderDemo/Avln_RenderDemo.csproj index 83f749add..ecbc3d7df 100644 --- a/Avalonia_Apps/AvlnSamples/Avln_RenderDemo/Avln_RenderDemo.csproj +++ b/Avalonia_Apps/AvlnSamples/Avln_RenderDemo/Avln_RenderDemo.csproj @@ -5,6 +5,7 @@ net8.0 enable true + Debug;Release;DesktopOnly @@ -36,6 +37,9 @@ + + 11.3.17 + From 67a855f8e93019226f60915de6f1dc088245178b Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:34 +0200 Subject: [PATCH 060/106] SampleControls --- .../AvlnSamples/SampleControls/ControlSamples.Pack.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AvlnSamples/SampleControls/ControlSamples.Pack.csproj b/Avalonia_Apps/AvlnSamples/SampleControls/ControlSamples.Pack.csproj index ea13f50c2..5bf8d5e1c 100644 --- a/Avalonia_Apps/AvlnSamples/SampleControls/ControlSamples.Pack.csproj +++ b/Avalonia_Apps/AvlnSamples/SampleControls/ControlSamples.Pack.csproj @@ -11,6 +11,7 @@ $(SolutionDir).packages false ControlSamples + Debug;Release;DesktopOnly From 12819b73419c640ce4b724d3c4f7f5dd171f6a25 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:57 +0200 Subject: [PATCH 061/106] Avln_Marble.Browser --- .../Avln_Marble/Avln_Marble.Browser/Avln_Marble.Browser.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Avln_Marble.Browser.csproj b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Avln_Marble.Browser.csproj index 4d5b7b932..a5de87929 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Avln_Marble.Browser.csproj +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Browser/Avln_Marble.Browser.csproj @@ -5,6 +5,7 @@ Exe true enable + Debug;Release;DesktopOnly From 5adbeb36c5151a3bd5e8dfeb2dda7c8eb92ef4f8 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:57 +0200 Subject: [PATCH 062/106] Avln_Marble.Desktop --- .../Avln_Marble/Avln_Marble.Desktop/Avln_Marble.Desktop.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Desktop/Avln_Marble.Desktop.csproj b/Avalonia_Apps/Avln_Marble/Avln_Marble.Desktop/Avln_Marble.Desktop.csproj index 6ff3ccd6b..bdd3bb86a 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Desktop/Avln_Marble.Desktop.csproj +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Desktop/Avln_Marble.Desktop.csproj @@ -5,6 +5,7 @@ net10.0 true app.manifest + Debug;Release;DesktopOnly From c4dc19c68cdc283d4b1aeae7ab08921f5b59165b Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:58 +0200 Subject: [PATCH 063/106] Avln_Marble.Display --- .../Avln_Marble/Avln_Marble.Display/Avln_Marble.Display.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Avln_Marble.Display.csproj b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Avln_Marble.Display.csproj index c14583aae..801ac8eaa 100644 --- a/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Avln_Marble.Display.csproj +++ b/Avalonia_Apps/Avln_Marble/Avln_Marble.Display/Avln_Marble.Display.csproj @@ -4,6 +4,7 @@ net10.0 enable true + Debug;Release;DesktopOnly @@ -18,7 +19,7 @@ - + From 7a7d227667192987d64ecff7d0878b4f2ce0c0d7 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:58 +0200 Subject: [PATCH 064/106] BenchmarkSuite1 --- Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj b/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj index 24e58c61b..8b597231d 100644 --- a/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj +++ b/Avalonia_Apps/BenchmarkSuite1/BenchmarkSuite1.csproj @@ -1,15 +1,16 @@  - + net8.0 Exe enable enable + Debug;Release;DesktopOnly - + From 0d1dcd0094ab890ce84be3449509f3f9f7a58ddc Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:59 +0200 Subject: [PATCH 065/106] Avln_BaseLib --- Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj b/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj index 470f7965c..582afd99a 100644 --- a/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj +++ b/Avalonia_Apps/Libraries/Avln_BaseLib/Avln_BaseLib.csproj @@ -8,6 +8,9 @@ ..\sgLib.snk + + + Debug;Release;DesktopOnly From f01c2e6c9db0d1da8e7cf99a5701ad0fcfd26264 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:55:59 +0200 Subject: [PATCH 066/106] Avln_BaseLibTests --- .../Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj b/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj index d9b284671..ed6374709 100644 --- a/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj +++ b/Avalonia_Apps/Libraries/Avln_BaseLibTests/Avln_BaseLibTests.csproj @@ -4,6 +4,7 @@ net8.0 false AnyCPU;x86 + Debug;Release;DesktopOnly From a0ea7d1bc87a2d16764024195a33739cd74cdb89 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:00 +0200 Subject: [PATCH 067/106] Avln_CommonDialogs.Avalonia --- .../Avln_CommonDialogs.Avalonia.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Libraries/Avln_CommonDialogs.Avalonia/Avln_CommonDialogs.Avalonia.csproj b/Avalonia_Apps/Libraries/Avln_CommonDialogs.Avalonia/Avln_CommonDialogs.Avalonia.csproj index c71a5208a..f34faa4a8 100644 --- a/Avalonia_Apps/Libraries/Avln_CommonDialogs.Avalonia/Avln_CommonDialogs.Avalonia.csproj +++ b/Avalonia_Apps/Libraries/Avln_CommonDialogs.Avalonia/Avln_CommonDialogs.Avalonia.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + Debug;Release;DesktopOnly From e45d150dfdafd9f63a9338e247f7de58b5244b1c Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:00 +0200 Subject: [PATCH 068/106] Avln_CommonDialogs.Base --- .../Avln_CommonDialogs.Base/Avln_CommonDialogs.Base.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Libraries/Avln_CommonDialogs.Base/Avln_CommonDialogs.Base.csproj b/Avalonia_Apps/Libraries/Avln_CommonDialogs.Base/Avln_CommonDialogs.Base.csproj index fa71b7ae6..46132d5c7 100644 --- a/Avalonia_Apps/Libraries/Avln_CommonDialogs.Base/Avln_CommonDialogs.Base.csproj +++ b/Avalonia_Apps/Libraries/Avln_CommonDialogs.Base/Avln_CommonDialogs.Base.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + Debug;Release;DesktopOnly From e150ac206eff27949d306c9920cb4014575949be Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:01 +0200 Subject: [PATCH 069/106] BaseLib --- Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj b/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj index ebaf98c6f..f5c9668ef 100644 --- a/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj +++ b/Avalonia_Apps/Libraries/BaseLib/BaseLib.csproj @@ -4,6 +4,7 @@ Library net6.0;net7.0;net8.0;net481;net48;net472;net462 AnyCPU;x64 + Debug;Release;DesktopOnly From 080b0d926d21ce192da13393024508bab8640033 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:01 +0200 Subject: [PATCH 070/106] BaseLibTests --- Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj b/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj index e595557ef..f1f599832 100644 --- a/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj +++ b/Avalonia_Apps/Libraries/BaseLibTests/BaseLibTests.csproj @@ -1,10 +1,12 @@  + Library net8.0;net481;net48;net472;net462 - false - true - AnyCPU;x64 + false + true + AnyCPU;x64 + Debug;Release;DesktopOnly From a10043072ac86a515df3b88c465949b46064d772 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:02 +0200 Subject: [PATCH 071/106] MathLibrary --- Avalonia_Apps/Libraries/MathLibrary/MathLibrary.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/Libraries/MathLibrary/MathLibrary.csproj b/Avalonia_Apps/Libraries/MathLibrary/MathLibrary.csproj index 9541ca7b3..e908aaa04 100644 --- a/Avalonia_Apps/Libraries/MathLibrary/MathLibrary.csproj +++ b/Avalonia_Apps/Libraries/MathLibrary/MathLibrary.csproj @@ -3,6 +3,7 @@ net481;net48;net472;net462;net6.0;net7.0;net8.0;net9.0 True + Debug;Release;DesktopOnly From e17d2529cb1f4fe5ddef6cac6137e9165db64989 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:02 +0200 Subject: [PATCH 072/106] RenderImage.Base --- Avalonia_Apps/RenderImage.Base/RenderImage.Base.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/RenderImage.Base/RenderImage.Base.csproj b/Avalonia_Apps/RenderImage.Base/RenderImage.Base.csproj index cb2f813b4..66b87a1eb 100644 --- a/Avalonia_Apps/RenderImage.Base/RenderImage.Base.csproj +++ b/Avalonia_Apps/RenderImage.Base/RenderImage.Base.csproj @@ -4,6 +4,7 @@ net9.0 enable enable + Debug;Release;DesktopOnly From 15bd7b1f62708262cdcfc97a73d87a1068df2978 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:03 +0200 Subject: [PATCH 073/106] RenderImage.BaseTests --- Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj b/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj index 24f9b1c15..662a916a4 100644 --- a/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj +++ b/Avalonia_Apps/RenderImage.BaseTests/RenderImage.BaseTests.csproj @@ -5,6 +5,7 @@ enable enable false + Debug;Release;DesktopOnly From 95141716f72de72dfc2f2e872468d3349776bcc4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Wed, 3 Jun 2026 05:56:04 +0200 Subject: [PATCH 074/106] Avalonia_Apps --- Avalonia_Apps/.github/copilot-instructions.md | 4 + .../AA98_AvlnCodeStudio.slnx | 4 + Avalonia_Apps/Avalonia_Apps.sln | 551 +++++++++++++++++- Avalonia_Apps/Avalonia_Apps.slnx | 50 +- Avalonia_Apps/DevOps/DevOps.projitems | 1 + 5 files changed, 586 insertions(+), 24 deletions(-) diff --git a/Avalonia_Apps/.github/copilot-instructions.md b/Avalonia_Apps/.github/copilot-instructions.md index 14688efc9..f7cc5533e 100644 --- a/Avalonia_Apps/.github/copilot-instructions.md +++ b/Avalonia_Apps/.github/copilot-instructions.md @@ -20,6 +20,10 @@ Apply these defaults when working in this repository unless the user explicitly - Prefer reusable scripts and skills from `C:\Projekte\CSharp\Tools` and its sub-directories over ad-hoc terminal commands whenever they fit the task, especially for coverage analysis, build and test diagnostics, reporting, automation, and similar repository-wide workflows. - Read adjacent `*.md` documentation in that tools area only when needed to confirm applicability or usage. - For this repository, prefer keeping RepoMigrator.Core provider-agnostic via broader migration source provider abstractions instead of forcing archive inputs into IVersionControlProvider. Additionally, provider-specific files should live in their specific provider projects; RepoMigrator.Core should contain only provider-agnostic shared abstractions and models. Sub-provider concerns such as compression formats should also live in dedicated specific projects (for example, a Zip compression provider project). +- In this C# workspace, architecture should be component-based: integrate UI components via Dependency Injection (DI), solve as much as possible through interfaces and interface factories, and keep all non-UI components UI-agnostic and OS-agnostic. A component in this repository means a separate project file with an additional test project, and `AA98_AvlnCodeStudio.Base` should have minimal or no dependencies, particularly not depending on Model. +- Additional base libraries such as `.Base.UI` and `.Base.OS` are encouraged; both should be able to reference `.Base` but must not have dependencies on each other. +- A provider-agnostic AI integration should be part of the concept from the beginning; therefore, a dedicated base library like `AA98_AvlnCodeStudio.Base.AI` is desired. +- Prefer capability-oriented abstractions in this repository's control tree architecture: interfaces should represent what a type has or can provide (for example, a WidgetSet or access to an application loop) rather than identity-style type markers. ## Code Organization - Prefer one class/interface/struct per file. diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.slnx b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.slnx index c81c94049..072b6f166 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.slnx +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.slnx @@ -2,6 +2,10 @@ + + + + diff --git a/Avalonia_Apps/Avalonia_Apps.sln b/Avalonia_Apps/Avalonia_Apps.sln index e1904dfe1..1a23e03c1 100644 --- a/Avalonia_Apps/Avalonia_Apps.sln +++ b/Avalonia_Apps/Avalonia_Apps.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 18 VisualStudioVersion = 18.0.11201.2 @@ -223,7 +223,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA22_AvlnCapTests", "AA22_A EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA22_AvlnCap2", "AA22_AvlnCap\AA22_AvlnCap2\AA22_AvlnCap2.csproj", "{BC312318-3671-5E97-5256-467F6FAEFC0B}" EndProject -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AA25_RichTextEdit", "AA25_RichTextEdit", "{3DB34546-9932-4FDF-A61C-E42E9C1FFDC9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA25_RichTextEdit", "AA25_RichTextEdit\AA25_RichTextEdit\AA25_RichTextEdit.csproj", "{CB02C822-0D46-8A0A-D98E-224642E5EE3C}" @@ -242,228 +241,752 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avln_CommonDialogs.Base", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avln_CommonDialogs.Avalonia", "Libraries\Avln_CommonDialogs.Avalonia\Avln_CommonDialogs.Avalonia.csproj", "{D94D599F-6B68-AB23-FF20-0FBD684A1FE7}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AA98_AvlnCodeStudio", "AA98_AvlnCodeStudio", "{6470625C-1271-0563-CF21-654520B9D8F4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA98_AvlnCodeStudio.Base.UI", "AA98_AvlnCodeStudio\AA98_AvlnCodeStudio.Base.UI\AA98_AvlnCodeStudio.Base.UI.csproj", "{71C741B8-E8EE-4530-9782-4BDA1CDE8D36}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA98_AvlnCodeStudio.Base", "AA98_AvlnCodeStudio\AA98_AvlnCodeStudio.Base\AA98_AvlnCodeStudio.Base.csproj", "{96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA98_AvlnCodeStudio.Base.OS", "AA98_AvlnCodeStudio\AA98_AvlnCodeStudio.Base.OS\AA98_AvlnCodeStudio.Base.OS.csproj", "{4374637B-5C4B-4FEF-ACC8-6DC761362768}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA98_AvlnCodeStudio.Base.AI", "AA98_AvlnCodeStudio\AA98_AvlnCodeStudio.Base.AI\AA98_AvlnCodeStudio.Base.AI.csproj", "{6FB140B0-92C8-4F2D-8379-135F76305153}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA98_AvlnCodeStudio.Editor", "AA98_AvlnCodeStudio\AA98_AvlnCodeStudio.Editor\AA98_AvlnCodeStudio.Editor.csproj", "{E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA98_AvlnCodeStudio.Model", "AA98_AvlnCodeStudio\AA98_AvlnCodeStudio.Model\AA98_AvlnCodeStudio.Model.csproj", "{AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AA98_AvlnCodeStudio.Editor.Tests", "AA98_AvlnCodeStudio\AA98_AvlnCodeStudio.Editor.Tests\AA98_AvlnCodeStudio.Editor.Tests.csproj", "{3FB7F287-94F0-44CB-94F9-10FAEE447A96}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Debug|x64.Build.0 = Debug|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Debug|x86.Build.0 = Debug|Any CPU {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Release|Any CPU.Build.0 = Release|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Release|x64.ActiveCfg = Release|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Release|x64.Build.0 = Release|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Release|x86.ActiveCfg = Release|Any CPU + {8EE2B2EC-CED9-43C5-AA6D-A17AD53614CB}.Release|x86.Build.0 = Release|Any CPU {54195590-130A-404F-8A59-720FCD6055FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {54195590-130A-404F-8A59-720FCD6055FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Debug|x64.ActiveCfg = Debug|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Debug|x64.Build.0 = Debug|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Debug|x86.ActiveCfg = Debug|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Debug|x86.Build.0 = Debug|Any CPU {54195590-130A-404F-8A59-720FCD6055FB}.Release|Any CPU.ActiveCfg = Release|Any CPU {54195590-130A-404F-8A59-720FCD6055FB}.Release|Any CPU.Build.0 = Release|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Release|x64.ActiveCfg = Release|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Release|x64.Build.0 = Release|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Release|x86.ActiveCfg = Release|Any CPU + {54195590-130A-404F-8A59-720FCD6055FB}.Release|x86.Build.0 = Release|Any CPU {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Debug|x64.ActiveCfg = Debug|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Debug|x64.Build.0 = Debug|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Debug|x86.ActiveCfg = Debug|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Debug|x86.Build.0 = Debug|Any CPU {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Release|Any CPU.ActiveCfg = Release|Any CPU {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Release|Any CPU.Build.0 = Release|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Release|x64.ActiveCfg = Release|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Release|x64.Build.0 = Release|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Release|x86.ActiveCfg = Release|Any CPU + {1BBA8FFC-B6E6-4B37-BC4A-C9B9CE1B7873}.Release|x86.Build.0 = Release|Any CPU {72188BD8-AF61-4691-BB84-E186C71FB58C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72188BD8-AF61-4691-BB84-E186C71FB58C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Debug|x64.ActiveCfg = Debug|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Debug|x64.Build.0 = Debug|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Debug|x86.ActiveCfg = Debug|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Debug|x86.Build.0 = Debug|Any CPU {72188BD8-AF61-4691-BB84-E186C71FB58C}.Release|Any CPU.ActiveCfg = Release|Any CPU {72188BD8-AF61-4691-BB84-E186C71FB58C}.Release|Any CPU.Build.0 = Release|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Release|x64.ActiveCfg = Release|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Release|x64.Build.0 = Release|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Release|x86.ActiveCfg = Release|Any CPU + {72188BD8-AF61-4691-BB84-E186C71FB58C}.Release|x86.Build.0 = Release|Any CPU {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Debug|x64.ActiveCfg = Debug|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Debug|x64.Build.0 = Debug|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Debug|x86.ActiveCfg = Debug|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Debug|x86.Build.0 = Debug|Any CPU {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Release|Any CPU.Build.0 = Release|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Release|x64.ActiveCfg = Release|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Release|x64.Build.0 = Release|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Release|x86.ActiveCfg = Release|Any CPU + {E6C78ACA-A947-4E1D-BE34-172BBF620FF6}.Release|x86.Build.0 = Release|Any CPU {CEB09204-47F7-4136-877A-1920D53133C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CEB09204-47F7-4136-877A-1920D53133C1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Debug|x64.ActiveCfg = Debug|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Debug|x64.Build.0 = Debug|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Debug|x86.ActiveCfg = Debug|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Debug|x86.Build.0 = Debug|Any CPU {CEB09204-47F7-4136-877A-1920D53133C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {CEB09204-47F7-4136-877A-1920D53133C1}.Release|Any CPU.Build.0 = Release|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Release|x64.ActiveCfg = Release|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Release|x64.Build.0 = Release|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Release|x86.ActiveCfg = Release|Any CPU + {CEB09204-47F7-4136-877A-1920D53133C1}.Release|x86.Build.0 = Release|Any CPU {E791FC44-D382-40BD-9345-2FA8DB263922}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E791FC44-D382-40BD-9345-2FA8DB263922}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Debug|x64.ActiveCfg = Debug|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Debug|x64.Build.0 = Debug|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Debug|x86.ActiveCfg = Debug|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Debug|x86.Build.0 = Debug|Any CPU {E791FC44-D382-40BD-9345-2FA8DB263922}.Release|Any CPU.ActiveCfg = Release|Any CPU {E791FC44-D382-40BD-9345-2FA8DB263922}.Release|Any CPU.Build.0 = Release|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Release|x64.ActiveCfg = Release|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Release|x64.Build.0 = Release|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Release|x86.ActiveCfg = Release|Any CPU + {E791FC44-D382-40BD-9345-2FA8DB263922}.Release|x86.Build.0 = Release|Any CPU {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Debug|x64.ActiveCfg = Debug|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Debug|x64.Build.0 = Debug|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Debug|x86.ActiveCfg = Debug|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Debug|x86.Build.0 = Debug|Any CPU {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Release|Any CPU.ActiveCfg = Release|Any CPU {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Release|Any CPU.Build.0 = Release|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Release|x64.ActiveCfg = Release|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Release|x64.Build.0 = Release|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Release|x86.ActiveCfg = Release|Any CPU + {75BC2329-CC7B-4576-B326-DB9A7F52C861}.Release|x86.Build.0 = Release|Any CPU {671282D6-93C0-4B03-8782-636D12C446B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {671282D6-93C0-4B03-8782-636D12C446B5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Debug|x64.ActiveCfg = Debug|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Debug|x64.Build.0 = Debug|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Debug|x86.ActiveCfg = Debug|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Debug|x86.Build.0 = Debug|Any CPU {671282D6-93C0-4B03-8782-636D12C446B5}.Release|Any CPU.ActiveCfg = Release|Any CPU {671282D6-93C0-4B03-8782-636D12C446B5}.Release|Any CPU.Build.0 = Release|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Release|x64.ActiveCfg = Release|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Release|x64.Build.0 = Release|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Release|x86.ActiveCfg = Release|Any CPU + {671282D6-93C0-4B03-8782-636D12C446B5}.Release|x86.Build.0 = Release|Any CPU {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Debug|x64.ActiveCfg = Debug|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Debug|x64.Build.0 = Debug|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Debug|x86.ActiveCfg = Debug|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Debug|x86.Build.0 = Debug|Any CPU {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Release|Any CPU.Build.0 = Release|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Release|x64.ActiveCfg = Release|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Release|x64.Build.0 = Release|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Release|x86.ActiveCfg = Release|Any CPU + {A7AB3E3B-3806-4038-8E11-6FB4D9D31C33}.Release|x86.Build.0 = Release|Any CPU {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Debug|x64.ActiveCfg = Debug|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Debug|x64.Build.0 = Debug|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Debug|x86.ActiveCfg = Debug|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Debug|x86.Build.0 = Debug|Any CPU {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Release|Any CPU.Build.0 = Release|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Release|x64.ActiveCfg = Release|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Release|x64.Build.0 = Release|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Release|x86.ActiveCfg = Release|Any CPU + {EE714526-315A-4211-A4E3-2B74EE4C1D83}.Release|x86.Build.0 = Release|Any CPU {25D1167C-E942-49FD-B125-242062EB4D70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {25D1167C-E942-49FD-B125-242062EB4D70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Debug|x64.ActiveCfg = Debug|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Debug|x64.Build.0 = Debug|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Debug|x86.ActiveCfg = Debug|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Debug|x86.Build.0 = Debug|Any CPU {25D1167C-E942-49FD-B125-242062EB4D70}.Release|Any CPU.ActiveCfg = Release|Any CPU {25D1167C-E942-49FD-B125-242062EB4D70}.Release|Any CPU.Build.0 = Release|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Release|x64.ActiveCfg = Release|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Release|x64.Build.0 = Release|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Release|x86.ActiveCfg = Release|Any CPU + {25D1167C-E942-49FD-B125-242062EB4D70}.Release|x86.Build.0 = Release|Any CPU {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Debug|x64.ActiveCfg = Debug|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Debug|x64.Build.0 = Debug|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Debug|x86.ActiveCfg = Debug|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Debug|x86.Build.0 = Debug|Any CPU {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Release|Any CPU.Build.0 = Release|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Release|x64.ActiveCfg = Release|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Release|x64.Build.0 = Release|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Release|x86.ActiveCfg = Release|Any CPU + {DB95AE93-5D9E-41FF-AF2D-41DA28F98D78}.Release|x86.Build.0 = Release|Any CPU {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Debug|x64.ActiveCfg = Debug|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Debug|x64.Build.0 = Debug|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Debug|x86.ActiveCfg = Debug|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Debug|x86.Build.0 = Debug|Any CPU {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Release|Any CPU.ActiveCfg = Release|Any CPU {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Release|Any CPU.Build.0 = Release|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Release|x64.ActiveCfg = Release|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Release|x64.Build.0 = Release|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Release|x86.ActiveCfg = Release|Any CPU + {DE7CD837-BA7E-4488-891E-8DFD14EB529B}.Release|x86.Build.0 = Release|Any CPU {A943C3B6-9BE3-443D-A561-212E203A8A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A943C3B6-9BE3-443D-A561-212E203A8A83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Debug|x64.ActiveCfg = Debug|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Debug|x64.Build.0 = Debug|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Debug|x86.ActiveCfg = Debug|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Debug|x86.Build.0 = Debug|Any CPU {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|Any CPU.ActiveCfg = Release|Any CPU {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|Any CPU.Build.0 = Release|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|x64.ActiveCfg = Release|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|x64.Build.0 = Release|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|x86.ActiveCfg = Release|Any CPU + {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|x86.Build.0 = Release|Any CPU {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|Any CPU.Build.0 = Debug|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|x64.ActiveCfg = Debug|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|x64.Build.0 = Debug|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|x86.ActiveCfg = Debug|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|x86.Build.0 = Debug|Any CPU {291BFE74-F64A-D263-170E-6CF138F96265}.Release|Any CPU.ActiveCfg = Release|Any CPU {291BFE74-F64A-D263-170E-6CF138F96265}.Release|Any CPU.Build.0 = Release|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Release|x64.ActiveCfg = Release|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Release|x64.Build.0 = Release|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Release|x86.ActiveCfg = Release|Any CPU + {291BFE74-F64A-D263-170E-6CF138F96265}.Release|x86.Build.0 = Release|Any CPU {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Debug|x64.ActiveCfg = Debug|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Debug|x64.Build.0 = Debug|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Debug|x86.ActiveCfg = Debug|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Debug|x86.Build.0 = Debug|Any CPU {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Release|Any CPU.ActiveCfg = Release|Any CPU {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Release|Any CPU.Build.0 = Release|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Release|x64.ActiveCfg = Release|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Release|x64.Build.0 = Release|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Release|x86.ActiveCfg = Release|Any CPU + {7905A0A7-25D3-6FF8-7C24-E97BF4ABBCCD}.Release|x86.Build.0 = Release|Any CPU {79124D87-74BE-F687-684B-51288C64ABE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79124D87-74BE-F687-684B-51288C64ABE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Debug|x64.Build.0 = Debug|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Debug|x86.ActiveCfg = Debug|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Debug|x86.Build.0 = Debug|Any CPU {79124D87-74BE-F687-684B-51288C64ABE2}.Release|Any CPU.ActiveCfg = Release|Any CPU {79124D87-74BE-F687-684B-51288C64ABE2}.Release|Any CPU.Build.0 = Release|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Release|x64.ActiveCfg = Release|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Release|x64.Build.0 = Release|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Release|x86.ActiveCfg = Release|Any CPU + {79124D87-74BE-F687-684B-51288C64ABE2}.Release|x86.Build.0 = Release|Any CPU {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Debug|x64.ActiveCfg = Debug|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Debug|x64.Build.0 = Debug|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Debug|x86.ActiveCfg = Debug|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Debug|x86.Build.0 = Debug|Any CPU {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Release|Any CPU.Build.0 = Release|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Release|x64.ActiveCfg = Release|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Release|x64.Build.0 = Release|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Release|x86.ActiveCfg = Release|Any CPU + {00FC121D-0DBA-34F8-CE25-B5E980C9AF9B}.Release|x86.Build.0 = Release|Any CPU {8EAD2216-526E-8735-4400-EF1B275501FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8EAD2216-526E-8735-4400-EF1B275501FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Debug|x64.ActiveCfg = Debug|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Debug|x64.Build.0 = Debug|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Debug|x86.ActiveCfg = Debug|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Debug|x86.Build.0 = Debug|Any CPU {8EAD2216-526E-8735-4400-EF1B275501FE}.Release|Any CPU.ActiveCfg = Release|Any CPU {8EAD2216-526E-8735-4400-EF1B275501FE}.Release|Any CPU.Build.0 = Release|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Release|x64.ActiveCfg = Release|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Release|x64.Build.0 = Release|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Release|x86.ActiveCfg = Release|Any CPU + {8EAD2216-526E-8735-4400-EF1B275501FE}.Release|x86.Build.0 = Release|Any CPU {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Debug|x64.ActiveCfg = Debug|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Debug|x64.Build.0 = Debug|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Debug|x86.ActiveCfg = Debug|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Debug|x86.Build.0 = Debug|Any CPU {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Release|Any CPU.Build.0 = Release|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Release|x64.ActiveCfg = Release|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Release|x64.Build.0 = Release|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Release|x86.ActiveCfg = Release|Any CPU + {6E221A46-05EA-8EFA-FC50-44F1D99321CB}.Release|x86.Build.0 = Release|Any CPU {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Debug|x64.ActiveCfg = Debug|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Debug|x64.Build.0 = Debug|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Debug|x86.ActiveCfg = Debug|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Debug|x86.Build.0 = Debug|Any CPU {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Release|Any CPU.ActiveCfg = Release|Any CPU {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Release|Any CPU.Build.0 = Release|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Release|x64.ActiveCfg = Release|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Release|x64.Build.0 = Release|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Release|x86.ActiveCfg = Release|Any CPU + {253CFD5F-BA04-BF1F-34C3-26D8941C3B8D}.Release|x86.Build.0 = Release|Any CPU {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Debug|x64.ActiveCfg = Debug|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Debug|x64.Build.0 = Debug|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Debug|x86.ActiveCfg = Debug|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Debug|x86.Build.0 = Debug|Any CPU {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Release|Any CPU.Build.0 = Release|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Release|x64.ActiveCfg = Release|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Release|x64.Build.0 = Release|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Release|x86.ActiveCfg = Release|Any CPU + {1B2B2859-16F2-545A-0283-ECB16FCA474C}.Release|x86.Build.0 = Release|Any CPU {121694BC-F51B-36E6-E960-127ADF96FE43}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {121694BC-F51B-36E6-E960-127ADF96FE43}.Debug|Any CPU.Build.0 = Debug|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Debug|x64.ActiveCfg = Debug|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Debug|x64.Build.0 = Debug|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Debug|x86.ActiveCfg = Debug|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Debug|x86.Build.0 = Debug|Any CPU {121694BC-F51B-36E6-E960-127ADF96FE43}.Release|Any CPU.ActiveCfg = Release|Any CPU {121694BC-F51B-36E6-E960-127ADF96FE43}.Release|Any CPU.Build.0 = Release|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Release|x64.ActiveCfg = Release|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Release|x64.Build.0 = Release|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Release|x86.ActiveCfg = Release|Any CPU + {121694BC-F51B-36E6-E960-127ADF96FE43}.Release|x86.Build.0 = Release|Any CPU {A20861A9-411E-6150-BF5C-69E8196E5D22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A20861A9-411E-6150-BF5C-69E8196E5D22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Debug|x64.ActiveCfg = Debug|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Debug|x64.Build.0 = Debug|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Debug|x86.ActiveCfg = Debug|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Debug|x86.Build.0 = Debug|Any CPU {A20861A9-411E-6150-BF5C-69E8196E5D22}.Release|Any CPU.ActiveCfg = Release|Any CPU {A20861A9-411E-6150-BF5C-69E8196E5D22}.Release|Any CPU.Build.0 = Release|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Release|x64.ActiveCfg = Release|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Release|x64.Build.0 = Release|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Release|x86.ActiveCfg = Release|Any CPU + {A20861A9-411E-6150-BF5C-69E8196E5D22}.Release|x86.Build.0 = Release|Any CPU {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Debug|x64.ActiveCfg = Debug|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Debug|x64.Build.0 = Debug|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Debug|x86.ActiveCfg = Debug|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Debug|x86.Build.0 = Debug|Any CPU {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Release|Any CPU.ActiveCfg = Release|Any CPU {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Release|Any CPU.Build.0 = Release|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Release|x64.ActiveCfg = Release|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Release|x64.Build.0 = Release|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Release|x86.ActiveCfg = Release|Any CPU + {39616C08-0F2A-4EBB-6FB2-39BCD999ACA1}.Release|x86.Build.0 = Release|Any CPU {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Debug|x64.ActiveCfg = Debug|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Debug|x64.Build.0 = Debug|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Debug|x86.ActiveCfg = Debug|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Debug|x86.Build.0 = Debug|Any CPU {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Release|Any CPU.ActiveCfg = Release|Any CPU {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Release|Any CPU.Build.0 = Release|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Release|x64.ActiveCfg = Release|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Release|x64.Build.0 = Release|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Release|x86.ActiveCfg = Release|Any CPU + {B0117DC5-4192-48DE-B8A9-54D869E9DC8F}.Release|x86.Build.0 = Release|Any CPU {5491F846-27B1-4FD9-70B8-072B840288F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5491F846-27B1-4FD9-70B8-072B840288F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Debug|x64.ActiveCfg = Debug|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Debug|x64.Build.0 = Debug|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Debug|x86.ActiveCfg = Debug|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Debug|x86.Build.0 = Debug|Any CPU {5491F846-27B1-4FD9-70B8-072B840288F7}.Release|Any CPU.ActiveCfg = Release|Any CPU {5491F846-27B1-4FD9-70B8-072B840288F7}.Release|Any CPU.Build.0 = Release|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Release|x64.ActiveCfg = Release|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Release|x64.Build.0 = Release|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Release|x86.ActiveCfg = Release|Any CPU + {5491F846-27B1-4FD9-70B8-072B840288F7}.Release|x86.Build.0 = Release|Any CPU {1A4E9721-4029-22E8-883C-2395C9025557}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1A4E9721-4029-22E8-883C-2395C9025557}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Debug|x64.ActiveCfg = Debug|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Debug|x64.Build.0 = Debug|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Debug|x86.ActiveCfg = Debug|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Debug|x86.Build.0 = Debug|Any CPU {1A4E9721-4029-22E8-883C-2395C9025557}.Release|Any CPU.ActiveCfg = Release|Any CPU {1A4E9721-4029-22E8-883C-2395C9025557}.Release|Any CPU.Build.0 = Release|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Release|x64.ActiveCfg = Release|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Release|x64.Build.0 = Release|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Release|x86.ActiveCfg = Release|Any CPU + {1A4E9721-4029-22E8-883C-2395C9025557}.Release|x86.Build.0 = Release|Any CPU {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Debug|x64.ActiveCfg = Debug|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Debug|x64.Build.0 = Debug|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Debug|x86.ActiveCfg = Debug|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Debug|x86.Build.0 = Debug|Any CPU {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Release|Any CPU.Build.0 = Release|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Release|x64.ActiveCfg = Release|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Release|x64.Build.0 = Release|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Release|x86.ActiveCfg = Release|Any CPU + {D0705704-F704-D681-59FF-C65E5EBCDF9F}.Release|x86.Build.0 = Release|Any CPU {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Debug|x64.ActiveCfg = Debug|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Debug|x64.Build.0 = Debug|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Debug|x86.ActiveCfg = Debug|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Debug|x86.Build.0 = Debug|Any CPU {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Release|Any CPU.ActiveCfg = Release|Any CPU {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Release|Any CPU.Build.0 = Release|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Release|x64.ActiveCfg = Release|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Release|x64.Build.0 = Release|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Release|x86.ActiveCfg = Release|Any CPU + {72C83AA9-1059-6DB1-68DE-5178F70197E1}.Release|x86.Build.0 = Release|Any CPU {490FE0F3-0D68-FE00-09CB-67B262519444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {490FE0F3-0D68-FE00-09CB-67B262519444}.Debug|Any CPU.Build.0 = Debug|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Debug|x64.ActiveCfg = Debug|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Debug|x64.Build.0 = Debug|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Debug|x86.ActiveCfg = Debug|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Debug|x86.Build.0 = Debug|Any CPU {490FE0F3-0D68-FE00-09CB-67B262519444}.Release|Any CPU.ActiveCfg = Release|Any CPU {490FE0F3-0D68-FE00-09CB-67B262519444}.Release|Any CPU.Build.0 = Release|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Release|x64.ActiveCfg = Release|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Release|x64.Build.0 = Release|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Release|x86.ActiveCfg = Release|Any CPU + {490FE0F3-0D68-FE00-09CB-67B262519444}.Release|x86.Build.0 = Release|Any CPU {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Debug|x64.ActiveCfg = Debug|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Debug|x64.Build.0 = Debug|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Debug|x86.ActiveCfg = Debug|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Debug|x86.Build.0 = Debug|Any CPU {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Release|Any CPU.Build.0 = Release|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Release|x64.ActiveCfg = Release|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Release|x64.Build.0 = Release|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Release|x86.ActiveCfg = Release|Any CPU + {A87B46F3-6063-4DCD-8F3D-8D32F4562BAD}.Release|x86.Build.0 = Release|Any CPU {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Debug|x64.ActiveCfg = Debug|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Debug|x64.Build.0 = Debug|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Debug|x86.ActiveCfg = Debug|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Debug|x86.Build.0 = Debug|Any CPU {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Release|Any CPU.ActiveCfg = Release|Any CPU {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Release|Any CPU.Build.0 = Release|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Release|x64.ActiveCfg = Release|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Release|x64.Build.0 = Release|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Release|x86.ActiveCfg = Release|Any CPU + {7520BBCD-3367-4ECE-9ABF-D4B6C6361F27}.Release|x86.Build.0 = Release|Any CPU {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Debug|x64.ActiveCfg = Debug|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Debug|x64.Build.0 = Debug|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Debug|x86.ActiveCfg = Debug|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Debug|x86.Build.0 = Debug|Any CPU {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Release|Any CPU.ActiveCfg = Release|Any CPU {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Release|Any CPU.Build.0 = Release|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Release|x64.ActiveCfg = Release|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Release|x64.Build.0 = Release|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Release|x86.ActiveCfg = Release|Any CPU + {A26B3928-B89E-AE2E-BFFF-537C09B35D81}.Release|x86.Build.0 = Release|Any CPU {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|x64.ActiveCfg = Debug|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|x64.Build.0 = Debug|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|x86.ActiveCfg = Debug|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Debug|x86.Build.0 = Debug|Any CPU {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|Any CPU.Build.0 = Release|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|x64.ActiveCfg = Release|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|x64.Build.0 = Release|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|x86.ActiveCfg = Release|Any CPU + {9E4A987B-74C2-FFE1-78F7-716DEF2F613E}.Release|x86.Build.0 = Release|Any CPU {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|x64.ActiveCfg = Debug|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|x64.Build.0 = Debug|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|x86.ActiveCfg = Debug|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Debug|x86.Build.0 = Debug|Any CPU {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|Any CPU.Build.0 = Release|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|x64.ActiveCfg = Release|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|x64.Build.0 = Release|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|x86.ActiveCfg = Release|Any CPU + {FAAA3EDE-52A8-704C-8810-B1060F2C38F9}.Release|x86.Build.0 = Release|Any CPU {2A1AA816-B936-0119-984D-F6F2081F10E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2A1AA816-B936-0119-984D-F6F2081F10E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Debug|x64.ActiveCfg = Debug|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Debug|x64.Build.0 = Debug|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Debug|x86.ActiveCfg = Debug|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Debug|x86.Build.0 = Debug|Any CPU {2A1AA816-B936-0119-984D-F6F2081F10E3}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A1AA816-B936-0119-984D-F6F2081F10E3}.Release|Any CPU.Build.0 = Release|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Release|x64.ActiveCfg = Release|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Release|x64.Build.0 = Release|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Release|x86.ActiveCfg = Release|Any CPU + {2A1AA816-B936-0119-984D-F6F2081F10E3}.Release|x86.Build.0 = Release|Any CPU {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Debug|x64.ActiveCfg = Debug|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Debug|x64.Build.0 = Debug|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Debug|x86.ActiveCfg = Debug|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Debug|x86.Build.0 = Debug|Any CPU {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Release|Any CPU.ActiveCfg = Release|Any CPU {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Release|Any CPU.Build.0 = Release|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Release|x64.ActiveCfg = Release|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Release|x64.Build.0 = Release|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Release|x86.ActiveCfg = Release|Any CPU + {ECDEDABF-3AB8-8D94-2A87-8BBE2A18A758}.Release|x86.Build.0 = Release|Any CPU {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Debug|x64.ActiveCfg = Debug|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Debug|x64.Build.0 = Debug|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Debug|x86.ActiveCfg = Debug|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Debug|x86.Build.0 = Debug|Any CPU {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Release|Any CPU.Build.0 = Release|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Release|x64.ActiveCfg = Release|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Release|x64.Build.0 = Release|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Release|x86.ActiveCfg = Release|Any CPU + {ADC9F066-7F21-A823-9DB0-8788A4DE27FA}.Release|x86.Build.0 = Release|Any CPU {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Debug|x64.ActiveCfg = Debug|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Debug|x64.Build.0 = Debug|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Debug|x86.ActiveCfg = Debug|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Debug|x86.Build.0 = Debug|Any CPU {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Release|Any CPU.ActiveCfg = Release|Any CPU {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Release|Any CPU.Build.0 = Release|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Release|x64.ActiveCfg = Release|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Release|x64.Build.0 = Release|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Release|x86.ActiveCfg = Release|Any CPU + {F4972A4C-6BC3-AE06-6391-35E91D4C6515}.Release|x86.Build.0 = Release|Any CPU {57B337DC-7F63-22C8-728C-08DC2459FA23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {57B337DC-7F63-22C8-728C-08DC2459FA23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Debug|x64.ActiveCfg = Debug|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Debug|x64.Build.0 = Debug|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Debug|x86.ActiveCfg = Debug|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Debug|x86.Build.0 = Debug|Any CPU {57B337DC-7F63-22C8-728C-08DC2459FA23}.Release|Any CPU.ActiveCfg = Release|Any CPU {57B337DC-7F63-22C8-728C-08DC2459FA23}.Release|Any CPU.Build.0 = Release|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Release|x64.ActiveCfg = Release|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Release|x64.Build.0 = Release|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Release|x86.ActiveCfg = Release|Any CPU + {57B337DC-7F63-22C8-728C-08DC2459FA23}.Release|x86.Build.0 = Release|Any CPU {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Debug|x64.ActiveCfg = Debug|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Debug|x64.Build.0 = Debug|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Debug|x86.ActiveCfg = Debug|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Debug|x86.Build.0 = Debug|Any CPU {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Release|Any CPU.ActiveCfg = Release|Any CPU {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Release|Any CPU.Build.0 = Release|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Release|x64.ActiveCfg = Release|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Release|x64.Build.0 = Release|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Release|x86.ActiveCfg = Release|Any CPU + {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87}.Release|x86.Build.0 = Release|Any CPU {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Debug|x64.ActiveCfg = Debug|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Debug|x64.Build.0 = Debug|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Debug|x86.ActiveCfg = Debug|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Debug|x86.Build.0 = Debug|Any CPU {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Release|Any CPU.ActiveCfg = Release|Any CPU {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Release|Any CPU.Build.0 = Release|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Release|x64.ActiveCfg = Release|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Release|x64.Build.0 = Release|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Release|x86.ActiveCfg = Release|Any CPU + {9175CE78-D6A5-55BB-D4B1-E025BBA1B329}.Release|x86.Build.0 = Release|Any CPU {BC312318-3671-5E97-5256-467F6FAEFC0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BC312318-3671-5E97-5256-467F6FAEFC0B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Debug|x64.ActiveCfg = Debug|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Debug|x64.Build.0 = Debug|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Debug|x86.ActiveCfg = Debug|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Debug|x86.Build.0 = Debug|Any CPU {BC312318-3671-5E97-5256-467F6FAEFC0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC312318-3671-5E97-5256-467F6FAEFC0B}.Release|Any CPU.Build.0 = Release|Any CPU - {3BF6E365-19B9-A56C-890A-621ED3230455}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3BF6E365-19B9-A56C-890A-621ED3230455}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3BF6E365-19B9-A56C-890A-621ED3230455}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3BF6E365-19B9-A56C-890A-621ED3230455}.Release|Any CPU.Build.0 = Release|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Release|x64.ActiveCfg = Release|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Release|x64.Build.0 = Release|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Release|x86.ActiveCfg = Release|Any CPU + {BC312318-3671-5E97-5256-467F6FAEFC0B}.Release|x86.Build.0 = Release|Any CPU {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Debug|x64.ActiveCfg = Debug|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Debug|x64.Build.0 = Debug|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Debug|x86.ActiveCfg = Debug|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Debug|x86.Build.0 = Debug|Any CPU {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Release|Any CPU.Build.0 = Release|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Release|x64.ActiveCfg = Release|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Release|x64.Build.0 = Release|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Release|x86.ActiveCfg = Release|Any CPU + {CB02C822-0D46-8A0A-D98E-224642E5EE3C}.Release|x86.Build.0 = Release|Any CPU {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Debug|x64.ActiveCfg = Debug|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Debug|x64.Build.0 = Debug|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Debug|x86.ActiveCfg = Debug|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Debug|x86.Build.0 = Debug|Any CPU {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Release|Any CPU.Build.0 = Release|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Release|x64.ActiveCfg = Release|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Release|x64.Build.0 = Release|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Release|x86.ActiveCfg = Release|Any CPU + {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242}.Release|x86.Build.0 = Release|Any CPU {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Debug|x64.ActiveCfg = Debug|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Debug|x64.Build.0 = Debug|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Debug|x86.ActiveCfg = Debug|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Debug|x86.Build.0 = Debug|Any CPU {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Release|Any CPU.ActiveCfg = Release|Any CPU {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Release|Any CPU.Build.0 = Release|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Release|x64.ActiveCfg = Release|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Release|x64.Build.0 = Release|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Release|x86.ActiveCfg = Release|Any CPU + {9976096C-CC4E-5DDA-B34D-AA25D402703F}.Release|x86.Build.0 = Release|Any CPU {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Debug|x64.ActiveCfg = Debug|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Debug|x64.Build.0 = Debug|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Debug|x86.ActiveCfg = Debug|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Debug|x86.Build.0 = Debug|Any CPU {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Release|Any CPU.Build.0 = Release|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Release|x64.ActiveCfg = Release|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Release|x64.Build.0 = Release|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Release|x86.ActiveCfg = Release|Any CPU + {EFE04CA3-4C28-4D56-9AC3-B6C422C24626}.Release|x86.Build.0 = Release|Any CPU {167E435C-6598-F86F-1743-AF5C3B274E13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {167E435C-6598-F86F-1743-AF5C3B274E13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Debug|x64.ActiveCfg = Debug|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Debug|x64.Build.0 = Debug|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Debug|x86.ActiveCfg = Debug|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Debug|x86.Build.0 = Debug|Any CPU {167E435C-6598-F86F-1743-AF5C3B274E13}.Release|Any CPU.ActiveCfg = Release|Any CPU {167E435C-6598-F86F-1743-AF5C3B274E13}.Release|Any CPU.Build.0 = Release|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Release|x64.ActiveCfg = Release|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Release|x64.Build.0 = Release|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Release|x86.ActiveCfg = Release|Any CPU + {167E435C-6598-F86F-1743-AF5C3B274E13}.Release|x86.Build.0 = Release|Any CPU {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Debug|x64.ActiveCfg = Debug|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Debug|x64.Build.0 = Debug|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Debug|x86.ActiveCfg = Debug|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Debug|x86.Build.0 = Debug|Any CPU {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Release|Any CPU.Build.0 = Release|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Release|x64.ActiveCfg = Release|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Release|x64.Build.0 = Release|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Release|x86.ActiveCfg = Release|Any CPU + {9F873885-C33A-C49F-95B8-D0D1A5BE6968}.Release|x86.Build.0 = Release|Any CPU {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Debug|x64.ActiveCfg = Debug|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Debug|x64.Build.0 = Debug|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Debug|x86.ActiveCfg = Debug|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Debug|x86.Build.0 = Debug|Any CPU {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Release|Any CPU.ActiveCfg = Release|Any CPU {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Release|Any CPU.Build.0 = Release|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Release|x64.ActiveCfg = Release|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Release|x64.Build.0 = Release|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Release|x86.ActiveCfg = Release|Any CPU + {C0C89947-CE7A-36DC-EADF-2C2255AC1D78}.Release|x86.Build.0 = Release|Any CPU {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Debug|x64.ActiveCfg = Debug|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Debug|x64.Build.0 = Debug|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Debug|x86.ActiveCfg = Debug|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Debug|x86.Build.0 = Debug|Any CPU {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Release|Any CPU.ActiveCfg = Release|Any CPU {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Release|Any CPU.Build.0 = Release|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Release|x64.ActiveCfg = Release|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Release|x64.Build.0 = Release|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Release|x86.ActiveCfg = Release|Any CPU + {D94D599F-6B68-AB23-FF20-0FBD684A1FE7}.Release|x86.Build.0 = Release|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Debug|x64.ActiveCfg = Debug|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Debug|x64.Build.0 = Debug|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Debug|x86.ActiveCfg = Debug|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Debug|x86.Build.0 = Debug|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Release|Any CPU.Build.0 = Release|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Release|x64.ActiveCfg = Release|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Release|x64.Build.0 = Release|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Release|x86.ActiveCfg = Release|Any CPU + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36}.Release|x86.Build.0 = Release|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Debug|x64.ActiveCfg = Debug|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Debug|x64.Build.0 = Debug|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Debug|x86.ActiveCfg = Debug|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Debug|x86.Build.0 = Debug|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Release|Any CPU.Build.0 = Release|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Release|x64.ActiveCfg = Release|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Release|x64.Build.0 = Release|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Release|x86.ActiveCfg = Release|Any CPU + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0}.Release|x86.Build.0 = Release|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Debug|x64.ActiveCfg = Debug|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Debug|x64.Build.0 = Debug|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Debug|x86.ActiveCfg = Debug|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Debug|x86.Build.0 = Debug|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Release|Any CPU.Build.0 = Release|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Release|x64.ActiveCfg = Release|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Release|x64.Build.0 = Release|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Release|x86.ActiveCfg = Release|Any CPU + {4374637B-5C4B-4FEF-ACC8-6DC761362768}.Release|x86.Build.0 = Release|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Debug|x64.ActiveCfg = Debug|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Debug|x64.Build.0 = Debug|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Debug|x86.ActiveCfg = Debug|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Debug|x86.Build.0 = Debug|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Release|Any CPU.Build.0 = Release|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Release|x64.ActiveCfg = Release|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Release|x64.Build.0 = Release|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Release|x86.ActiveCfg = Release|Any CPU + {6FB140B0-92C8-4F2D-8379-135F76305153}.Release|x86.Build.0 = Release|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Debug|x64.ActiveCfg = Debug|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Debug|x64.Build.0 = Debug|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Debug|x86.ActiveCfg = Debug|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Debug|x86.Build.0 = Debug|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Release|Any CPU.Build.0 = Release|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Release|x64.ActiveCfg = Release|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Release|x64.Build.0 = Release|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Release|x86.ActiveCfg = Release|Any CPU + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B}.Release|x86.Build.0 = Release|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Debug|x64.ActiveCfg = Debug|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Debug|x64.Build.0 = Debug|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Debug|x86.ActiveCfg = Debug|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Debug|x86.Build.0 = Debug|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Release|Any CPU.Build.0 = Release|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Release|x64.ActiveCfg = Release|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Release|x64.Build.0 = Release|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Release|x86.ActiveCfg = Release|Any CPU + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0}.Release|x86.Build.0 = Release|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Debug|x64.ActiveCfg = Debug|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Debug|x64.Build.0 = Debug|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Debug|x86.ActiveCfg = Debug|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Debug|x86.Build.0 = Debug|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Release|Any CPU.Build.0 = Release|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Release|x64.ActiveCfg = Release|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Release|x64.Build.0 = Release|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Release|x86.ActiveCfg = Release|Any CPU + {3FB7F287-94F0-44CB-94F9-10FAEE447A96}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -513,15 +1036,13 @@ Global {FEC2F575-5BEF-9C87-FE82-D6CD5AE3AE87} = {E1173210-7A7A-448C-B4E4-B6FBF22FD1C7} {9175CE78-D6A5-55BB-D4B1-E025BBA1B329} = {E1173210-7A7A-448C-B4E4-B6FBF22FD1C7} {BC312318-3671-5E97-5256-467F6FAEFC0B} = {E1173210-7A7A-448C-B4E4-B6FBF22FD1C7} - {3BF6E365-19B9-A56C-890A-621ED3230455} = {BEF0C2FD-7280-45BF-9A9F-1BB804B4A8C4} - {CB02C822-0D46-8A0A-D98E-224642E5EE3C} = {3DB34546-9932-4FDF-A61C-E42E9C1FFDC9} - {6E22CA82-C65C-BA9C-BEDB-0B525CD4B242} = {3DB34546-9932-4FDF-A61C-E42E9C1FFDC9} - {9976096C-CC4E-5DDA-B34D-AA25D402703F} = {3DB34546-9932-4FDF-A61C-E42E9C1FFDC9} - {EFE04CA3-4C28-4D56-9AC3-B6C422C24626} = {3DB34546-9932-4FDF-A61C-E42E9C1FFDC9} - {167E435C-6598-F86F-1743-AF5C3B274E13} = {3DB34546-9932-4FDF-A61C-E42E9C1FFDC9} - {9F873885-C33A-C49F-95B8-D0D1A5BE6968} = {BE3C4175-29CB-4B10-A72B-45259D58A33C} - {C0C89947-CE7A-36DC-EADF-2C2255AC1D78} = {FBC48B38-56FA-4530-850A-8A68B3A959CF} - {D94D599F-6B68-AB23-FF20-0FBD684A1FE7} = {FBC48B38-56FA-4530-850A-8A68B3A959CF} + {71C741B8-E8EE-4530-9782-4BDA1CDE8D36} = {6470625C-1271-0563-CF21-654520B9D8F4} + {96D1BA1E-1FA0-4A1D-BB50-13F036C379E0} = {6470625C-1271-0563-CF21-654520B9D8F4} + {4374637B-5C4B-4FEF-ACC8-6DC761362768} = {6470625C-1271-0563-CF21-654520B9D8F4} + {6FB140B0-92C8-4F2D-8379-135F76305153} = {6470625C-1271-0563-CF21-654520B9D8F4} + {E0CC0AE7-2E2D-42E3-ABFB-3A081098CE1B} = {6470625C-1271-0563-CF21-654520B9D8F4} + {AB78D0A5-8006-4F6F-AD16-A382EAEF22B0} = {6470625C-1271-0563-CF21-654520B9D8F4} + {3FB7F287-94F0-44CB-94F9-10FAEE447A96} = {6470625C-1271-0563-CF21-654520B9D8F4} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CCEB9146-C458-415E-BB4B-E073B0B24876} diff --git a/Avalonia_Apps/Avalonia_Apps.slnx b/Avalonia_Apps/Avalonia_Apps.slnx index 955f48604..dbf459c25 100644 --- a/Avalonia_Apps/Avalonia_Apps.slnx +++ b/Avalonia_Apps/Avalonia_Apps.slnx @@ -1,4 +1,9 @@ + + + + + @@ -78,14 +83,20 @@ + + - + + + - + + + @@ -97,12 +108,20 @@ - + + + - + + + + + + + @@ -141,12 +160,22 @@ + + + + - + + + - - + + + + + + @@ -165,13 +194,16 @@ + + + + + - - diff --git a/Avalonia_Apps/DevOps/DevOps.projitems b/Avalonia_Apps/DevOps/DevOps.projitems index 962c9e48d..0646af89c 100644 --- a/Avalonia_Apps/DevOps/DevOps.projitems +++ b/Avalonia_Apps/DevOps/DevOps.projitems @@ -48,6 +48,7 @@ + From 0ef94dbe32aa5d43d5c1cd2c32e9b56505d334d6 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 4 Jun 2026 01:08:47 +0200 Subject: [PATCH 075/106] copilot-instructions --- CSharpBible/MVVM_Tutorial/.github/copilot-instructions.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CSharpBible/MVVM_Tutorial/.github/copilot-instructions.md b/CSharpBible/MVVM_Tutorial/.github/copilot-instructions.md index 14688efc9..4328d6e7d 100644 --- a/CSharpBible/MVVM_Tutorial/.github/copilot-instructions.md +++ b/CSharpBible/MVVM_Tutorial/.github/copilot-instructions.md @@ -28,6 +28,7 @@ Apply these defaults when working in this repository unless the user explicitly - Use `MSTest` in the latest practical version for new or updated tests. - Use `NSubstitute` for mocks, stubs, and substitutes in tests. - Prefer `DataRow` for parameterized single-test scenarios. +- Prefer one dedicated test project per production project with a clear scope; any combined test project should only act as an aggregate collector via project references. ## Internationalization - Keep I18N in mind when writing code, ensuring it can be easily adapted for different languages and regions. From 04c9f509aa5b196f605ae7de9243b5586487c671 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 4 Jun 2026 15:21:05 +0200 Subject: [PATCH 076/106] AA05_CommandParCalc --- .../ControlSamples.0.1.1-local.nupkg | Bin 0 -> 42166 bytes .../Avln_TerminalHost/App.axaml | 9 + .../Avln_TerminalHost/App.axaml.cs | 52 +++ .../Avln_TerminalHost.csproj | 31 ++ .../Avln_TerminalHost/Program.cs | 27 ++ .../Services/ComSpecLocator.cs | 22 ++ .../Services/HostedProcess.cs | 93 +++++ .../Services/IComSpecLocator.cs | 13 + .../Services/IHostedProcess.cs | 39 ++ .../Services/IProcessRunner.cs | 17 + .../Services/ProcessRunner.cs | 38 ++ .../ViewModels/MainWindowViewModel.cs | 148 ++++++++ .../ViewModels/ViewModelBase.cs | 10 + .../Avln_TerminalHost/Views/MainWindow.axaml | 27 ++ .../Views/MainWindow.axaml.cs | 17 + .../Views/TerminalConsoleView.axaml | 5 + .../Views/TerminalConsoleView.axaml.cs | 32 ++ .../Avln_TerminalHostTests.csproj | 26 ++ .../MainWindowViewModelTests.cs | 93 +++++ .../TestHostedProcess.cs | 81 ++++ .../Directory.Packages.props | 18 + .../Avln_TerminalHost/TerminalHost.props | 9 + ...A00-Bl001-Avalonia-Test-Console-Library.md | 19 + .../AA00-Bl002-Avalonia-Terminal-Host-App.md | 20 + .../ConsoleLib-WidgetSet-Abstraction.md | 73 ++++ ...01-Create-Avalonia-Test-Console-Library.md | 32 ++ ...-T002-Create-Avalonia-Terminal-Host-App.md | 32 ++ ...tor-Component-And-DI-Hosted-Avalonia-UI.md | 58 +++ .../Avln_TestConsole/AvaloniaTestConsole.cs | 212 +++++++++++ .../Avln_TestConsole/Avln_TestConsole.csproj | 35 ++ .../Controls/AvaloniaConsoleControl.cs | 206 ++++++++++ .../Interfaces/IAvaloniaConsole.cs | 46 +++ .../Avln_TestConsole/Models/ConsoleBuffer.cs | 352 ++++++++++++++++++ .../Models/ConsoleCharacterInfo.cs | 36 ++ .../AvaloniaTestConsoleTests.cs | 136 +++++++ .../Avln_TestConsoleTests.csproj | 35 ++ 36 files changed, 2099 insertions(+) create mode 100644 Avalonia_Apps/.packages/ControlSamples.0.1.1-local.nupkg create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Avln_TerminalHost.csproj create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Program.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ComSpecLocator.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/HostedProcess.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IComSpecLocator.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IProcessRunner.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ProcessRunner.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/ViewModelBase.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/TerminalConsoleView.axaml create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/TerminalConsoleView.axaml.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/Avln_TerminalHostTests.csproj create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/MainWindowViewModelTests.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/TestHostedProcess.cs create mode 100644 Avalonia_Apps/Avln_TerminalHost/Directory.Packages.props create mode 100644 Avalonia_Apps/Avln_TerminalHost/TerminalHost.props create mode 100644 Avalonia_Apps/DevOps/BacklogItems/AA00-Bl001-Avalonia-Test-Console-Library.md create mode 100644 Avalonia_Apps/DevOps/BacklogItems/AA00-Bl002-Avalonia-Terminal-Host-App.md create mode 100644 Avalonia_Apps/DevOps/Features/ConsoleLib-WidgetSet-Abstraction.md create mode 100644 Avalonia_Apps/DevOps/Tasks/AA00-T001-Create-Avalonia-Test-Console-Library.md create mode 100644 Avalonia_Apps/DevOps/Tasks/AA00-T002-Create-Avalonia-Terminal-Host-App.md create mode 100644 Avalonia_Apps/DevOps/Tasks/AA98-T001-Extract-Editor-Component-And-DI-Hosted-Avalonia-UI.md create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsole/AvaloniaTestConsole.cs create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsole/Avln_TestConsole.csproj create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsole/Controls/AvaloniaConsoleControl.cs create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsole/Interfaces/IAvaloniaConsole.cs create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsole/Models/ConsoleBuffer.cs create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsole/Models/ConsoleCharacterInfo.cs create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsoleTests/AvaloniaTestConsoleTests.cs create mode 100644 Avalonia_Apps/Libraries/Avln_TestConsoleTests/Avln_TestConsoleTests.csproj diff --git a/Avalonia_Apps/.packages/ControlSamples.0.1.1-local.nupkg b/Avalonia_Apps/.packages/ControlSamples.0.1.1-local.nupkg new file mode 100644 index 0000000000000000000000000000000000000000..f71a63edb55703f1fe192ea67b612e52e09b2c7f GIT binary patch literal 42166 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 + + + + diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml.cs new file mode 100644 index 000000000..bd47987fe --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml.cs @@ -0,0 +1,52 @@ +using System; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avln_TerminalHost.Services; +using Avln_TerminalHost.ViewModels; +using Avln_TerminalHost.Views; +using Microsoft.Extensions.DependencyInjection; + +namespace Avln_TerminalHost; + +/// +/// Represents the Avalonia application for the terminal host. +/// +public partial class App : Application +{ + /// + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + /// + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + var services = new ServiceCollection(); + ConfigureServices(services); + Services = services.BuildServiceProvider(); + + desktop.MainWindow = new MainWindow + { + DataContext = Services.GetRequiredService(), + }; + } + + base.OnFrameworkInitializationCompleted(); + } + + /// + /// Gets the configured service provider. + /// + public IServiceProvider? Services { get; private set; } + + private static void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Avln_TerminalHost.csproj b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Avln_TerminalHost.csproj new file mode 100644 index 000000000..5d5503d1d --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Avln_TerminalHost.csproj @@ -0,0 +1,31 @@ + + + + WinExe + net8.0;net9.0 + true + true + Debug;Release;DesktopOnly + + + + + + + + + + None + All + + + + + + + + + + + + diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Program.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Program.cs new file mode 100644 index 000000000..e86129a23 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Program.cs @@ -0,0 +1,27 @@ +using Avalonia; + +namespace Avln_TerminalHost; + +/// +/// Defines the entry point for the terminal host application. +/// +public sealed class Program +{ + /// + /// Starts the Avalonia desktop application. + /// + /// The startup arguments. + [STAThread] + public static void Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + /// + /// Builds the Avalonia application. + /// + /// The configured application builder. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ComSpecLocator.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ComSpecLocator.cs new file mode 100644 index 000000000..6f8615ea9 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ComSpecLocator.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; + +namespace Avln_TerminalHost.Services; + +/// +/// Resolves the Windows command interpreter path from the environment. +/// +public sealed class ComSpecLocator : IComSpecLocator +{ + /// + public string GetShellPath() + { + var comSpec = Environment.GetEnvironmentVariable("ComSpec"); + if (!string.IsNullOrWhiteSpace(comSpec) && File.Exists(comSpec)) + { + return comSpec; + } + + return "cmd.exe"; + } +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/HostedProcess.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/HostedProcess.cs new file mode 100644 index 000000000..a72389eb3 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/HostedProcess.cs @@ -0,0 +1,93 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace Avln_TerminalHost.Services; + +/// +/// Wraps a running process and forwards redirected streams as events. +/// +public sealed class HostedProcess : IHostedProcess +{ + private readonly Process _process; + + /// + /// Initializes a new instance of the class. + /// + /// The underlying process. + public HostedProcess(Process process) + { + _process = process; + _process.OutputDataReceived += OnOutputDataReceived; + _process.ErrorDataReceived += OnErrorDataReceived; + _process.Exited += OnExited; + _process.BeginOutputReadLine(); + _process.BeginErrorReadLine(); + } + + /// + public event EventHandler? StandardOutputReceived; + + /// + public event EventHandler? StandardErrorReceived; + + /// + public event EventHandler? Exited; + + /// + public async Task WriteLineAsync(string text, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + await _process.StandardInput.WriteLineAsync(text).ConfigureAwait(false); + await _process.StandardInput.FlushAsync(cancellationToken).ConfigureAwait(false); + } + + /// + public Task StopAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!_process.HasExited) + { + _process.Kill(true); + } + + return Task.CompletedTask; + } + + /// + public async ValueTask DisposeAsync() + { + _process.OutputDataReceived -= OnOutputDataReceived; + _process.ErrorDataReceived -= OnErrorDataReceived; + _process.Exited -= OnExited; + + if (!_process.HasExited) + { + _process.Kill(true); + await _process.WaitForExitAsync().ConfigureAwait(false); + } + + _process.Dispose(); + } + + private void OnOutputDataReceived(object sender, DataReceivedEventArgs e) + { + if (e.Data is not null) + { + StandardOutputReceived?.Invoke(this, e.Data); + } + } + + private void OnErrorDataReceived(object sender, DataReceivedEventArgs e) + { + if (e.Data is not null) + { + StandardErrorReceived?.Invoke(this, e.Data); + } + } + + private void OnExited(object? sender, EventArgs e) + => Exited?.Invoke(this, _process.ExitCode); +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IComSpecLocator.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IComSpecLocator.cs new file mode 100644 index 000000000..13ff322fd --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IComSpecLocator.cs @@ -0,0 +1,13 @@ +namespace Avln_TerminalHost.Services; + +/// +/// Resolves the command interpreter path used by the terminal host. +/// +public interface IComSpecLocator +{ + /// + /// Gets the shell executable path. + /// + /// The resolved executable path. + string GetShellPath(); +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs new file mode 100644 index 000000000..046b2de7d --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Avln_TerminalHost.Services; + +/// +/// Represents a running child process used by the terminal host. +/// +public interface IHostedProcess : IAsyncDisposable +{ + /// + /// Occurs when standard output data is received. + /// + event EventHandler? StandardOutputReceived; + + /// + /// Occurs when standard error data is received. + /// + event EventHandler? StandardErrorReceived; + + /// + /// Occurs when the process exits. + /// + event EventHandler? Exited; + + /// + /// Writes a line to the process standard input. + /// + /// The text to write. + /// The cancellation token. + Task WriteLineAsync(string text, CancellationToken cancellationToken = default); + + /// + /// Stops the process if it is still running. + /// + /// The cancellation token. + Task StopAsync(CancellationToken cancellationToken = default); +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IProcessRunner.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IProcessRunner.cs new file mode 100644 index 000000000..774c025b4 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IProcessRunner.cs @@ -0,0 +1,17 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Avln_TerminalHost.Services; + +/// +/// Starts terminal child processes for the host application. +/// +public interface IProcessRunner +{ + /// + /// Starts the configured shell process. + /// + /// The cancellation token. + /// The hosted process abstraction. + Task StartAsync(CancellationToken cancellationToken = default); +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ProcessRunner.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ProcessRunner.cs new file mode 100644 index 000000000..382aff577 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/ProcessRunner.cs @@ -0,0 +1,38 @@ +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace Avln_TerminalHost.Services; + +/// +/// Starts `%ComSpec%` as a redirected child process. +/// +public sealed class ProcessRunner(IComSpecLocator comSpecLocator) : IProcessRunner +{ + private readonly IComSpecLocator _comSpecLocator = comSpecLocator; + + /// + public Task StartAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + var startInfo = new ProcessStartInfo + { + FileName = _comSpecLocator.GetShellPath(), + RedirectStandardInput = true, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + var process = new Process + { + StartInfo = startInfo, + EnableRaisingEvents = true, + }; + + process.Start(); + return Task.FromResult(new HostedProcess(process)); + } +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs new file mode 100644 index 000000000..25f86d8d2 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,148 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Avln_TestConsole; +using Avln_TestConsole.Interfaces; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Avln_TerminalHost.Services; + +namespace Avln_TerminalHost.ViewModels; + +/// +/// Coordinates the terminal host UI and the hosted shell process. +/// +public partial class MainWindowViewModel : ViewModelBase, IAsyncDisposable +{ + private readonly IProcessRunner _processRunner; + private IHostedProcess? _hostedProcess; + + /// + /// Initializes a new instance of the class. + /// + /// The process runner used to start the shell. + public MainWindowViewModel(IProcessRunner processRunner) + { + _processRunner = processRunner; + Console = new AvaloniaTestConsole + { + WindowWidth = 120, + WindowHeight = 40, + }; + } + + /// + /// Gets the reusable console instance displayed by the UI. + /// + public IAvaloniaConsole Console { get; } + + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(StartCommand))] + [NotifyCanExecuteChangedFor(nameof(SendInputCommand))] + [NotifyCanExecuteChangedFor(nameof(StopCommand))] + private bool _isRunning; + + [ObservableProperty] + [NotifyCanExecuteChangedFor(nameof(SendInputCommand))] + private string _inputText = string.Empty; + + /// + /// Starts the terminal process if it is not already running. + /// + [RelayCommand(CanExecute = nameof(CanStart))] + private async Task StartAsync() + { + if (_hostedProcess is not null) + { + return; + } + + Console.Clear(); + Console.Title = "Terminal Host"; + _hostedProcess = await _processRunner.StartAsync().ConfigureAwait(false); + _hostedProcess.StandardOutputReceived += OnStandardOutputReceived; + _hostedProcess.StandardErrorReceived += OnStandardErrorReceived; + _hostedProcess.Exited += OnExited; + IsRunning = true; + Console.WriteLine("Started shell process.", ConsoleColor.Gray, ConsoleColor.Black); + } + + /// + /// Sends the current input line to the hosted process. + /// + [RelayCommand(CanExecute = nameof(CanSendInput))] + private async Task SendInputAsync() + { + if (_hostedProcess is null) + { + return; + } + + var currentInput = InputText; + if (string.IsNullOrWhiteSpace(currentInput)) + { + return; + } + + await _hostedProcess.WriteLineAsync(currentInput, CancellationToken.None).ConfigureAwait(false); + Console.WriteLine($"> {currentInput}", ConsoleColor.Green, ConsoleColor.Black); + InputText = string.Empty; + } + + /// + /// Stops the running shell process. + /// + [RelayCommand(CanExecute = nameof(CanStop))] + private async Task StopAsync() + { + if (_hostedProcess is null) + { + return; + } + + await _hostedProcess.StopAsync(CancellationToken.None).ConfigureAwait(false); + Console.WriteLine("Stopping shell process.", ConsoleColor.Yellow, ConsoleColor.Black); + } + + /// + public async ValueTask DisposeAsync() + { + if (_hostedProcess is not null) + { + await DetachAndDisposeProcessAsync(_hostedProcess).ConfigureAwait(false); + _hostedProcess = null; + } + } + + private bool CanStart() => !IsRunning; + + private bool CanSendInput() => IsRunning && !string.IsNullOrWhiteSpace(InputText); + + private bool CanStop() => IsRunning; + + private void OnStandardOutputReceived(object? sender, string text) + => Console.WriteLine(text, ConsoleColor.Gray, ConsoleColor.Black); + + private void OnStandardErrorReceived(object? sender, string text) + => Console.WriteLine(text, ConsoleColor.White, ConsoleColor.DarkRed); + + private async void OnExited(object? sender, int exitCode) + { + Console.WriteLine($"Process exited with code {exitCode}.", ConsoleColor.Yellow, ConsoleColor.Black); + IsRunning = false; + + if (_hostedProcess is not null) + { + await DetachAndDisposeProcessAsync(_hostedProcess).ConfigureAwait(false); + _hostedProcess = null; + } + } + + private async Task DetachAndDisposeProcessAsync(IHostedProcess hostedProcess) + { + hostedProcess.StandardOutputReceived -= OnStandardOutputReceived; + hostedProcess.StandardErrorReceived -= OnStandardErrorReceived; + hostedProcess.Exited -= OnExited; + await hostedProcess.DisposeAsync().ConfigureAwait(false); + } +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/ViewModelBase.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/ViewModelBase.cs new file mode 100644 index 000000000..cd3e5a41f --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/ViewModelBase.cs @@ -0,0 +1,10 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Avln_TerminalHost.ViewModels; + +/// +/// Provides the common observable base class for terminal host view models. +/// +public abstract partial class ViewModelBase : ObservableObject +{ +} diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml new file mode 100644 index 000000000..475fc2c27 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml @@ -0,0 +1,27 @@ + + + + + + + + + public sealed class HostedProcess : IHostedProcess { + private static readonly TimeSpan PartialFlushDelay = TimeSpan.FromMilliseconds(100); private readonly Process _process; + private readonly CancellationTokenSource _readCancellationTokenSource = new(); + private readonly Task _standardOutputTask; + private readonly Task _standardErrorTask; /// /// Initializes a new instance of the class. @@ -19,19 +23,35 @@ public sealed class HostedProcess : IHostedProcess public HostedProcess(Process process) { _process = process; - _process.OutputDataReceived += OnOutputDataReceived; - _process.ErrorDataReceived += OnErrorDataReceived; _process.Exited += OnExited; - _process.BeginOutputReadLine(); - _process.BeginErrorReadLine(); + + var standardOutputReader = new OutputChunkReader( + _process.StandardOutput, + PartialFlushDelay, + text => StandardOutputReceived?.Invoke(this, text), + text => StandardOutputPartialReceived?.Invoke(this, text)); + var standardErrorReader = new OutputChunkReader( + _process.StandardError, + PartialFlushDelay, + text => StandardErrorReceived?.Invoke(this, text), + text => StandardErrorPartialReceived?.Invoke(this, text)); + + _standardOutputTask = standardOutputReader.RunAsync(_readCancellationTokenSource.Token); + _standardErrorTask = standardErrorReader.RunAsync(_readCancellationTokenSource.Token); } /// public event EventHandler? StandardOutputReceived; + /// + public event EventHandler? StandardOutputPartialReceived; + /// public event EventHandler? StandardErrorReceived; + /// + public event EventHandler? StandardErrorPartialReceived; + /// public event EventHandler? Exited; @@ -59,9 +79,8 @@ public Task StopAsync(CancellationToken cancellationToken = default) /// public async ValueTask DisposeAsync() { - _process.OutputDataReceived -= OnOutputDataReceived; - _process.ErrorDataReceived -= OnErrorDataReceived; _process.Exited -= OnExited; + _readCancellationTokenSource.Cancel(); if (!_process.HasExited) { @@ -69,23 +88,16 @@ public async ValueTask DisposeAsync() await _process.WaitForExitAsync().ConfigureAwait(false); } - _process.Dispose(); - } - - private void OnOutputDataReceived(object sender, DataReceivedEventArgs e) - { - if (e.Data is not null) + try { - StandardOutputReceived?.Invoke(this, e.Data); + await Task.WhenAll(_standardOutputTask, _standardErrorTask).ConfigureAwait(false); } - } - - private void OnErrorDataReceived(object sender, DataReceivedEventArgs e) - { - if (e.Data is not null) + catch (OperationCanceledException) { - StandardErrorReceived?.Invoke(this, e.Data); } + + _readCancellationTokenSource.Dispose(); + _process.Dispose(); } private void OnExited(object? sender, EventArgs e) diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs index 046b2de7d..58e018801 100644 --- a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs @@ -14,11 +14,21 @@ public interface IHostedProcess : IAsyncDisposable /// event EventHandler? StandardOutputReceived; + /// + /// Occurs when partial standard output data is received before a line terminator arrives. + /// + event EventHandler? StandardOutputPartialReceived; + /// /// Occurs when standard error data is received. /// event EventHandler? StandardErrorReceived; + /// + /// Occurs when partial standard error data is received before a line terminator arrives. + /// + event EventHandler? StandardErrorPartialReceived; + /// /// Occurs when the process exits. /// diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs index 25f86d8d2..2ead4f7d7 100644 --- a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs @@ -1,8 +1,10 @@ using System; +using System.Text; using System.Threading; using System.Threading.Tasks; using Avln_TestConsole; using Avln_TestConsole.Interfaces; +using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Avln_TerminalHost.Services; @@ -15,6 +17,7 @@ namespace Avln_TerminalHost.ViewModels; public partial class MainWindowViewModel : ViewModelBase, IAsyncDisposable { private readonly IProcessRunner _processRunner; + private readonly StringBuilder _interactiveInputBuffer = new(); private IHostedProcess? _hostedProcess; /// @@ -59,14 +62,83 @@ private async Task StartAsync() Console.Clear(); Console.Title = "Terminal Host"; - _hostedProcess = await _processRunner.StartAsync().ConfigureAwait(false); + _hostedProcess = await _processRunner.StartAsync(); _hostedProcess.StandardOutputReceived += OnStandardOutputReceived; + _hostedProcess.StandardOutputPartialReceived += OnStandardOutputPartialReceived; _hostedProcess.StandardErrorReceived += OnStandardErrorReceived; + _hostedProcess.StandardErrorPartialReceived += OnStandardErrorPartialReceived; _hostedProcess.Exited += OnExited; + _interactiveInputBuffer.Clear(); + InputText = string.Empty; IsRunning = true; Console.WriteLine("Started shell process.", ConsoleColor.Gray, ConsoleColor.Black); } + /// + /// Processes direct text input coming from the terminal control. + /// + /// The entered text. + public Task HandleConsoleTextInputAsync(string? text) + { + if (!IsRunning || _hostedProcess is null || string.IsNullOrEmpty(text)) + { + return Task.CompletedTask; + } + + foreach (var character in text) + { + if (character == '\r' || character == '\n') + { + continue; + } + + _interactiveInputBuffer.Append(character); + Console.Write(character); + } + + InputText = _interactiveInputBuffer.ToString(); + return Task.CompletedTask; + } + + /// + /// Processes direct special-key input coming from the terminal control. + /// + /// The pressed console key. + public async Task HandleConsoleSpecialKeyAsync(ConsoleKey key) + { + if (!IsRunning || _hostedProcess is null) + { + return; + } + + switch (key) + { + case ConsoleKey.Backspace: + if (_interactiveInputBuffer.Length > 0) + { + _interactiveInputBuffer.Length -= 1; + Console.Write("\b \b"); + InputText = _interactiveInputBuffer.ToString(); + } + + break; + + case ConsoleKey.Tab: + _interactiveInputBuffer.Append('\t'); + Console.Write('\t'); + InputText = _interactiveInputBuffer.ToString(); + break; + + case ConsoleKey.Enter: + var currentInput = _interactiveInputBuffer.ToString(); + await _hostedProcess.WriteLineAsync(currentInput, CancellationToken.None); + _interactiveInputBuffer.Clear(); + InputText = string.Empty; + Console.WriteLine(); + break; + } + } + /// /// Sends the current input line to the hosted process. /// @@ -84,8 +156,9 @@ private async Task SendInputAsync() return; } - await _hostedProcess.WriteLineAsync(currentInput, CancellationToken.None).ConfigureAwait(false); + await _hostedProcess.WriteLineAsync(currentInput, CancellationToken.None); Console.WriteLine($"> {currentInput}", ConsoleColor.Green, ConsoleColor.Black); + _interactiveInputBuffer.Clear(); InputText = string.Empty; } @@ -100,7 +173,7 @@ private async Task StopAsync() return; } - await _hostedProcess.StopAsync(CancellationToken.None).ConfigureAwait(false); + await _hostedProcess.StopAsync(CancellationToken.None); Console.WriteLine("Stopping shell process.", ConsoleColor.Yellow, ConsoleColor.Black); } @@ -109,9 +182,12 @@ public async ValueTask DisposeAsync() { if (_hostedProcess is not null) { - await DetachAndDisposeProcessAsync(_hostedProcess).ConfigureAwait(false); + await DetachAndDisposeProcessAsync(_hostedProcess); _hostedProcess = null; } + + _interactiveInputBuffer.Clear(); + InputText = string.Empty; } private bool CanStart() => !IsRunning; @@ -121,28 +197,51 @@ public async ValueTask DisposeAsync() private bool CanStop() => IsRunning; private void OnStandardOutputReceived(object? sender, string text) - => Console.WriteLine(text, ConsoleColor.Gray, ConsoleColor.Black); + => Dispatcher.UIThread.Post(() => Console.WriteLine(text, ConsoleColor.Gray, ConsoleColor.Black)); + + private void OnStandardOutputPartialReceived(object? sender, string text) + => Dispatcher.UIThread.Post(() => Console.Write(text)); private void OnStandardErrorReceived(object? sender, string text) - => Console.WriteLine(text, ConsoleColor.White, ConsoleColor.DarkRed); + => Dispatcher.UIThread.Post(() => Console.WriteLine(text, ConsoleColor.White, ConsoleColor.DarkRed)); + + private void OnStandardErrorPartialReceived(object? sender, string text) + => Dispatcher.UIThread.Post(() => + { + var previousForegroundColor = Console.ForegroundColor; + var previousBackgroundColor = Console.BackgroundColor; + Console.ForegroundColor = ConsoleColor.White; + Console.BackgroundColor = ConsoleColor.DarkRed; + Console.Write(text); + Console.ForegroundColor = previousForegroundColor; + Console.BackgroundColor = previousBackgroundColor; + }); private async void OnExited(object? sender, int exitCode) { - Console.WriteLine($"Process exited with code {exitCode}.", ConsoleColor.Yellow, ConsoleColor.Black); - IsRunning = false; - - if (_hostedProcess is not null) + await Dispatcher.UIThread.InvokeAsync(async () => { - await DetachAndDisposeProcessAsync(_hostedProcess).ConfigureAwait(false); - _hostedProcess = null; - } + Console.WriteLine($"Process exited with code {exitCode}.", ConsoleColor.Yellow, ConsoleColor.Black); + IsRunning = false; + + if (_hostedProcess is not null) + { + await DetachAndDisposeProcessAsync(_hostedProcess); + _hostedProcess = null; + } + + _interactiveInputBuffer.Clear(); + InputText = string.Empty; + }); } private async Task DetachAndDisposeProcessAsync(IHostedProcess hostedProcess) { hostedProcess.StandardOutputReceived -= OnStandardOutputReceived; + hostedProcess.StandardOutputPartialReceived -= OnStandardOutputPartialReceived; hostedProcess.StandardErrorReceived -= OnStandardErrorReceived; + hostedProcess.StandardErrorPartialReceived -= OnStandardErrorPartialReceived; hostedProcess.Exited -= OnExited; - await hostedProcess.DisposeAsync().ConfigureAwait(false); + await hostedProcess.DisposeAsync(); } } diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml index 475fc2c27..d0be4b103 100644 --- a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml @@ -17,7 +17,7 @@ public partial class TerminalConsoleView : UserControl { + private static readonly TimeSpan ResizeDebounceDelay = TimeSpan.FromMilliseconds(100); private readonly ContentControl? _host; + private CancellationTokenSource? _resizeCancellationTokenSource; /// /// Initializes a new instance of the class. @@ -18,6 +26,9 @@ public TerminalConsoleView() InitializeComponent(); _host = this.FindControl("PART_Host"); DataContextChanged += OnDataContextChanged; + SizeChanged += OnSizeChanged; + AttachedToVisualTree += OnAttachedToVisualTree; + DetachedFromVisualTree += OnDetachedFromVisualTree; } private void OnDataContextChanged(object? sender, System.EventArgs e) @@ -27,6 +38,153 @@ private void OnDataContextChanged(object? sender, System.EventArgs e) return; } - _host.Content = (DataContext as MainWindowViewModel)?.Console.Control; + if (_host.Content is AvaloniaConsoleControl previousControl) + { + previousControl.TextInput -= OnConsoleTextInput; + previousControl.KeyDown -= OnConsoleKeyDown; + } + + var consoleControl = (DataContext as MainWindowViewModel)?.Console.Control; + if (consoleControl is not null) + { + consoleControl.TextInput += OnConsoleTextInput; + consoleControl.KeyDown += OnConsoleKeyDown; + } + + _host.Content = consoleControl; + ScheduleConsoleResize(); + } + + private void OnSizeChanged(object? sender, SizeChangedEventArgs e) + => ScheduleConsoleResize(); + + private void OnAttachedToVisualTree(object? sender, Avalonia.VisualTreeAttachmentEventArgs e) + => ScheduleConsoleResize(); + + private void OnDetachedFromVisualTree(object? sender, Avalonia.VisualTreeAttachmentEventArgs e) + => CancelPendingResize(); + + private void ScheduleConsoleResize() + { + CancelPendingResize(); + _resizeCancellationTokenSource = new CancellationTokenSource(); + _ = ApplyConsoleResizeAsync(_resizeCancellationTokenSource); + } + + private async Task ApplyConsoleResizeAsync(CancellationTokenSource resizeCancellationTokenSource) + { + try + { + await Task.Delay(ResizeDebounceDelay, resizeCancellationTokenSource.Token); + await Dispatcher.UIThread.InvokeAsync(() => ApplyConsoleResize(resizeCancellationTokenSource)); + } + catch (OperationCanceledException) + { + } + finally + { + resizeCancellationTokenSource.Dispose(); + if (ReferenceEquals(_resizeCancellationTokenSource, resizeCancellationTokenSource)) + { + _resizeCancellationTokenSource = null; + } + } + } + + private void ApplyConsoleResize(CancellationTokenSource resizeCancellationTokenSource) + { + if (!ReferenceEquals(_resizeCancellationTokenSource, resizeCancellationTokenSource) || _host?.Content is not AvaloniaConsoleControl consoleControl) + { + return; + } + + if (DataContext is not MainWindowViewModel viewModel) + { + return; + } + + var width = GetFirstPositive(_host.Bounds.Width, Bounds.Width, _host.Width, Width); + var height = GetFirstPositive(_host.Bounds.Height, Bounds.Height, _host.Height, Height); + if (width <= 0d || height <= 0d) + { + return; + } + + var cellSize = consoleControl.CharacterCellSize; + var columnCount = Math.Max(1, (int)Math.Floor(width / cellSize.Width)); + var rowCount = Math.Max(1, (int)Math.Floor(height / cellSize.Height)); + if (viewModel.Console.WindowWidth == columnCount && viewModel.Console.WindowHeight == rowCount) + { + return; + } + + viewModel.Console.WindowWidth = columnCount; + viewModel.Console.WindowHeight = rowCount; + } + + private static double GetFirstPositive(params double[] values) + { + foreach (var value in values) + { + if (!double.IsNaN(value) && value > 0d) + { + return value; + } + } + + return 0d; + } + + private void CancelPendingResize() + { + var resizeCancellationTokenSource = _resizeCancellationTokenSource; + _resizeCancellationTokenSource = null; + resizeCancellationTokenSource?.Cancel(); + } + + private async void OnConsoleTextInput(object? sender, TextInputEventArgs e) + { + if (DataContext is not MainWindowViewModel viewModel || string.IsNullOrEmpty(e.Text)) + { + return; + } + + await viewModel.HandleConsoleTextInputAsync(e.Text); + e.Handled = true; + } + + private async void OnConsoleKeyDown(object? sender, KeyEventArgs e) + { + if (DataContext is not MainWindowViewModel viewModel) + { + return; + } + + if (!TryMapConsoleKey(e.Key, out var consoleKey)) + { + return; + } + + await viewModel.HandleConsoleSpecialKeyAsync(consoleKey); + e.Handled = true; + } + + private static bool TryMapConsoleKey(Key key, out ConsoleKey consoleKey) + { + switch (key) + { + case Key.Enter: + consoleKey = ConsoleKey.Enter; + return true; + case Key.Back: + consoleKey = ConsoleKey.Backspace; + return true; + case Key.Tab: + consoleKey = ConsoleKey.Tab; + return true; + default: + consoleKey = default; + return false; + } } } From 8af1235be95ee487cec8dc80d122c0e51166a028 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 4 Jun 2026 20:51:54 +0200 Subject: [PATCH 081/106] Avln_TerminalHostTests --- .../MainWindowViewModelTests.cs | 126 +++++++++++++++++- .../TestHostedProcess.cs | 20 +++ 2 files changed, 142 insertions(+), 4 deletions(-) diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/MainWindowViewModelTests.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/MainWindowViewModelTests.cs index 639ea377b..c347199e0 100644 --- a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/MainWindowViewModelTests.cs +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/MainWindowViewModelTests.cs @@ -1,6 +1,10 @@ +using System; +using System.IO; using System.Threading.Tasks; using Avln_TerminalHost.Services; using Avln_TerminalHost.ViewModels; +using Avalonia.Headless.MSTest; +using Avalonia.Threading; using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using NSubstitute; @@ -10,7 +14,7 @@ namespace Avln_TerminalHostTests; [TestClass] public class MainWindowViewModelTests { - [TestMethod] + [AvaloniaTestMethod] public async Task StartCommand_ShouldStartProcessAndAppendStartupMessage() { var hostedProcess = new TestHostedProcess(); @@ -25,7 +29,7 @@ public async Task StartCommand_ShouldStartProcessAndAppendStartupMessage() await processRunner.Received(1).StartAsync(default); } - [TestMethod] + [AvaloniaTestMethod] public async Task SendInputCommand_ShouldForwardInputAndEchoPrompt() { var hostedProcess = new TestHostedProcess(); @@ -42,7 +46,7 @@ public async Task SendInputCommand_ShouldForwardInputAndEchoPrompt() sut.Console.Content.Should().Contain("> dir"); } - [TestMethod] + [AvaloniaTestMethod] public async Task ProcessOutput_ShouldBeWrittenToConsole() { var hostedProcess = new TestHostedProcess(); @@ -53,13 +57,81 @@ public async Task ProcessOutput_ShouldBeWrittenToConsole() hostedProcess.EmitStandardOutput("hello"); hostedProcess.EmitStandardError("boom"); + await Dispatcher.UIThread.InvokeAsync(() => { }); sut.Console.Content.Should().Contain("hello"); sut.Console.Content.Should().Contain("boom"); sut.Console.Content.Should().Contain("\\c4Fboom"); } + [AvaloniaTestMethod] + public async Task PartialProcessOutput_ShouldBeWrittenWithoutWaitingForLineBreak() + { + var hostedProcess = new TestHostedProcess(); + var processRunner = Substitute.For(); + processRunner.StartAsync(default).ReturnsForAnyArgs(Task.FromResult(hostedProcess)); + var sut = new MainWindowViewModel(processRunner); + await sut.StartCommand.ExecuteAsync(null); + + hostedProcess.EmitStandardOutputPartial("C:\\> "); + await Dispatcher.UIThread.InvokeAsync(() => { }); + + sut.Console.Content.Should().Contain("C:\\\\>"); + } + + [TestMethod] + public async Task OutputChunkReader_ShouldFlushPartialOutputAfterInactivity() + { + using var reader = new StringReader("prompt> "); + var partialOutput = string.Empty; + var sut = new OutputChunkReader( + reader, + TimeSpan.FromMilliseconds(20), + _ => Assert.Fail("No complete line expected."), + text => partialOutput += text); + + await sut.RunAsync(default); + + partialOutput.Should().Be("prompt> "); + } + + [TestMethod] + public async Task OutputChunkReader_ShouldEmitCompleteLineWithoutPartialFlush() + { + using var reader = new StringReader("hello\n"); + var lineOutput = string.Empty; + var partialOutput = string.Empty; + var sut = new OutputChunkReader( + reader, + TimeSpan.FromMilliseconds(20), + text => lineOutput += text, + text => partialOutput += text); + + await sut.RunAsync(default); + + lineOutput.Should().Be("hello"); + partialOutput.Should().BeEmpty(); + } + [TestMethod] + public async Task OutputChunkReader_ShouldEmitLineImmediatelyAndKeepTrailingPromptPartial() + { + using var reader = new StringReader("hello\nprompt> "); + var lineOutput = string.Empty; + var partialOutput = string.Empty; + var sut = new OutputChunkReader( + reader, + TimeSpan.FromMilliseconds(20), + text => lineOutput += text, + text => partialOutput += text); + + await sut.RunAsync(default); + + lineOutput.Should().Be("hello"); + partialOutput.Should().Be("prompt> "); + } + + [AvaloniaTestMethod] public async Task StopCommand_ShouldRequestProcessStop() { var hostedProcess = new TestHostedProcess(); @@ -74,7 +146,53 @@ public async Task StopCommand_ShouldRequestProcessStop() sut.Console.Content.Should().Contain("Stopping shell process."); } - [TestMethod] + [AvaloniaTestMethod] + public async Task ConsoleTextInput_ShouldEchoAndTrackCurrentInteractiveInput() + { + var hostedProcess = new TestHostedProcess(); + var processRunner = Substitute.For(); + processRunner.StartAsync(default).ReturnsForAnyArgs(Task.FromResult(hostedProcess)); + var sut = new MainWindowViewModel(processRunner); + await sut.StartCommand.ExecuteAsync(null); + + await sut.HandleConsoleTextInputAsync("dir"); + + sut.InputText.Should().Be("dir"); + sut.Console.Content.Should().Contain("dir"); + } + + [AvaloniaTestMethod] + public async Task ConsoleEnter_ShouldForwardBufferedInteractiveInputToProcess() + { + var hostedProcess = new TestHostedProcess(); + var processRunner = Substitute.For(); + processRunner.StartAsync(default).ReturnsForAnyArgs(Task.FromResult(hostedProcess)); + var sut = new MainWindowViewModel(processRunner); + await sut.StartCommand.ExecuteAsync(null); + await sut.HandleConsoleTextInputAsync("dir"); + + await sut.HandleConsoleSpecialKeyAsync(ConsoleKey.Enter); + + hostedProcess.WrittenLines.Should().ContainSingle().Which.Should().Be("dir"); + sut.InputText.Should().BeEmpty(); + } + + [AvaloniaTestMethod] + public async Task ConsoleBackspace_ShouldUpdateBufferedInteractiveInput() + { + var hostedProcess = new TestHostedProcess(); + var processRunner = Substitute.For(); + processRunner.StartAsync(default).ReturnsForAnyArgs(Task.FromResult(hostedProcess)); + var sut = new MainWindowViewModel(processRunner); + await sut.StartCommand.ExecuteAsync(null); + await sut.HandleConsoleTextInputAsync("dir"); + + await sut.HandleConsoleSpecialKeyAsync(ConsoleKey.Backspace); + + sut.InputText.Should().Be("di"); + } + + [AvaloniaTestMethod] public async Task ExitedEvent_ShouldResetRunningStateAndDisposeProcess() { var hostedProcess = new TestHostedProcess(); diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/TestHostedProcess.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/TestHostedProcess.cs index 9688c25da..d8524bc13 100644 --- a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/TestHostedProcess.cs +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHostTests/TestHostedProcess.cs @@ -14,9 +14,15 @@ public sealed class TestHostedProcess : IHostedProcess /// public event EventHandler? StandardOutputReceived; + /// + public event EventHandler? StandardOutputPartialReceived; + /// public event EventHandler? StandardErrorReceived; + /// + public event EventHandler? StandardErrorPartialReceived; + /// public event EventHandler? Exited; @@ -65,6 +71,13 @@ public ValueTask DisposeAsync() public void EmitStandardOutput(string text) => StandardOutputReceived?.Invoke(this, text); + /// + /// Emits a partial standard output chunk. + /// + /// The output text. + public void EmitStandardOutputPartial(string text) + => StandardOutputPartialReceived?.Invoke(this, text); + /// /// Emits a standard error line. /// @@ -72,6 +85,13 @@ public void EmitStandardOutput(string text) public void EmitStandardError(string text) => StandardErrorReceived?.Invoke(this, text); + /// + /// Emits a partial standard error chunk. + /// + /// The error text. + public void EmitStandardErrorPartial(string text) + => StandardErrorPartialReceived?.Invoke(this, text); + /// /// Emits a process exit event. /// From 850eaf28078a469809fda0c4c9011e15463b50f4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 4 Jun 2026 20:51:58 +0200 Subject: [PATCH 082/106] Avln_TestConsole --- .../Controls/AvaloniaConsoleControl.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Avalonia_Apps/Libraries/Avln_TestConsole/Controls/AvaloniaConsoleControl.cs b/Avalonia_Apps/Libraries/Avln_TestConsole/Controls/AvaloniaConsoleControl.cs index ee1edf44e..342dd119c 100644 --- a/Avalonia_Apps/Libraries/Avln_TestConsole/Controls/AvaloniaConsoleControl.cs +++ b/Avalonia_Apps/Libraries/Avln_TestConsole/Controls/AvaloniaConsoleControl.cs @@ -51,6 +51,11 @@ public ConsoleBuffer? Buffer } } + /// + /// Gets the current rendered cell size for a single console character. + /// + public Size CharacterCellSize => CalculateCellSize(); + /// protected override Size MeasureOverride(Size availableSize) { @@ -59,7 +64,7 @@ protected override Size MeasureOverride(Size availableSize) return base.MeasureOverride(availableSize); } - var cellSize = GetCellSize(); + var cellSize = CalculateCellSize(); return new Size(_buffer.WindowWidth * cellSize.Width, _buffer.WindowHeight * cellSize.Height); } @@ -73,7 +78,7 @@ public override void Render(DrawingContext context) return; } - var cellSize = GetCellSize(); + var cellSize = CalculateCellSize(); var cells = _buffer.ScreenBuffer; for (var index = 0; index < cells.Count; index++) { @@ -173,7 +178,7 @@ private void OnBufferChanged(object? sender, EventArgs e) InvalidateVisual(); }); - private Size GetCellSize() + private Size CalculateCellSize() { var sampleText = new FormattedText( "W", From 5c5c3ba480d95221b02bf88551c520435142a8be Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 4 Jun 2026 20:51:59 +0200 Subject: [PATCH 083/106] Avln_TestConsoleTests --- .../Avln_TestConsoleTests/AvaloniaTestConsoleTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Avalonia_Apps/Libraries/Avln_TestConsoleTests/AvaloniaTestConsoleTests.cs b/Avalonia_Apps/Libraries/Avln_TestConsoleTests/AvaloniaTestConsoleTests.cs index 52825f1e6..cb9a6c934 100644 --- a/Avalonia_Apps/Libraries/Avln_TestConsoleTests/AvaloniaTestConsoleTests.cs +++ b/Avalonia_Apps/Libraries/Avln_TestConsoleTests/AvaloniaTestConsoleTests.cs @@ -33,7 +33,7 @@ public void Write_ShouldExportColorMarkersLikeTestConsole() sut.BackgroundColor = ConsoleColor.Blue; sut.Write('A'); - sut.Content.Should().StartWith("\\c94A"); + sut.Content.Should().StartWith("\\c9CA"); } [TestMethod] @@ -48,7 +48,7 @@ public void Write_ShouldEscapeBackslashesAndTabs() sut.Clear(); sut.Write("\\\t"); - sut.Content.Should().StartWith("\\\\t"); + sut.Content.Should().StartWith(@"\\\t"); } [TestMethod] From aac4d20725d87d8e7930d0fde17508e49eb72a35 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Thu, 4 Jun 2026 20:52:06 +0200 Subject: [PATCH 084/106] Avalonia_Apps --- Avalonia_Apps/Avalonia_Apps.sln | 28 +++++++++++++++++++ Avalonia_Apps/Avalonia_Apps.slnx | 6 +++- .../Directory.Packages.props | 14 +--------- Avalonia_Apps/DevOps/DevOps.projitems | 2 ++ ...-T002-Create-Avalonia-Terminal-Host-App.md | 16 +++++++---- 5 files changed, 46 insertions(+), 20 deletions(-) diff --git a/Avalonia_Apps/Avalonia_Apps.sln b/Avalonia_Apps/Avalonia_Apps.sln index 1a23e03c1..6f1d81a11 100644 --- a/Avalonia_Apps/Avalonia_Apps.sln +++ b/Avalonia_Apps/Avalonia_Apps.sln @@ -68,6 +68,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseLib", "Libraries\BaseLi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BaseLibTests", "Libraries\BaseLibTests\BaseLibTests.csproj", "{A943C3B6-9BE3-443D-A561-212E203A8A83}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avln_TerminalHost", "Avln_TerminalHost\Avln_TerminalHost\Avln_TerminalHost.csproj", "{AD097195-7EC8-4776-8856-5B882C1F3D28}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avln_TerminalHostTests", "Avln_TerminalHost\Avln_TerminalHostTests\Avln_TerminalHostTests.csproj", "{8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}" +EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AA09_DialogBoxes", "AA09_DialogBoxes", "{5326A8F8-1CB1-4690-80E1-5BC0664D8EC0}" ProjectSection(SolutionItems) = preProject AA09_DialogBoxes\Application.props = AA09_DialogBoxes\Application.props @@ -447,6 +451,30 @@ Global {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|x64.Build.0 = Release|Any CPU {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|x86.ActiveCfg = Release|Any CPU {A943C3B6-9BE3-443D-A561-212E203A8A83}.Release|x86.Build.0 = Release|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Debug|x64.ActiveCfg = Debug|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Debug|x64.Build.0 = Debug|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Debug|x86.ActiveCfg = Debug|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Debug|x86.Build.0 = Debug|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Release|Any CPU.Build.0 = Release|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Release|x64.ActiveCfg = Release|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Release|x64.Build.0 = Release|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Release|x86.ActiveCfg = Release|Any CPU + {AD097195-7EC8-4776-8856-5B882C1F3D28}.Release|x86.Build.0 = Release|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Debug|x64.ActiveCfg = Debug|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Debug|x64.Build.0 = Debug|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Debug|x86.ActiveCfg = Debug|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Debug|x86.Build.0 = Debug|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Release|Any CPU.Build.0 = Release|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Release|x64.ActiveCfg = Release|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Release|x64.Build.0 = Release|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Release|x86.ActiveCfg = Release|Any CPU + {8EBEA0FF-88D6-4CD0-9C53-D0FE57C37F1D}.Release|x86.Build.0 = Release|Any CPU {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|Any CPU.Build.0 = Debug|Any CPU {291BFE74-F64A-D263-170E-6CF138F96265}.Debug|x64.ActiveCfg = Debug|Any CPU diff --git a/Avalonia_Apps/Avalonia_Apps.slnx b/Avalonia_Apps/Avalonia_Apps.slnx index 070d959dc..b7b360980 100644 --- a/Avalonia_Apps/Avalonia_Apps.slnx +++ b/Avalonia_Apps/Avalonia_Apps.slnx @@ -108,11 +108,15 @@ - + + + + + diff --git a/Avalonia_Apps/Avln_TerminalHost/Directory.Packages.props b/Avalonia_Apps/Avln_TerminalHost/Directory.Packages.props index 5450bca13..b30adab53 100644 --- a/Avalonia_Apps/Avln_TerminalHost/Directory.Packages.props +++ b/Avalonia_Apps/Avln_TerminalHost/Directory.Packages.props @@ -1,18 +1,6 @@ + - - - - - - - - - - - - - diff --git a/Avalonia_Apps/DevOps/DevOps.projitems b/Avalonia_Apps/DevOps/DevOps.projitems index f2089b26a..ec4e6f641 100644 --- a/Avalonia_Apps/DevOps/DevOps.projitems +++ b/Avalonia_Apps/DevOps/DevOps.projitems @@ -11,6 +11,7 @@ + @@ -48,6 +49,7 @@ + diff --git a/Avalonia_Apps/DevOps/Tasks/AA00-T002-Create-Avalonia-Terminal-Host-App.md b/Avalonia_Apps/DevOps/Tasks/AA00-T002-Create-Avalonia-Terminal-Host-App.md index 56aefaec0..bf3768804 100644 --- a/Avalonia_Apps/DevOps/Tasks/AA00-T002-Create-Avalonia-Terminal-Host-App.md +++ b/Avalonia_Apps/DevOps/Tasks/AA00-T002-Create-Avalonia-Terminal-Host-App.md @@ -20,13 +20,17 @@ Create a separate Avalonia desktop application that hosts `Libraries/Avln_TestCo - Full VT terminal emulation is explicitly out of scope for this slice. ## Tasks -- [ ] Extend the reusable console library only where hosting requires it. -- [ ] Add the Avalonia desktop host app and dedicated tests. -- [ ] Implement process launching, output forwarding, and input submission. -- [ ] Validate scoped builds and tests. +- [x] Extend the reusable console library only where hosting requires it. +- [x] Add the Avalonia desktop host app and dedicated tests. +- [x] Implement process launching, output forwarding, and input submission. +- [x] Validate scoped builds and tests. ## Validation -- Pending. +- Build: `dotnet build C:\Projekte\CSharp\Avalonia_Apps\Libraries\Avln_TestConsole\Avln_TestConsole.csproj -nologo` +- Build: `dotnet build C:\Projekte\CSharp\Avalonia_Apps\Avln_TerminalHost\Avln_TerminalHost\Avln_TerminalHost.csproj -nologo` +- Tests: `dotnet test C:\Projekte\CSharp\Avalonia_Apps\Libraries\Avln_TestConsoleTests\Avln_TestConsoleTests.csproj -nologo --no-restore` +- Tests: `dotnet test C:\Projekte\CSharp\Avalonia_Apps\Avln_TerminalHost\Avln_TerminalHostTests\Avln_TerminalHostTests.csproj -nologo --no-restore` +- Note: IDE-backed solution-wide build/test remained blocked by unrelated existing workspace errors outside this scope. ## Status -- In Progress +- Done From 600b806bdad8518e3e759441c025d066715db686 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:30:10 +0200 Subject: [PATCH 085/106] AA05_CommandParCalc --- .../AIServiceCollectionExtensions.cs | 41 ++++++++ .../Services/NullAIClient.cs | 18 ++++ .../Services/NullAIClientFactory.cs | 13 +++ .../AA98_AvlnCodeStudio.Base.Debugging.csproj | 20 ++++ .../DebuggingServiceCollectionExtensions.cs | 38 +++++++ .../Models/DebugLaunchRequest.cs | 29 ++++++ .../Models/DebugSessionInfo.cs | 22 +++++ .../Models/DebugSessionState.cs | 37 +++++++ .../Services/IDebugSessionService.cs | 27 +++++ .../Services/NullDebugSessionService.cs | 29 ++++++ .../OSServiceCollectionExtensions.cs | 26 +++++ .../AA98_AvlnCodeStudio.Base.Testing.csproj | 20 ++++ .../TestingServiceCollectionExtensions.cs | 38 +++++++ .../Models/TestRunOutcome.cs | 32 ++++++ .../Models/TestRunRequest.cs | 24 +++++ .../Models/TestRunSummary.cs | 32 ++++++ .../Services/ITestExecutionService.cs | 19 ++++ .../Services/NullTestExecutionService.cs | 26 +++++ .../UIServiceCollectionExtensions.cs | 26 +++++ ...AA98_AvlnCodeStudio.Base.Versioning.csproj | 20 ++++ .../VersioningServiceCollectionExtensions.cs | 38 +++++++ .../Models/VersionControlChangeKind.cs | 42 ++++++++ .../Models/VersionControlChangeSummary.cs | 22 +++++ .../Models/VersionControlStatus.cs | 29 ++++++ .../Models/VersionControlStatusRequest.cs | 17 ++++ .../Services/IVersionControlService.cs | 19 ++++ .../Services/NullVersionControlService.cs | 23 +++++ ...eStudioServiceCollectionExtensionsTests.cs | 99 +++++++++++++++++++ .../EngineeringFoundationModelTests.cs | 93 +++++++++++++++++ .../CodeStudioServiceCollectionExtensions.cs | 35 +++++++ ...ioning-Testing-and-Debugging-Foundation.md | 27 +++++ ...AA98-F37-Engineering-Tooling-Foundation.md | 51 ++++++++++ ...ioning-Testing-and-Debugging-Foundation.md | 15 +++ 33 files changed, 1047 insertions(+) create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/DependencyInjection/AIServiceCollectionExtensions.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClient.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClientFactory.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/AA98_AvlnCodeStudio.Base.Debugging.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/DependencyInjection/DebuggingServiceCollectionExtensions.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugLaunchRequest.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionInfo.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionState.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/IDebugSessionService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/NullDebugSessionService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/DependencyInjection/OSServiceCollectionExtensions.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/AA98_AvlnCodeStudio.Base.Testing.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/DependencyInjection/TestingServiceCollectionExtensions.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunOutcome.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunRequest.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunSummary.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/ITestExecutionService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/NullTestExecutionService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/DependencyInjection/UIServiceCollectionExtensions.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/AA98_AvlnCodeStudio.Base.Versioning.csproj create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/DependencyInjection/VersioningServiceCollectionExtensions.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeKind.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeSummary.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatus.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatusRequest.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/IVersionControlService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/NullVersionControlService.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/DependencyInjection/CodeStudioServiceCollectionExtensionsTests.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/Engineering/EngineeringFoundationModelTests.cs create mode 100644 Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/DependencyInjection/CodeStudioServiceCollectionExtensions.cs create mode 100644 Avalonia_Apps/DevOps/BacklogItems/AA98-Bl037-Versioning-Testing-and-Debugging-Foundation.md create mode 100644 Avalonia_Apps/DevOps/Features/AA98-F37-Engineering-Tooling-Foundation.md create mode 100644 Avalonia_Apps/DevOps/Tasks/AA98-T037-Implement-Versioning-Testing-and-Debugging-Foundation.md diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/DependencyInjection/AIServiceCollectionExtensions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/DependencyInjection/AIServiceCollectionExtensions.cs new file mode 100644 index 000000000..8a3685baa --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/DependencyInjection/AIServiceCollectionExtensions.cs @@ -0,0 +1,41 @@ +using AA98_AvlnCodeStudio.Base.AI.Services; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace AA98_AvlnCodeStudio.Base.AI.DependencyInjection; + +/// +/// Provides registration helpers for AI services. +/// +public static class AIServiceCollectionExtensions +{ + /// + /// Adds the default provider-neutral AI services. + /// + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioAI(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + services.AddSingleton(); + services.AddTransient(serviceProvider => serviceProvider.GetRequiredService().Create()); + return services; + } + + /// + /// Adds a specific AI client factory implementation. + /// + /// The AI client factory implementation type. + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioAI(this IServiceCollection services) + where TAIClientFactory : class, IAIClientFactory + { + ArgumentNullException.ThrowIfNull(services); + + services.AddSingleton(); + services.AddTransient(serviceProvider => serviceProvider.GetRequiredService().Create()); + return services; + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClient.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClient.cs new file mode 100644 index 000000000..414961e68 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClient.cs @@ -0,0 +1,18 @@ +using AA98_AvlnCodeStudio.Base.AI.Models; +using System.Threading; +using System.Threading.Tasks; + +namespace AA98_AvlnCodeStudio.Base.AI.Services; + +/// +/// Provides a provider-neutral fallback AI client without external provider integration. +/// +public sealed class NullAIClient : IAIClient +{ + /// + public Task CompleteAsync(AICompletionRequest request, CancellationToken cancellationToken = default) + { + var response = new AICompletionResponse(new AIMessage(AIMessageRole.Assistant, string.Empty)); + return Task.FromResult(response); + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClientFactory.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClientFactory.cs new file mode 100644 index 000000000..3d2cbb10a --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/Services/NullAIClientFactory.cs @@ -0,0 +1,13 @@ +namespace AA98_AvlnCodeStudio.Base.AI.Services; + +/// +/// Creates fallback AI client instances without provider integration. +/// +public sealed class NullAIClientFactory : IAIClientFactory +{ + /// + public IAIClient Create() + { + return new NullAIClient(); + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/AA98_AvlnCodeStudio.Base.Debugging.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/AA98_AvlnCodeStudio.Base.Debugging.csproj new file mode 100644 index 000000000..f0b346975 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/AA98_AvlnCodeStudio.Base.Debugging.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/DependencyInjection/DebuggingServiceCollectionExtensions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/DependencyInjection/DebuggingServiceCollectionExtensions.cs new file mode 100644 index 000000000..28c12d648 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/DependencyInjection/DebuggingServiceCollectionExtensions.cs @@ -0,0 +1,38 @@ +using AA98_AvlnCodeStudio.Base.Debugging.Services; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace AA98_AvlnCodeStudio.Base.Debugging.DependencyInjection; + +/// +/// Provides registration helpers for debugging services. +/// +public static class DebuggingServiceCollectionExtensions +{ + /// + /// Adds the default provider-neutral debugging services. + /// + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioDebugging(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + return services.AddCodeStudioDebugging(); + } + + /// + /// Adds a specific debugging service implementation. + /// + /// The debugging service implementation type. + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioDebugging(this IServiceCollection services) + where TDebugSessionService : class, IDebugSessionService + { + ArgumentNullException.ThrowIfNull(services); + + services.AddSingleton(); + return services; + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugLaunchRequest.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugLaunchRequest.cs new file mode 100644 index 000000000..0dca3bda7 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugLaunchRequest.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace AA98_AvlnCodeStudio.Base.Debugging.Models; + +/// +/// Represents a provider-neutral request to start or attach a debugging session. +/// +public sealed class DebugLaunchRequest +{ + /// + /// Gets or sets the optional launch target path or identifier. + /// + public string? Target { get; set; } + + /// + /// Gets the optional command-line arguments. + /// + public IList Arguments { get; } = new List(); + + /// + /// Gets or sets the optional working directory. + /// + public string? WorkingDirectory { get; set; } + + /// + /// Gets or sets a value indicating whether the request should attach to an existing process. + /// + public bool AttachToExistingProcess { get; set; } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionInfo.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionInfo.cs new file mode 100644 index 000000000..db54d5ce2 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionInfo.cs @@ -0,0 +1,22 @@ +namespace AA98_AvlnCodeStudio.Base.Debugging.Models; + +/// +/// Represents provider-neutral summary information about a debugging session. +/// +public sealed class DebugSessionInfo +{ + /// + /// Gets or sets the provider-specific session identifier. + /// + public string SessionId { get; set; } = string.Empty; + + /// + /// Gets or sets the optional display name of the session target. + /// + public string? DisplayName { get; set; } + + /// + /// Gets or sets the current session state. + /// + public DebugSessionState State { get; set; } = DebugSessionState.Unknown; +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionState.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionState.cs new file mode 100644 index 000000000..777626954 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Models/DebugSessionState.cs @@ -0,0 +1,37 @@ +namespace AA98_AvlnCodeStudio.Base.Debugging.Models; + +/// +/// Describes the high-level state of a debugging session. +/// +public enum DebugSessionState +{ + /// + /// The state is not yet known. + /// + Unknown = 0, + + /// + /// The session is starting. + /// + Starting, + + /// + /// The session is active. + /// + Running, + + /// + /// The session is paused. + /// + Paused, + + /// + /// The session ended normally. + /// + Stopped, + + /// + /// The session terminated unexpectedly. + /// + Faulted +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/IDebugSessionService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/IDebugSessionService.cs new file mode 100644 index 000000000..da54de0d2 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/IDebugSessionService.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; +using AA98_AvlnCodeStudio.Base.Debugging.Models; + +namespace AA98_AvlnCodeStudio.Base.Debugging.Services; + +/// +/// Defines provider-neutral debugging session operations for studio components. +/// +public interface IDebugSessionService +{ + /// + /// Starts or attaches a debugging session. + /// + /// The launch or attach request. + /// The cancellation token. + /// The created or attached session information. + Task StartSessionAsync(DebugLaunchRequest request, CancellationToken cancellationToken = default); + + /// + /// Stops a running debugging session. + /// + /// The provider-specific session identifier. + /// The cancellation token. + /// A task that completes when the session has stopped. + Task StopSessionAsync(string sessionId, CancellationToken cancellationToken = default); +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/NullDebugSessionService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/NullDebugSessionService.cs new file mode 100644 index 000000000..b64393bf8 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Debugging/Services/NullDebugSessionService.cs @@ -0,0 +1,29 @@ +using System.Threading; +using System.Threading.Tasks; +using AA98_AvlnCodeStudio.Base.Debugging.Models; + +namespace AA98_AvlnCodeStudio.Base.Debugging.Services; + +/// +/// Provides a provider-neutral fallback debugging service without debugger integration. +/// +public sealed class NullDebugSessionService : IDebugSessionService +{ + /// + public Task StartSessionAsync(DebugLaunchRequest request, CancellationToken cancellationToken = default) + { + var session = new DebugSessionInfo + { + DisplayName = request.Target, + State = DebugSessionState.Stopped, + }; + + return Task.FromResult(session); + } + + /// + public Task StopSessionAsync(string sessionId, CancellationToken cancellationToken = default) + { + return Task.CompletedTask; + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/DependencyInjection/OSServiceCollectionExtensions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/DependencyInjection/OSServiceCollectionExtensions.cs new file mode 100644 index 000000000..41f2a4f23 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/DependencyInjection/OSServiceCollectionExtensions.cs @@ -0,0 +1,26 @@ +using AA98_AvlnCodeStudio.Base.OS.Services; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace AA98_AvlnCodeStudio.Base.OS.DependencyInjection; + +/// +/// Provides registration helpers for OS-scoped services. +/// +public static class OSServiceCollectionExtensions +{ + /// + /// Adds a specific text document storage service implementation. + /// + /// The storage service implementation type. + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioOS(this IServiceCollection services) + where TTextDocumentStorageService : class, ITextDocumentStorageService + { + ArgumentNullException.ThrowIfNull(services); + + services.AddSingleton(); + return services; + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/AA98_AvlnCodeStudio.Base.Testing.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/AA98_AvlnCodeStudio.Base.Testing.csproj new file mode 100644 index 000000000..f0b346975 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/AA98_AvlnCodeStudio.Base.Testing.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/DependencyInjection/TestingServiceCollectionExtensions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/DependencyInjection/TestingServiceCollectionExtensions.cs new file mode 100644 index 000000000..218a4b533 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/DependencyInjection/TestingServiceCollectionExtensions.cs @@ -0,0 +1,38 @@ +using AA98_AvlnCodeStudio.Base.Testing.Services; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace AA98_AvlnCodeStudio.Base.Testing.DependencyInjection; + +/// +/// Provides registration helpers for testing services. +/// +public static class TestingServiceCollectionExtensions +{ + /// + /// Adds the default provider-neutral testing services. + /// + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioTesting(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + return services.AddCodeStudioTesting(); + } + + /// + /// Adds a specific testing service implementation. + /// + /// The testing service implementation type. + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioTesting(this IServiceCollection services) + where TTestExecutionService : class, ITestExecutionService + { + ArgumentNullException.ThrowIfNull(services); + + services.AddSingleton(); + return services; + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunOutcome.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunOutcome.cs new file mode 100644 index 000000000..0deb9eabe --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunOutcome.cs @@ -0,0 +1,32 @@ +namespace AA98_AvlnCodeStudio.Base.Testing.Models; + +/// +/// Describes the high-level outcome of a test execution. +/// +public enum TestRunOutcome +{ + /// + /// The outcome is not yet known. + /// + Unknown = 0, + + /// + /// The test run passed. + /// + Passed, + + /// + /// The test run failed. + /// + Failed, + + /// + /// The test run was canceled. + /// + Canceled, + + /// + /// The test run completed with mixed results. + /// + Partial +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunRequest.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunRequest.cs new file mode 100644 index 000000000..38156602b --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunRequest.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace AA98_AvlnCodeStudio.Base.Testing.Models; + +/// +/// Represents a provider-neutral request to execute tests. +/// +public sealed class TestRunRequest +{ + /// + /// Gets or sets the optional workspace root path. + /// + public string? WorkspaceRootPath { get; set; } + + /// + /// Gets the logical test targets to include in the run. + /// + public IList Targets { get; } = new List(); + + /// + /// Gets or sets a value indicating whether code coverage should be collected. + /// + public bool CollectCoverage { get; set; } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunSummary.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunSummary.cs new file mode 100644 index 000000000..14e3a3c55 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Models/TestRunSummary.cs @@ -0,0 +1,32 @@ +namespace AA98_AvlnCodeStudio.Base.Testing.Models; + +/// +/// Represents a provider-neutral summary of a test run. +/// +public sealed class TestRunSummary +{ + /// + /// Gets or sets the high-level outcome. + /// + public TestRunOutcome Outcome { get; set; } = TestRunOutcome.Unknown; + + /// + /// Gets or sets the total number of discovered or executed tests. + /// + public int TotalCount { get; set; } + + /// + /// Gets or sets the number of passed tests. + /// + public int PassedCount { get; set; } + + /// + /// Gets or sets the number of failed tests. + /// + public int FailedCount { get; set; } + + /// + /// Gets or sets the number of skipped tests. + /// + public int SkippedCount { get; set; } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/ITestExecutionService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/ITestExecutionService.cs new file mode 100644 index 000000000..b1957ef84 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/ITestExecutionService.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; +using AA98_AvlnCodeStudio.Base.Testing.Models; + +namespace AA98_AvlnCodeStudio.Base.Testing.Services; + +/// +/// Defines provider-neutral test execution operations for studio components. +/// +public interface ITestExecutionService +{ + /// + /// Executes a test run for the requested targets. + /// + /// The test run request. + /// The cancellation token. + /// The summarized test run result. + Task RunTestsAsync(TestRunRequest request, CancellationToken cancellationToken = default); +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/NullTestExecutionService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/NullTestExecutionService.cs new file mode 100644 index 000000000..dcd66fde6 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Testing/Services/NullTestExecutionService.cs @@ -0,0 +1,26 @@ +using System.Threading; +using System.Threading.Tasks; +using AA98_AvlnCodeStudio.Base.Testing.Models; + +namespace AA98_AvlnCodeStudio.Base.Testing.Services; + +/// +/// Provides a provider-neutral fallback test execution service without runner integration. +/// +public sealed class NullTestExecutionService : ITestExecutionService +{ + /// + public Task RunTestsAsync(TestRunRequest request, CancellationToken cancellationToken = default) + { + var summary = new TestRunSummary + { + Outcome = TestRunOutcome.Unknown, + TotalCount = 0, + PassedCount = 0, + FailedCount = 0, + SkippedCount = 0, + }; + + return Task.FromResult(summary); + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/DependencyInjection/UIServiceCollectionExtensions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/DependencyInjection/UIServiceCollectionExtensions.cs new file mode 100644 index 000000000..30144e2b1 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/DependencyInjection/UIServiceCollectionExtensions.cs @@ -0,0 +1,26 @@ +using AA98_AvlnCodeStudio.Base.UI.Services; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace AA98_AvlnCodeStudio.Base.UI.DependencyInjection; + +/// +/// Provides registration helpers for UI-scoped services. +/// +public static class UIServiceCollectionExtensions +{ + /// + /// Adds a specific editor file dialog service implementation. + /// + /// The dialog service implementation type. + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioUI(this IServiceCollection services) + where TEditorFileDialogService : class, IEditorFileDialogService + { + ArgumentNullException.ThrowIfNull(services); + + services.AddSingleton(); + return services; + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/AA98_AvlnCodeStudio.Base.Versioning.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/AA98_AvlnCodeStudio.Base.Versioning.csproj new file mode 100644 index 000000000..f0b346975 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/AA98_AvlnCodeStudio.Base.Versioning.csproj @@ -0,0 +1,20 @@ + + + + net8.0 + enable + Debug;Release;DesktopOnly + + + + + $(TargetFrameworks);net9.0 + + + $(TargetFrameworks);net10.0 + + + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/DependencyInjection/VersioningServiceCollectionExtensions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/DependencyInjection/VersioningServiceCollectionExtensions.cs new file mode 100644 index 000000000..391c568b3 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/DependencyInjection/VersioningServiceCollectionExtensions.cs @@ -0,0 +1,38 @@ +using AA98_AvlnCodeStudio.Base.Versioning.Services; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace AA98_AvlnCodeStudio.Base.Versioning.DependencyInjection; + +/// +/// Provides registration helpers for versioning services. +/// +public static class VersioningServiceCollectionExtensions +{ + /// + /// Adds the default provider-neutral versioning services. + /// + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioVersioning(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + return services.AddCodeStudioVersioning(); + } + + /// + /// Adds a specific versioning service implementation. + /// + /// The versioning service implementation type. + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioVersioning(this IServiceCollection services) + where TVersionControlService : class, IVersionControlService + { + ArgumentNullException.ThrowIfNull(services); + + services.AddSingleton(); + return services; + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeKind.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeKind.cs new file mode 100644 index 000000000..7388f10ac --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeKind.cs @@ -0,0 +1,42 @@ +namespace AA98_AvlnCodeStudio.Base.Versioning.Models; + +/// +/// Describes the high-level kind of a version-controlled file change. +/// +public enum VersionControlChangeKind +{ + /// + /// The change kind is not yet known. + /// + Unknown = 0, + + /// + /// A new item was added. + /// + Added, + + /// + /// An existing item was modified. + /// + Modified, + + /// + /// An existing item was deleted. + /// + Deleted, + + /// + /// An existing item was renamed. + /// + Renamed, + + /// + /// An existing item was copied. + /// + Copied, + + /// + /// An existing item was moved. + /// + Moved +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeSummary.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeSummary.cs new file mode 100644 index 000000000..86938346b --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlChangeSummary.cs @@ -0,0 +1,22 @@ +namespace AA98_AvlnCodeStudio.Base.Versioning.Models; + +/// +/// Represents a lightweight summary of a version-controlled item change. +/// +public sealed class VersionControlChangeSummary +{ + /// + /// Gets or sets the repository-relative path of the changed item. + /// + public string Path { get; set; } = string.Empty; + + /// + /// Gets or sets the optional previous repository-relative path. + /// + public string? PreviousPath { get; set; } + + /// + /// Gets or sets the high-level change kind. + /// + public VersionControlChangeKind ChangeKind { get; set; } = VersionControlChangeKind.Unknown; +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatus.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatus.cs new file mode 100644 index 000000000..3d1a87cdb --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatus.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace AA98_AvlnCodeStudio.Base.Versioning.Models; + +/// +/// Represents a provider-neutral snapshot of repository state information. +/// +public sealed class VersionControlStatus +{ + /// + /// Gets or sets the repository root path. + /// + public string RepositoryRootPath { get; set; } = string.Empty; + + /// + /// Gets or sets the current branch or equivalent version line name. + /// + public string? ActiveReferenceName { get; set; } + + /// + /// Gets or sets a value indicating whether the repository contains local changes. + /// + public bool HasLocalChanges { get; set; } + + /// + /// Gets the tracked change summaries. + /// + public IList Changes { get; } = new List(); +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatusRequest.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatusRequest.cs new file mode 100644 index 000000000..422b397dd --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Models/VersionControlStatusRequest.cs @@ -0,0 +1,17 @@ +namespace AA98_AvlnCodeStudio.Base.Versioning.Models; + +/// +/// Represents a provider-neutral request for repository status information. +/// +public sealed class VersionControlStatusRequest +{ + /// + /// Gets or sets the repository root path. + /// + public string RepositoryRootPath { get; set; } = string.Empty; + + /// + /// Gets or sets a value indicating whether detailed file changes should be included. + /// + public bool IncludeChanges { get; set; } = true; +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/IVersionControlService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/IVersionControlService.cs new file mode 100644 index 000000000..594c1fed4 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/IVersionControlService.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; +using AA98_AvlnCodeStudio.Base.Versioning.Models; + +namespace AA98_AvlnCodeStudio.Base.Versioning.Services; + +/// +/// Defines provider-neutral version control operations for studio components. +/// +public interface IVersionControlService +{ + /// + /// Gets a repository status snapshot for the requested workspace. + /// + /// The status request. + /// The cancellation token. + /// The repository status snapshot. + Task GetStatusAsync(VersionControlStatusRequest request, CancellationToken cancellationToken = default); +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/NullVersionControlService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/NullVersionControlService.cs new file mode 100644 index 000000000..f979b7198 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.Versioning/Services/NullVersionControlService.cs @@ -0,0 +1,23 @@ +using System.Threading; +using System.Threading.Tasks; +using AA98_AvlnCodeStudio.Base.Versioning.Models; + +namespace AA98_AvlnCodeStudio.Base.Versioning.Services; + +/// +/// Provides a provider-neutral fallback version control service without repository integration. +/// +public sealed class NullVersionControlService : IVersionControlService +{ + /// + public Task GetStatusAsync(VersionControlStatusRequest request, CancellationToken cancellationToken = default) + { + var status = new VersionControlStatus + { + RepositoryRootPath = request.RepositoryRootPath, + HasLocalChanges = false, + }; + + return Task.FromResult(status); + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/DependencyInjection/CodeStudioServiceCollectionExtensionsTests.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/DependencyInjection/CodeStudioServiceCollectionExtensionsTests.cs new file mode 100644 index 000000000..9d7254b59 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/DependencyInjection/CodeStudioServiceCollectionExtensionsTests.cs @@ -0,0 +1,99 @@ +using AA98_AvlnCodeStudio.Base.AI.DependencyInjection; +using AA98_AvlnCodeStudio.Base.AI.Services; +using AA98_AvlnCodeStudio.Base.Debugging.DependencyInjection; +using AA98_AvlnCodeStudio.Base.Debugging.Services; +using AA98_AvlnCodeStudio.Base.OS.DependencyInjection; +using AA98_AvlnCodeStudio.Base.OS.Services; +using AA98_AvlnCodeStudio.Base.Testing.DependencyInjection; +using AA98_AvlnCodeStudio.Base.Testing.Services; +using AA98_AvlnCodeStudio.Base.UI.DependencyInjection; +using AA98_AvlnCodeStudio.Base.UI.Services; +using AA98_AvlnCodeStudio.Base.Versioning.DependencyInjection; +using AA98_AvlnCodeStudio.Base.Versioning.Services; +using AA98_AvlnCodeStudio.UI.DependencyInjection; +using AA98_AvlnCodeStudio.UI.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Linq; + +namespace AA98_AvlnCodeStudio.Tests.DependencyInjection; + +/// +/// Verifies the shared DI registration baseline for Code Studio foundation services. +/// +[TestClass] +public class CodeStudioServiceCollectionExtensionsTests +{ + /// + /// Verifies that the default AI registration adds the fallback AI services. + /// + [TestMethod] + public void AddCodeStudioAI_RegistersFallbackServices() + { + var services = new ServiceCollection(); + + services.AddCodeStudioAI(); + + using var serviceProvider = services.BuildServiceProvider(); + Assert.IsInstanceOfType(serviceProvider.GetRequiredService()); + Assert.IsInstanceOfType(serviceProvider.GetRequiredService()); + } + + /// + /// Verifies that the default engineering registrations add fallback services. + /// + [TestMethod] + public void EngineeringRegistrations_RegisterFallbackServices() + { + var services = new ServiceCollection(); + + services.AddCodeStudioVersioning(); + services.AddCodeStudioTesting(); + services.AddCodeStudioDebugging(); + + using var serviceProvider = services.BuildServiceProvider(); + Assert.IsInstanceOfType(serviceProvider.GetRequiredService()); + Assert.IsInstanceOfType(serviceProvider.GetRequiredService()); + Assert.IsInstanceOfType(serviceProvider.GetRequiredService()); + } + + /// + /// Verifies that environment-bound OS and UI registrations use the configured implementation types. + /// + [TestMethod] + public void EnvironmentRegistrations_RegisterConfiguredImplementationTypes() + { + var services = new ServiceCollection(); + + services.AddCodeStudioOS(); + services.AddCodeStudioUI(); + + using var serviceProvider = services.BuildServiceProvider(); + Assert.IsInstanceOfType(serviceProvider.GetRequiredService()); + Assert.IsInstanceOfType(serviceProvider.GetRequiredService()); + } + + /// + /// Verifies that the shared foundation registration composes all base scopes for the application startup. + /// + [TestMethod] + public void AddCodeStudioFoundation_RegistersSharedBaseScopes() + { + var services = new ServiceCollection(); + + services.AddCodeStudioFoundation(); + + using var serviceProvider = services.BuildServiceProvider(); + Assert.IsNotNull(serviceProvider.GetRequiredService()); + Assert.IsNotNull(serviceProvider.GetRequiredService()); + Assert.IsNotNull(serviceProvider.GetRequiredService()); + Assert.IsNotNull(serviceProvider.GetRequiredService()); + + var storageDescriptor = services.Last(static descriptor => descriptor.ServiceType == typeof(ITextDocumentStorageService)); + var dialogDescriptor = services.Last(static descriptor => descriptor.ServiceType == typeof(IEditorFileDialogService)); + + Assert.AreEqual(typeof(FileSystemTextDocumentStorageService), storageDescriptor.ImplementationType); + Assert.AreEqual(typeof(AvaloniaEditorFileDialogService), dialogDescriptor.ImplementationType); + } +} diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/Engineering/EngineeringFoundationModelTests.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/Engineering/EngineeringFoundationModelTests.cs new file mode 100644 index 000000000..d9ce19a4a --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/Engineering/EngineeringFoundationModelTests.cs @@ -0,0 +1,93 @@ +using AA98_AvlnCodeStudio.Base.Debugging.Models; +using AA98_AvlnCodeStudio.Base.Testing.Models; +using AA98_AvlnCodeStudio.Base.Versioning.Models; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AA98_AvlnCodeStudio.Tests.Engineering; + +/// +/// Verifies baseline defaults for the shared engineering foundation models. +/// +[TestClass] +public class EngineeringFoundationModelTests +{ + /// + /// Verifies that a version control status request includes changes by default. + /// + [TestMethod] + public void VersionControlStatusRequest_UsesExpectedDefaults() + { + var request = new VersionControlStatusRequest(); + + Assert.AreEqual(string.Empty, request.RepositoryRootPath); + Assert.IsTrue(request.IncludeChanges); + } + + /// + /// Verifies that a version control status starts with an empty change collection. + /// + [TestMethod] + public void VersionControlStatus_StartsWithEmptyChanges() + { + var status = new VersionControlStatus(); + + Assert.AreEqual(string.Empty, status.RepositoryRootPath); + Assert.IsFalse(status.HasLocalChanges); + Assert.AreEqual(0, status.Changes.Count); + } + + /// + /// Verifies that a test run request starts without targets and without coverage collection. + /// + [TestMethod] + public void TestRunRequest_UsesExpectedDefaults() + { + var request = new TestRunRequest(); + + Assert.IsNull(request.WorkspaceRootPath); + Assert.AreEqual(0, request.Targets.Count); + Assert.IsFalse(request.CollectCoverage); + } + + /// + /// Verifies that a test run summary starts with an unknown outcome and zero counters. + /// + [TestMethod] + public void TestRunSummary_UsesExpectedDefaults() + { + var summary = new TestRunSummary(); + + Assert.AreEqual(TestRunOutcome.Unknown, summary.Outcome); + Assert.AreEqual(0, summary.TotalCount); + Assert.AreEqual(0, summary.PassedCount); + Assert.AreEqual(0, summary.FailedCount); + Assert.AreEqual(0, summary.SkippedCount); + } + + /// + /// Verifies that a debug launch request starts detached and without arguments. + /// + [TestMethod] + public void DebugLaunchRequest_UsesExpectedDefaults() + { + var request = new DebugLaunchRequest(); + + Assert.IsNull(request.Target); + Assert.AreEqual(0, request.Arguments.Count); + Assert.IsNull(request.WorkingDirectory); + Assert.IsFalse(request.AttachToExistingProcess); + } + + /// + /// Verifies that a debug session info starts with an unknown state. + /// + [TestMethod] + public void DebugSessionInfo_UsesExpectedDefaults() + { + var session = new DebugSessionInfo(); + + Assert.AreEqual(string.Empty, session.SessionId); + Assert.IsNull(session.DisplayName); + Assert.AreEqual(DebugSessionState.Unknown, session.State); + } +} \ No newline at end of file diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/DependencyInjection/CodeStudioServiceCollectionExtensions.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/DependencyInjection/CodeStudioServiceCollectionExtensions.cs new file mode 100644 index 000000000..557271e02 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/DependencyInjection/CodeStudioServiceCollectionExtensions.cs @@ -0,0 +1,35 @@ +using AA98_AvlnCodeStudio.Base.AI.DependencyInjection; +using AA98_AvlnCodeStudio.Base.Debugging.DependencyInjection; +using AA98_AvlnCodeStudio.Base.OS.DependencyInjection; +using AA98_AvlnCodeStudio.Base.Testing.DependencyInjection; +using AA98_AvlnCodeStudio.Base.UI.DependencyInjection; +using AA98_AvlnCodeStudio.Base.Versioning.DependencyInjection; +using AA98_AvlnCodeStudio.UI.Services; +using Microsoft.Extensions.DependencyInjection; +using System; + +namespace AA98_AvlnCodeStudio.UI.DependencyInjection; + +/// +/// Provides the application-level composition baseline for Code Studio services. +/// +public static class CodeStudioServiceCollectionExtensions +{ + /// + /// Adds the shared Code Studio base-scope registrations for the current application startup. + /// + /// The service collection. + /// The same service collection instance. + public static IServiceCollection AddCodeStudioFoundation(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + services.AddCodeStudioAI(); + services.AddCodeStudioVersioning(); + services.AddCodeStudioTesting(); + services.AddCodeStudioDebugging(); + services.AddCodeStudioOS(); + services.AddCodeStudioUI(); + return services; + } +} diff --git a/Avalonia_Apps/DevOps/BacklogItems/AA98-Bl037-Versioning-Testing-and-Debugging-Foundation.md b/Avalonia_Apps/DevOps/BacklogItems/AA98-Bl037-Versioning-Testing-and-Debugging-Foundation.md new file mode 100644 index 000000000..7719d25dc --- /dev/null +++ b/Avalonia_Apps/DevOps/BacklogItems/AA98-Bl037-Versioning-Testing-and-Debugging-Foundation.md @@ -0,0 +1,27 @@ +# AA98-Bl037 Versioning, Testing, and Debugging Foundation + +## Parent +- Feature: `DevOps/Features/AA98-F37-Engineering-Tooling-Foundation.md` +- Epic: `DevOps/Epics/AA98-E09-Quality-Tests-and-Engineering-Baseline.md` +- Vision: `DevOps/Vision.md` + +## Scope +Define the first shared engineering capability baseline for `AA98_AvlnCodeStudio` so versioning, testing, and debugging can be consumed through common base abstractions by all future program components. + +## Goals +- Define provider-neutral contracts for versioning, testing, and debugging. +- Keep the contracts available through dedicated scope-specific base libraries for all components. +- Clarify the separation between shared engineering capabilities and later concrete providers. +- Keep the baseline architectural and independent from concrete tool implementations. + +## Assumptions +- A common base abstraction is preferable to separate optional feature islands for these capabilities. +- The first iteration should focus on requests, status reporting, and lifecycle entry points. +- Concrete providers can be introduced later without reshaping the shared contracts fundamentally. + +## Open Questions +- Is a shared aggregated engineering service needed later, or should capabilities stay separate long-term? +- Which capability should be connected to UI workflows first after the baseline contracts exist? + +## Status +- Proposed \ No newline at end of file diff --git a/Avalonia_Apps/DevOps/Features/AA98-F37-Engineering-Tooling-Foundation.md b/Avalonia_Apps/DevOps/Features/AA98-F37-Engineering-Tooling-Foundation.md new file mode 100644 index 000000000..0fe0eb10e --- /dev/null +++ b/Avalonia_Apps/DevOps/Features/AA98-F37-Engineering-Tooling-Foundation.md @@ -0,0 +1,51 @@ +# AA98-F37 Engineering Tooling Foundation + +## Parent +- Epic: `DevOps/Epics/AA98-E09-Quality-Tests-and-Engineering-Baseline.md` +- Vision: `DevOps/Vision.md` + +## Goal +Define the first provider-neutral engineering tooling foundation for `AA98_AvlnCodeStudio` so versioning, testing, and debugging capabilities are available as shared application contracts from the beginning. + +## Scope +- Define provider-neutral contracts for versioning, testing, and debugging. +- Keep the contracts available to all application components through scope-specific `.Base.XXX` libraries built on the shared base layer. +- Clarify the architectural boundaries between shared contracts and later concrete providers. +- Prepare the path for future workflow, UI, and provider integrations without coupling the foundation to a specific toolchain. + +## Included +- Versioning capability baseline +- Testing capability baseline +- Debugging capability baseline +- Shared request and status models for later orchestration +- Boundaries between capability contracts and provider implementations + +## Excluded for Now +- Concrete Git integrations +- Concrete test runner adapters +- Concrete debugger adapters +- Rich UI workflow orchestration +- Cross-process session management details + +## Success Indicators +- Future components can depend on shared versioning, testing, and debugging abstractions without referencing concrete tools. +- The base layer remains provider-agnostic and reusable. +- Later service and UI implementations can extend the contracts without breaking the initial architectural baseline. + +## Candidate Backlog Items +- Define provider-neutral versioning abstractions +- Define provider-neutral testing abstractions +- Define provider-neutral debugging abstractions +- Keep the baseline available to all components through the shared base layer + +## Assumptions +- These capabilities should be part of the common application foundation through dedicated scope-specific base libraries instead of isolated optional modules. +- The first contracts should remain small, stable, and implementation agnostic. +- Later providers may differ significantly, so the initial models should focus on shared workflow concepts only. + +## Open Questions +- Should a higher-level engineering workspace facade be introduced later above the individual capabilities? +- Which first status details are required by the shell and tool windows once UI integration begins? + +## Status +- Proposed \ No newline at end of file diff --git a/Avalonia_Apps/DevOps/Tasks/AA98-T037-Implement-Versioning-Testing-and-Debugging-Foundation.md b/Avalonia_Apps/DevOps/Tasks/AA98-T037-Implement-Versioning-Testing-and-Debugging-Foundation.md new file mode 100644 index 000000000..23c3ccbdb --- /dev/null +++ b/Avalonia_Apps/DevOps/Tasks/AA98-T037-Implement-Versioning-Testing-and-Debugging-Foundation.md @@ -0,0 +1,15 @@ +# AA98-T037 Implement Versioning, Testing, and Debugging Foundation + +## Parent +- Backlog Item: `AA98-Bl037 Versioning, Testing, and Debugging Foundation` + +## Tasks +- [x] Define the initial DevOps planning baseline for the shared engineering capability foundation. +- [x] Define provider-neutral versioning request and status models. +- [x] Define provider-neutral testing request and status models. +- [x] Define provider-neutral debugging request and status models. +- [x] Define provider-neutral service interfaces for versioning, testing, and debugging. +- [x] Validate the new baseline with build and relevant tests. + +## Notes +- The first implementation keeps these capabilities available through `AA98_AvlnCodeStudio.Base.Versioning`, `AA98_AvlnCodeStudio.Base.Testing`, and `AA98_AvlnCodeStudio.Base.Debugging`, each built on the shared base layer so later program components can adopt them consistently. From cd3d0070d52550bcbf26157aec5e42e1b7528fc1 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:30:35 +0200 Subject: [PATCH 086/106] AA98_AvlnCodeStudio.Base --- .../AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj index 9bde2a3e2..12daf3616 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -18,5 +18,6 @@ + From 9df41305342a4f85b7f690d03d20a45303daeb6a Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:30:36 +0200 Subject: [PATCH 087/106] AA98_AvlnCodeStudio.Base.AI --- .../AA98_AvlnCodeStudio.Base.AI.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj index 2a515800a..f0b346975 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.csproj @@ -14,6 +14,7 @@ $(TargetFrameworks);net10.0 + From 36f37b59c8bd2462bead2eed0aef4b5f7daf8214 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:30:37 +0200 Subject: [PATCH 088/106] AA98_AvlnCodeStudio.Base.OS --- .../AA98_AvlnCodeStudio.Base.OS.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj index 2a515800a..f0b346975 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.csproj @@ -14,6 +14,7 @@ $(TargetFrameworks);net10.0 + From c699526ada9ed8aa66b9718fff80985b8cd9fe1e Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:30:38 +0200 Subject: [PATCH 089/106] AA98_AvlnCodeStudio.Base.UI --- .../AA98_AvlnCodeStudio.Base.UI.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj index 2a515800a..f0b346975 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.csproj @@ -14,6 +14,7 @@ $(TargetFrameworks);net10.0 + From 39395fa7d3664a707e8b5eccaf622dfba3c038a4 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:30:41 +0200 Subject: [PATCH 090/106] AA98_AvlnCodeStudio.Tests --- .../AA98_AvlnCodeStudio.Tests.csproj | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj index dfcaa66ff..465a7034f 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj @@ -16,6 +16,7 @@ + all @@ -26,6 +27,13 @@ + + + + + + + From 0a57b895eee1c529de7defd61375bdc536eda63f Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:30:41 +0200 Subject: [PATCH 091/106] AA98_AvlnCodeStudio.UI --- .../AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj | 4 ++++ .../AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj index 90da39d92..6c0a350a9 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj @@ -39,8 +39,12 @@ + + + + diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs index 905ee748b..d2dd247e0 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/App.axaml.cs @@ -1,10 +1,9 @@ using AA98_AvlnCodeStudio.UI.ViewModels; using AA98_AvlnCodeStudio.UI.Views; -using AA98_AvlnCodeStudio.Base.OS.Services; -using AA98_AvlnCodeStudio.Base.UI.Services; using AA98_AvlnCodeStudio.Editor.Components; using AA98_AvlnCodeStudio.Editor.Services; using AA98_AvlnCodeStudio.Model.Documents; +using AA98_AvlnCodeStudio.UI.DependencyInjection; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; @@ -42,9 +41,8 @@ private static IServiceProvider ConfigureServices(IClassicDesktopStyleApplicatio var services = new ServiceCollection(); services.AddAvaloniaCommonDialogs(() => desktop.MainWindow); + services.AddCodeStudioFoundation(); services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); From 7c58a64d3ebb5599aa7bf70e9f8a2e5a528ff3c7 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 09:31:35 +0200 Subject: [PATCH 092/106] Avalonia_Apps --- Avalonia_Apps/AA98_AvlnCodeStudio.slnx | 3 +++ Avalonia_Apps/DevOps/DevOps.projitems | 3 +++ ...T010-Implement-Component-Registration-Baseline.md | 12 +++++++----- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio.slnx b/Avalonia_Apps/AA98_AvlnCodeStudio.slnx index 499cc6547..c372134cb 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio.slnx +++ b/Avalonia_Apps/AA98_AvlnCodeStudio.slnx @@ -7,8 +7,11 @@ + + + diff --git a/Avalonia_Apps/DevOps/DevOps.projitems b/Avalonia_Apps/DevOps/DevOps.projitems index ec4e6f641..ebf415fb3 100644 --- a/Avalonia_Apps/DevOps/DevOps.projitems +++ b/Avalonia_Apps/DevOps/DevOps.projitems @@ -25,6 +25,7 @@ + @@ -47,6 +48,7 @@ + @@ -59,6 +61,7 @@ + \ No newline at end of file diff --git a/Avalonia_Apps/DevOps/Tasks/AA98-T010-Implement-Component-Registration-Baseline.md b/Avalonia_Apps/DevOps/Tasks/AA98-T010-Implement-Component-Registration-Baseline.md index 943ec714e..99c922780 100644 --- a/Avalonia_Apps/DevOps/Tasks/AA98-T010-Implement-Component-Registration-Baseline.md +++ b/Avalonia_Apps/DevOps/Tasks/AA98-T010-Implement-Component-Registration-Baseline.md @@ -4,11 +4,13 @@ - Backlog Item: `AA98-Bl010 Component Registration Baseline` ## Tasks -- [ ] Define the first explicit component registration model for internal modules. -- [ ] Connect component registration to the existing application composition path. -- [ ] Keep the registration baseline DI-friendly and easy to validate. -- [ ] Prepare registration seams for later command, UI, and configuration contributions. -- [ ] Validate the registration baseline with build and relevant tests. +- [x] Define the first explicit component registration model for internal modules. +- [x] Connect component registration to the existing application composition path. +- [x] Keep the registration baseline DI-friendly and easy to validate. +- [x] Prepare registration seams for later command, UI, and configuration contributions. +- [x] Validate the registration baseline with build and relevant tests. ## Notes - The first implementation should prefer explicit composition over early discovery automation. +- The current baseline uses scope-specific DI extension methods for `.Base.AI`, `.Base.Versioning`, `.Base.Testing`, `.Base.Debugging`, `.Base.OS`, and `.Base.UI`, with application-level aggregation in the UI composition layer. +- Validation result: solution build succeeded and DI registration tests passed for fallback, environment-bound, and application-level composition paths. From 7268c8faa66deb00f033096e0416227a0f5e98bc Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 23:11:08 +0200 Subject: [PATCH 093/106] AA05_CommandParCalc --- ...ms-Menu-Mapping-for-ConsoleLib.WinForms.md | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Avalonia_Apps/DevOps/Tasks/AA00-T003-Native-WinForms-Menu-Mapping-for-ConsoleLib.WinForms.md diff --git a/Avalonia_Apps/DevOps/Tasks/AA00-T003-Native-WinForms-Menu-Mapping-for-ConsoleLib.WinForms.md b/Avalonia_Apps/DevOps/Tasks/AA00-T003-Native-WinForms-Menu-Mapping-for-ConsoleLib.WinForms.md new file mode 100644 index 000000000..24e1ac356 --- /dev/null +++ b/Avalonia_Apps/DevOps/Tasks/AA00-T003-Native-WinForms-Menu-Mapping-for-ConsoleLib.WinForms.md @@ -0,0 +1,28 @@ +# AA00-T003 Native WinForms Menu Mapping for ConsoleLib.WinForms + +## Scope +Introduce a backend-specific native menu path in `ConsoleLib.WinForms` so that `MenuBar` and `MenuItem` are represented by WinForms menu components instead of generic panel/button controls. + +## Parent +- AA00 - ConsoleLib / WinForms widget host ongoing implementation slice + +## Summary +`ConsoleLib.WinForms` now contains a dedicated menu mapping path: +- `MenuBar` -> `MenuStrip` +- normal `MenuItem` -> `ToolStripMenuItem` +- separator `MenuItem` -> `ToolStripSeparator` +- `MenuPopup` -> structural dropdown representation via parent `ToolStripMenuItem.DropDownItems` + +## Assumptions +- Native WinForms menus are backend-specific and do not need to preserve the console layout behavior one-to-one. +- `MenuPopup` is treated as a structural menu node in the WinForms backend rather than as a normal `Control`. +- Existing non-menu ConsoleLib controls continue to use the prior `Control` registry path. + +## Notes +- The current implementation keeps menu registrations local to `WinFormsWidgetSet` instead of widening `NativeWidgetRegistry` beyond `Control` objects. +- A later refinement may extract native menu registration into a dedicated helper if more hosted non-control widget types are introduced. +- `ConsoleLib` core has since been slimmed down further, and the concrete ExtendedConsole-backed console widget set is being hosted separately in `ConsoleLib.ExtCon`. + +## Validation +- File-level validation completed for the changed WinForms widget-set file. +- Full project build should be re-run in a follow-up validation pass because one direct terminal build was cancelled. From 6968f8efb74f95c21984c1f3421828ccba711c42 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 23:11:28 +0200 Subject: [PATCH 094/106] Avln_RichTextEdit --- .../Avln_RichTextEdit/Avln_RichTextEdit.csproj | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj b/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj index 0f4abadf9..63be81ed0 100644 --- a/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj +++ b/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj @@ -30,10 +30,4 @@ - - - - - - From 277f81035a4750a93b7825afe04f03a299fa0623 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 23:12:26 +0200 Subject: [PATCH 095/106] Avalonia_Apps --- Avalonia_Apps/Avalonia_Apps.slnx | 1 + Avalonia_Apps/DevOps/DevOps.projitems | 1 + .../DevOps/Features/ConsoleLib-WidgetSet-Abstraction.md | 1 + 3 files changed, 3 insertions(+) diff --git a/Avalonia_Apps/Avalonia_Apps.slnx b/Avalonia_Apps/Avalonia_Apps.slnx index b7b360980..2620764ce 100644 --- a/Avalonia_Apps/Avalonia_Apps.slnx +++ b/Avalonia_Apps/Avalonia_Apps.slnx @@ -165,6 +165,7 @@ + diff --git a/Avalonia_Apps/DevOps/DevOps.projitems b/Avalonia_Apps/DevOps/DevOps.projitems index ebf415fb3..afaa5e367 100644 --- a/Avalonia_Apps/DevOps/DevOps.projitems +++ b/Avalonia_Apps/DevOps/DevOps.projitems @@ -52,6 +52,7 @@ + diff --git a/Avalonia_Apps/DevOps/Features/ConsoleLib-WidgetSet-Abstraction.md b/Avalonia_Apps/DevOps/Features/ConsoleLib-WidgetSet-Abstraction.md index 0a4250fa2..60150da31 100644 --- a/Avalonia_Apps/DevOps/Features/ConsoleLib-WidgetSet-Abstraction.md +++ b/Avalonia_Apps/DevOps/Features/ConsoleLib-WidgetSet-Abstraction.md @@ -68,6 +68,7 @@ Introduce a `WidgetSet` abstraction for `ConsoleLib` so the existing public cont - The widget seam now covers the base rendering path plus the currently identified specialized rendering hotspots while preserving the existing console-backed behavior. - A first `ConsoleLib.WinForms` project now exists with a host form, widget registry, hosted widget-set contract, and an initial adapter slice for core controls using real WinForms instances. - The current WinForms slice already compiles as a separate project and supports bidirectional baseline synchronization for key control state such as text, enabled/visible state, bounds, button clicks, list selection, and scrollbar values. +- The concrete ExtendedConsole-based default widget set has now been moved out of `ConsoleLib` into `ConsoleLib.ExtCon`, while `ConsoleLib` itself targets only modern .NET core TFMs. ## Status - Completed From 5320033aa42bb0f23e187af1998a0eaf1911acfb Mon Sep 17 00:00:00 2001 From: Joe Care Date: Fri, 5 Jun 2026 23:58:33 +0200 Subject: [PATCH 096/106] Core --- JC-AMS/DevOps/Fortschritt.md | 165 +++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 JC-AMS/DevOps/Fortschritt.md diff --git a/JC-AMS/DevOps/Fortschritt.md b/JC-AMS/DevOps/Fortschritt.md new file mode 100644 index 000000000..f9a5a833c --- /dev/null +++ b/JC-AMS/DevOps/Fortschritt.md @@ -0,0 +1,165 @@ +# Fortschritt / Resume-Punkt + +Dieser Bereich hält den aktuellen Dokumentationsfortschritt getrennt fest, damit an dieser Stelle später ohne Nacharbeit fortgesetzt werden kann. + +## Bearbeitungsstand + +### Bereits eingelesen + +- `Core` +- `MPS` +- `Service` +- `WinGUI` +- `FASys.KG.Mps.Interface` +- `AGV` +- `Communication` +- `DNC` +- `PLC` +- `Message` +- `Manager` +- `MPS.Manager` +- `Simulator` +- `Components` +- `Resources` +- `JCAMSManager` +- `Core.Tests` + +### Bereits dokumentiert + +- Basis-README für DevOps +- Feature-Dokumentation für SPS-Anbindung +- Feature-Dokumentation für Datenbank-Persistenz +- Backlog-Items für Konfiguration, dynamische Daten und Logs +- Feature-Dokumentation für AGV, Kommunikation, DNC, PLC, Messages, Manager, MPS-Management, Simulator, Komponenten, Ressourcen und Startup +- Backlog-Item für Testabdeckung von Logging und Core-Utilities +- Task-Übersicht als Startpunkt für weitere Zerlegung +- Feature `F_800008_Shows_and_manages_messages_and_system_feedback` mit Prisma-, Stations- und MessageMonitor-Fluss +- Feature `F_800008_Shows_and_manages_messages_and_system_feedback` erweitert um SyncMessage und PrismaStation-Abläufe +- Feature `F_800008_Shows_and_manages_messages_and_system_feedback` erweitert um AGV-/Prisma-ServiceVehicle- und Störcode-Pools +- Feature `F_800008_Shows_and_manages_messages_and_system_feedback` erweitert um detaillierte Prisma-Report- und MessageHistory-Flüsse +- Feature `F_800008_Shows_and_manages_messages_and_system_feedback` erweitert um die relevanten Message-Datenmodelle +- Feature `F_800008_Shows_and_manages_messages_and_system_feedback` erweitert um Historienlogik und Platzbezug +- Feature `F_800008_Shows_and_manages_messages_and_system_feedback` erweitert um Datenquellen und Tabellenbezug +- Feature `F_800015_Provides_logging_and_startup_protocols` für Logging und Startup-Protokolle ergänzt +- Feature `F_800016_Synchronizes_system_values_from_telegrams` für Systemwertsynchronisation ergänzt +- Feature `F_800017_String_and_stream_extension_utilities` für String-/Stream-Utilities ergänzt +- Feature `F_800018_Boolean_conversion_and_math_utilities` für Bool- und Math-Utilities ergänzt +- Feature `F_800019_List_extension_utilities` für Listen-Erweiterungen ergänzt +- Feature `F_800020_Integer_extension_utilities` für Integer-Erweiterungen ergänzt +- Feature `F_800021_Provides_assertion_helpers` für Assertions-Hilfen ergänzt +- Feature `F_800022_Network_helpers_for_users_services_and_UNC_paths` für Netzwerk-Hilfen ergänzt +- Feature `F_800023_Date_helpers_for_formatting_and_weeks` für Datums-Hilfen ergänzt +- Feature `F_800024_Provides_graphics_and_xml_conversions_for_UI_assets` für Grafik-/XML-Konvertierungen ergänzt +- Feature `F_800025_DotNet_runtime_detection` für .NET-Runtime-Erkennung ergänzt +- Feature `F_800026_Class_bool_conversion` für Bool-Konverter ergänzt +- Feature `F_800027_Double_conversion_and_numeric_extensions` für Double-/Numeric-Extensions ergänzt +- Feature `F_800028_Bit_operations_for_flags_and_masks` für Bit-Operationen ergänzt +- Feature `F_800029_Provides_simple_cipher_utility` für Cipher-Utility ergänzt +- Feature `F_800030_Debug_and_stack_trace_helpers` für Debug-/Stacktrace-Hilfen ergänzt +- Feature `F_800031_Station_serialization_and_notifications` für Stationsserialisierung ergänzt +- Feature `F_800032_Graphics2_conversion_and_xml_tests` für Grafik-Konvertierungs- und XML-Tests ergänzt +- Feature `F_800033_Numeric_and_bit_helpers_test_coverage` für numerische und Bit-Hilfen ergänzt +- Feature `F_800034_Core_test_suite_overview` für die Core-Test-Suite ergänzt +- Feature `F_800035_Coloring_component_tests` für Coloring-Komponenten ergänzt +- Feature `F_800036_Graphics_rendering_and_metafile_tests` für Grafik-Rendering ergänzt +- Feature `F_800037_Process_helpers_for_starting_and_capturing` für Prozess-Hilfen ergänzt +- Feature `F_800038_System_value_model_and_station_registry` für Systemwerte und Stationsregistrierung ergänzt +- Feature `F_800039_Number_byte_and_word_extraction_helpers` für numerische Byte-/Word-Hilfen ergänzt +- Feature `F_800040_System_value_telegram_and_database_projection` für Telegramm- und Datenbankprojektion von Systemwerten ergänzt +- Feature `F_800041_System_value_wrapper_and_property_collection` für Systemwert-Wrapper und Property-Collections ergänzt +- Feature `F_800042_Substation_model_and_lookup_helpers` für Substation-Modell und Lookup-Hilfen ergänzt +- Feature `F_800043_CValue_typed_wrapper_and_bit_access` für typisierte Systemwert-Wrapper und Bitzugriffe ergänzt +- Feature `F_800044_System_values_registry_and_lookup_services` für zentrale Systemwert-Registry und Lookup-Services ergänzt +- Feature `F_800045_Property_descriptor_for_dynamic_system_values` für PropertyDescriptors dynamischer Systemwerte ergänzt +- Feature `F_800046_System_value_communication_helpers_for_telegram_roundtrip` für Telegramm-Roundtrips und Systemwert-Synchronisation ergänzt +- Feature `F_800047_System_value_definition_registry_and_metadata` für Systemwert-Definitionen und Metadaten ergänzt +- Feature `F_800048_System_values_bulk_refresh_and_mapping` für Bulk-Refresh und Mapping der Systemwerte ergänzt +- Feature `F_800049_File_helpers_for_directories_security_and_validation` für Datei-/Verzeichnis-Hilfen ergänzt +- Feature `F_800050_Network_helpers_for_users_unc_paths_and_services` für Netzwerk-, Benutzer- und UNC-Hilfen ergänzt +- Feature `F_800051_Date_helpers_for_formatting_calendar_weeks_and_parsing` für Datums- und Kalenderwochen-Hilfen ergänzt +- Feature `F_800052_Debug_helpers_for_method_stack_capture` für Debug-Methodenstack-Hilfen ergänzt +- Feature `F_800053_Asynchronous_socket_lifecycle_and_timeout_handling` für Socket-Lebenszyklus und Timeouts ergänzt +- Feature `F_800054_Serial_port_configuration_and_control_characters` für serielle Portkonfiguration und Steuerzeichen ergänzt +- Feature `F_800055_Pooled_connections_performance_counter` für gepoolte Verbindungen als Performance-Counter ergänzt +- Feature `F_800056_Communication_station_and_substation_registry_models` für Kommunikationsstationen und Substations ergänzt +- Feature `F_800057_Named_pipe_communication_and_event_payloads` für Named-Pipe-Kommunikation und Event-Payloads ergänzt +- Feature `F_800058_Ping_helper_for_network_latency_measurement` für Ping-basierte Netzwerklatenz ergänzt +- Feature `F_800059_Named_pipe_read_event_arguments_and_command_parsing` für Named-Pipe-Read-Event-Parsing ergänzt +- Feature `F_800060_Communication_message_buffer_and_stationing_placeholders` für Kommunikationspuffer und Stationing-Platzhalter ergänzt +- Feature `F_800061_Core_assertion_helpers_and_debug_integration` für Core-Assertions und Debug-Integration ergänzt +- Feature `F_800062_DotNet_runtime_detection_via_registry` für .NET-Erkennung per Registry ergänzt +- Feature `F_800063_Logging_infrastructure_and_destination_routing` für Logging-Infrastruktur und Zielrouting ergänzt +- Feature `F_800064_String_extension_utilities_for_paths_html_and_streams` für String-Utilities, HTML und Streams ergänzt +- Feature `F_800065_List_extension_utilities_for_case_insensitive_lookup` für case-insensitive Listen-Suche ergänzt +- Feature `F_800066_Simple_cipher_for_base64_triple_des_roundtrip` für einfache TripleDES-/Base64-Verschlüsselung ergänzt +- Feature `F_800067_Math_helpers_for_average_angles_limits_and_capability` für mathematische Mittelwerte, Winkel und Cp/Cpk ergänzt +- Feature `F_800068_Geometry_helpers_for_points_lines_circles_and_vectors` für Geometrie-Hilfen ergänzt +- Feature `F_800069_Arithmetic_average_value_objects` für Durchschnitts-Value-Objekte ergänzt +- Feature `F_800070_Geometry_test_suite_overview_and_placeholder_coverage` für Geometrie-Test-Suite und Platzhalter ergänzt +- Feature `F_800071_Math_test_suite_for_statistical_and_angle_helpers` für Math-Test-Suite ergänzt +- Feature `F_800072_Logging_test_suite_and_destination_behavior` für Logging-Test-Suite und Zielverhalten ergänzt +- Feature `F_800073_Debug_helper_test_suite_for_method_stack_capture` für Debug-Helper-Test-Suite ergänzt +- Feature `F_800074_List_extension_test_suite_for_case_insensitive_matching` für List-Extension-Test-Suite ergänzt +- Feature `F_800075_Numeric_conversion_extensions_for_dates_sizes_timestamps_and_unsigned_values` für numerische Konvertierungs-Extensions ergänzt +- Feature `F_800076_Boolean_conversion_extension_for_loose_truth_detection` für Bool-Konvertierung ergänzt +- Feature `F_800077_Integer_conversion_extension_for_tolerant_parsing` für Integer-Konvertierung ergänzt +- Feature `F_800078_Double_and_float_conversion_extensions_for_numeric_text_and_binary_data` für Double-/Float-Konvertierung ergänzt +- Feature `F_800079_Ftp_helper_for_file_transfer_and_stale_file_handling` für FTP-Hilfen ergänzt +- Feature `F_800080_Variable_handling_for_ini_registry_and_database_configurations` für Variablenverwaltung ergänzt +- Feature `F_800081_Control_name_normalization_and_type_detection` für Control-Namen-Normalisierung ergänzt +- Feature `F_800082_List_helper_for_csv_and_segment_parsing` für CSV-/Segment-Listen-Hilfen ergänzt +- Feature `F_800083_WinAPI_interop_for_ini_profiles_and_message_sending` für WinAPI-Interop ergänzt +- Feature `F_800084_Date_helper_for_order_format_and_calendar_week_logic` für Datums- und Kalenderwochenlogik ergänzt +- Feature `F_800085_Bool_conversion_test_suite_for_truthy_and_falsy_values` für Bool-Konvertierungs-Test-Suite ergänzt +- Feature `F_800086_Integer_conversion_test_suite_for_loose_numeric_parsing` für Integer-Konvertierungs-Test-Suite ergänzt +- Feature `F_800087_Double_conversion_test_suite_for_floating_point_values` für Double-/Float-Konvertierungs-Test-Suite ergänzt +- Feature `F_800088_Numeric_conversion_test_suite_for_date_size_timestamp_and_unsigned_values` für numerische Konvertierungs-Test-Suite ergänzt +- Feature `F_800089_String_extension_test_suite_for_paths_streams_and_text_encoding` für String-Extensions-Test-Suite ergänzt +- Feature `F_800090_WinAPI_interop_for_ini_profiles_and_message_sending` für WinAPI-Interop ergänzt +- Feature `F_800091_Ftp_helper_for_file_transfer_and_stale_file_handling` für FTP-Hilfen ergänzt +- Feature `F_800092_Variable_handling_for_ini_registry_and_database_configurations` für Variablenverwaltung ergänzt +- Feature `F_800093_Control_name_normalization_and_type_detection` für Control-Namen-Normalisierung ergänzt +- Feature `F_800094_List_helper_for_csv_and_segment_parsing` für CSV-/Segment-Listen-Hilfen ergänzt +- Feature `F_800095_Object_list_wrapper_for_typed_value_access_and_lookup` für Objektlisten-Wrapper ergänzt +- Feature `F_800096_Delayed_boolean_value_for_temporal_state_handling` für verzögerte Bool-Werte ergänzt +- Feature `F_800097_IPC_named_pipe_or_fallback_connection_manager` für IPC- und Named-Pipe-Verbindungsmanagement ergänzt +- Feature `F_800098_Color_state_and_palette_configuration_for_ui_states` für Farbzustände und Paletten ergänzt +- Feature `F_800099_Stopwatch_based_timing_and_debug_output_helper` für Stoppuhr- und Debug-Ausgabe ergänzt +- Feature `F_800100_Cyclic_point_of_time_for_periodic_overdue_checks` für zyklische Zeitpunktprüfung ergänzt +- Feature `F_800101_Color_list_and_cube_for_ui_palette_management` für UI-Farbpaletten ergänzt +- Feature `F_800102_UI_color_state_tests_and_configuration_gaps` für UI-Farbzustands-Tests ergänzt +- Feature `F_800103_List_of_objects_and_delayed_bool_component_overview` für Objektlisten und verzögerte Bool-Komponenten ergänzt +- Feature `F_800104_IPC_send_ping_and_receive_tracking` für IPC-Senden, Ping und Empfangstracking ergänzt +- Feature `F_800105_Color_list_brush_pen_lifecycle_and_config_persistence` für ColorList-Lifecycle und Konfigurationspersistenz ergänzt +- Feature `F_800106_Timer_state_for_busy_idle_control` für Timer-State ergänzt +- Feature `F_800107_Traffic_signal_and_configurable_component_contracts` für TrafficSignal- und Configurable-Verträge ergänzt +- Feature `F_800108_Object_list_and_timer_state_component_overview` für Objektlisten und Timer-State-Übersicht ergänzt +- Feature `F_800109_Bool_class_converter_for_ja_nein_text_roundtrip` für Bool-Class-Konverter ergänzt +- Feature `F_800110_Core_assertions_for_debug_checks` für Core-Assertions ergänzt +- Feature `F_800111_Timing_helpers_for_blinking_triangle_and_date_now_values` für Timing-Hilfen ergänzt +- Feature `F_800112_Timeout_helper_and_obsolete_timing_model` für Timeout-Hilfsklasse ergänzt +- Feature `F_800113_Day_and_shift_model_for_timing_schedules` für Tages-/Schichtmodell ergänzt +- Feature `F_800114_Holiday_calendar_model_and_validity_persistence` für Feiertagskalender und Validität ergänzt +- Feature `F_800115_Week_day_shift_model_for_scheduling` für Wochen-/Tages-/Schichtplanung ergänzt +- Feature `F_800116_Time_scheduler_with_productivity_spans_and_holidays` für Zeitplaner mit Produktivität und Feiertagen ergänzt +- Feature `F_800117_Productivity_time_spans_for_weekly_schedules` für Produktivitätszeitspannen ergänzt +- Feature `F_800118_Time_span_in_week_model_for_schedule_boundaries` für Wochen-Zeitspannen ergänzt +- Feature `F_800119_Weekly_structure_for_days_and_time_slices` für Wochenstruktur ergänzt +- Feature `F_800120_Maintenance_scheduler_for_jobs_tasks_and_protocols` für Wartungsplanung ergänzt +- Feature `F_800121_Maintenance_job_model_and_cycle_mapping` für Wartungsjob-Modell ergänzt +- Feature `F_800122_Maintenance_task_model_and_increment_logic` für Wartungsaufgaben ergänzt +- Feature `F_800123_Maintenance_protocol_model_and_pending_lookup` für Wartungsprotokolle ergänzt + +## Nächster Fortsetzungspunkt + +Als nächstes sollten weitere zentrale Projekte und Domänenklassen eingelesen werden, insbesondere: + +- Weitere Kernelemente aus `WinGUI`, `Core`, `Components`, `Resources`, `JCAMSManager` und den Testprojekten + +## Fortführungsregel + +Nach jedem Lesedurchlauf hier den Stand ergänzen: + +- Welche Projekte wurden gelesen? +- Welche fachlichen Erkenntnisse wurden ergänzt? +- Welche neuen Features, Backlogs oder Tasks wurden abgeleitet? From 3ec82bfebd99bd2eeea941d5a070a5b24f083ab1 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:04 +0200 Subject: [PATCH 097/106] Analyzer1.Test --- Transpiler_pp/Analyzer1/Analyzer1.Test/Analyzer1.Test.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/Analyzer1/Analyzer1.Test/Analyzer1.Test.csproj b/Transpiler_pp/Analyzer1/Analyzer1.Test/Analyzer1.Test.csproj index 05a3f7c98..88d57ce92 100644 --- a/Transpiler_pp/Analyzer1/Analyzer1.Test/Analyzer1.Test.csproj +++ b/Transpiler_pp/Analyzer1/Analyzer1.Test/Analyzer1.Test.csproj @@ -8,7 +8,7 @@ - + From 7393506cf50475297df0fb86801e3be6e8174239 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:07 +0200 Subject: [PATCH 098/106] TranspilerLib.CSharp.Tests --- .../TranspilerLib.CSharp.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/TranspilerLib.CSharp.Tests/TranspilerLib.CSharp.Tests.csproj b/Transpiler_pp/TranspilerLib.CSharp.Tests/TranspilerLib.CSharp.Tests.csproj index fb50f3e25..97211040f 100644 --- a/Transpiler_pp/TranspilerLib.CSharp.Tests/TranspilerLib.CSharp.Tests.csproj +++ b/Transpiler_pp/TranspilerLib.CSharp.Tests/TranspilerLib.CSharp.Tests.csproj @@ -24,7 +24,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From ced3b4e1857172b4a14ea10cd49d44566eb09f46 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:07 +0200 Subject: [PATCH 099/106] TranspilerLib.DriveBASIC.Tests --- .../TranspilerLib.DriveBASIC.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/TranspilerLib.DriveBASIC.Tests/TranspilerLib.DriveBASIC.Tests.csproj b/Transpiler_pp/TranspilerLib.DriveBASIC.Tests/TranspilerLib.DriveBASIC.Tests.csproj index 30133de7b..a986eac46 100644 --- a/Transpiler_pp/TranspilerLib.DriveBASIC.Tests/TranspilerLib.DriveBASIC.Tests.csproj +++ b/Transpiler_pp/TranspilerLib.DriveBASIC.Tests/TranspilerLib.DriveBASIC.Tests.csproj @@ -24,7 +24,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 5623b2357f1c2e64ee7d68adc755b51d89fc6cbb Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:08 +0200 Subject: [PATCH 100/106] TranspilerLib.IEC.Tests --- .../TranspilerLib.IEC.Tests/TranspilerLib.IEC.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/TranspilerLib.IEC.Tests/TranspilerLib.IEC.Tests.csproj b/Transpiler_pp/TranspilerLib.IEC.Tests/TranspilerLib.IEC.Tests.csproj index 86fd8920a..ea8665f06 100644 --- a/Transpiler_pp/TranspilerLib.IEC.Tests/TranspilerLib.IEC.Tests.csproj +++ b/Transpiler_pp/TranspilerLib.IEC.Tests/TranspilerLib.IEC.Tests.csproj @@ -24,7 +24,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 5690d2e38871cd50d250a0c4ea9790cc775c8ca8 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:08 +0200 Subject: [PATCH 101/106] TranspilerLib.Integration.Tests --- .../TranspilerLib.Integration.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/TranspilerLib.Integration.Tests/TranspilerLib.Integration.Tests.csproj b/Transpiler_pp/TranspilerLib.Integration.Tests/TranspilerLib.Integration.Tests.csproj index f7b79e563..9e83d922f 100644 --- a/Transpiler_pp/TranspilerLib.Integration.Tests/TranspilerLib.Integration.Tests.csproj +++ b/Transpiler_pp/TranspilerLib.Integration.Tests/TranspilerLib.Integration.Tests.csproj @@ -26,7 +26,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From dacf6fecb902184efed7c416eff1c46f5b839d0e Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:09 +0200 Subject: [PATCH 102/106] TranspilerLib.Pascal.Tests --- .../TranspilerLib.Pascal.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/TranspilerLib.Pascal.Tests/TranspilerLib.Pascal.Tests.csproj b/Transpiler_pp/TranspilerLib.Pascal.Tests/TranspilerLib.Pascal.Tests.csproj index 8d701fb8b..f7c68ca2f 100644 --- a/Transpiler_pp/TranspilerLib.Pascal.Tests/TranspilerLib.Pascal.Tests.csproj +++ b/Transpiler_pp/TranspilerLib.Pascal.Tests/TranspilerLib.Pascal.Tests.csproj @@ -24,7 +24,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 771aca2106df80f941270c868461671762509a1c Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:10 +0200 Subject: [PATCH 103/106] TranspilerLib.Semantics.Tests --- .../TranspilerLib.Semantics.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/TranspilerLib.Semantics.Tests/TranspilerLib.Semantics.Tests.csproj b/Transpiler_pp/TranspilerLib.Semantics.Tests/TranspilerLib.Semantics.Tests.csproj index 9449cc214..64625bf36 100644 --- a/Transpiler_pp/TranspilerLib.Semantics.Tests/TranspilerLib.Semantics.Tests.csproj +++ b/Transpiler_pp/TranspilerLib.Semantics.Tests/TranspilerLib.Semantics.Tests.csproj @@ -24,7 +24,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 7b017e2fc711307a7707c9a872e94561c1b009bd Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:10 +0200 Subject: [PATCH 104/106] TranspilerLibTests --- Transpiler_pp/TranspilerLibTests/TranspilerLibTests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/TranspilerLibTests/TranspilerLibTests.csproj b/Transpiler_pp/TranspilerLibTests/TranspilerLibTests.csproj index 388e9978e..5a82295e0 100644 --- a/Transpiler_pp/TranspilerLibTests/TranspilerLibTests.csproj +++ b/Transpiler_pp/TranspilerLibTests/TranspilerLibTests.csproj @@ -37,7 +37,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + From 9ab7260df417e1f50b331ba7f66dd7d4c6bdd020 Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:11 +0200 Subject: [PATCH 105/106] Trnsp.Show.Lfm.Tests --- Transpiler_pp/Trnsp.Show.Lfm.Tests/Trnsp.Show.Lfm.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/Trnsp.Show.Lfm.Tests/Trnsp.Show.Lfm.Tests.csproj b/Transpiler_pp/Trnsp.Show.Lfm.Tests/Trnsp.Show.Lfm.Tests.csproj index c4dbbc26b..4ba232c12 100644 --- a/Transpiler_pp/Trnsp.Show.Lfm.Tests/Trnsp.Show.Lfm.Tests.csproj +++ b/Transpiler_pp/Trnsp.Show.Lfm.Tests/Trnsp.Show.Lfm.Tests.csproj @@ -16,7 +16,7 @@ - + From aa4d05f0ba80870a09b4a897af526be30f341c0a Mon Sep 17 00:00:00 2001 From: Joe Care Date: Sat, 6 Jun 2026 00:13:12 +0200 Subject: [PATCH 106/106] Trnsp.Show.Pas.Tests --- Transpiler_pp/Trnsp.Show.Pas.Tests/Trnsp.Show.Pas.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Transpiler_pp/Trnsp.Show.Pas.Tests/Trnsp.Show.Pas.Tests.csproj b/Transpiler_pp/Trnsp.Show.Pas.Tests/Trnsp.Show.Pas.Tests.csproj index 936c47bc7..108c2a7b8 100644 --- a/Transpiler_pp/Trnsp.Show.Pas.Tests/Trnsp.Show.Pas.Tests.csproj +++ b/Transpiler_pp/Trnsp.Show.Pas.Tests/Trnsp.Show.Pas.Tests.csproj @@ -16,7 +16,7 @@ - +