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/.packages/ControlSamples.0.1.1-local.nupkg b/Avalonia_Apps/.packages/ControlSamples.0.1.1-local.nupkg new file mode 100644 index 000000000..f71a63edb Binary files /dev/null and b/Avalonia_Apps/.packages/ControlSamples.0.1.1-local.nupkg differ 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 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 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/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 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 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 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 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 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 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 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 - + 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 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 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 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 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 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 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 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 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 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 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 @@ + 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 @@ + 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 @@ + 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 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 @@ + 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 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 @@ + 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 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 @@ + 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 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 @@ + 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 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 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 @@ + 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 diff --git a/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj b/Avalonia_Apps/AA25_RichTextEdit/Avln_RichTextEdit/Avln_RichTextEdit.csproj index bacba7e74..63be81ed0 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 @@ -29,10 +30,4 @@ - - - - - - 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 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 diff --git a/Avalonia_Apps/AA98_AvlnCodeStudio.slnx b/Avalonia_Apps/AA98_AvlnCodeStudio.slnx new file mode 100644 index 000000000..c372134cb --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio.slnx @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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..f0b346975 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.AI/AA98_AvlnCodeStudio.Base.AI.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.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/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.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/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..f0b346975 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/AA98_AvlnCodeStudio.Base.OS.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.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/Services/ITextDocumentStorageService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/Services/ITextDocumentStorageService.cs similarity index 95% rename from Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/ITextDocumentStorageService.cs rename to Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/Services/ITextDocumentStorageService.cs index 4a6a70694..424e1c21b 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/ITextDocumentStorageService.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.OS/Services/ITextDocumentStorageService.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -namespace AA98_AvlnCodeStudio.Base.Services; +namespace AA98_AvlnCodeStudio.Base.OS.Services; /// /// Defines asynchronous persistence operations for plain text documents. @@ -24,4 +24,4 @@ public interface ITextDocumentStorageService /// 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.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/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..f0b346975 --- /dev/null +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/AA98_AvlnCodeStudio.Base.UI.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.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/Services/IEditorFileDialogService.cs b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/Services/IEditorFileDialogService.cs similarity index 96% rename from Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/IEditorFileDialogService.cs rename to Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/Services/IEditorFileDialogService.cs index 54cf01941..ef7832bb9 100644 --- a/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/Services/IEditorFileDialogService.cs +++ b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base.UI/Services/IEditorFileDialogService.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -namespace AA98_AvlnCodeStudio.Base.Services; +namespace AA98_AvlnCodeStudio.Base.UI.Services; /// /// Defines UI-agnostic file picker operations for the editor workflow. @@ -24,4 +24,4 @@ public interface IEditorFileDialogService /// 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.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.Base/AA98_AvlnCodeStudio.Base.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Base/AA98_AvlnCodeStudio.Base.csproj index e48052ac3..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,8 +1,9 @@ - + net8.0 enable + Debug;Release;DesktopOnly @@ -15,4 +16,8 @@ + + + + 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/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"; 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.Tests/AA98_AvlnCodeStudio.Tests.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.Tests/AA98_AvlnCodeStudio.Tests.csproj index c6efd1ac3..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 @@ -1,9 +1,10 @@ - + net8.0 true false + Debug;Release;DesktopOnly @@ -15,6 +16,7 @@ + all @@ -25,8 +27,18 @@ + + + + + + + + + + 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/AA98_AvlnCodeStudio.UI.csproj b/Avalonia_Apps/AA98_AvlnCodeStudio/AA98_AvlnCodeStudio.UI/AA98_AvlnCodeStudio.UI.csproj index 00221ab2b..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 @@ -5,6 +5,7 @@ net8.0 enable true + Debug;Release;DesktopOnly @@ -38,6 +39,13 @@ + + + + + + + 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..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,7 +1,9 @@ using AA98_AvlnCodeStudio.UI.ViewModels; using AA98_AvlnCodeStudio.UI.Views; -using AA98_AvlnCodeStudio.Base.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; @@ -39,21 +41,24 @@ private static IServiceProvider ConfigureServices(IClassicDesktopStyleApplicatio var services = new ServiceCollection(); services.AddAvaloniaCommonDialogs(() => desktop.MainWindow); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddCodeStudioFoundation(); + 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/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/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/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/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/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/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/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/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 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 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_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 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 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 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 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 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 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 diff --git a/Avalonia_Apps/Avalonia_Apps.sln b/Avalonia_Apps/Avalonia_Apps.sln index e1904dfe1..6f1d81a11 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 @@ -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 @@ -223,7 +227,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 +245,776 @@ 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 + {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 + {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 +1064,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..2620764ce 100644 --- a/Avalonia_Apps/Avalonia_Apps.slnx +++ b/Avalonia_Apps/Avalonia_Apps.slnx @@ -1,4 +1,9 @@ + + + + + @@ -78,14 +83,20 @@ + + - + + + - + + + @@ -102,7 +113,20 @@ - + + + + + + + + + + + + + + @@ -141,15 +165,29 @@ + + + + + - + + + - - + + + + + + + + + @@ -165,13 +203,16 @@ + + + + + - - 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 + 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 + 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 + 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 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 + 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 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 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 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]); 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 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..f24d56a3e 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 @@ -17,9 +18,6 @@
- - - App.axaml 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(); } diff --git a/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml new file mode 100644 index 000000000..04a45c841 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/App.axaml @@ -0,0 +1,9 @@ + + + + + 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..1245b8b75 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Program.cs @@ -0,0 +1,28 @@ +using System; +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..bc13e6062 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/HostedProcess.cs @@ -0,0 +1,105 @@ +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 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. + /// + /// The underlying process. + public HostedProcess(Process process) + { + _process = process; + _process.Exited += OnExited; + + 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; + + /// + 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.Exited -= OnExited; + _readCancellationTokenSource.Cancel(); + + if (!_process.HasExited) + { + _process.Kill(true); + await _process.WaitForExitAsync().ConfigureAwait(false); + } + + try + { + await Task.WhenAll(_standardOutputTask, _standardErrorTask).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + } + + _readCancellationTokenSource.Dispose(); + _process.Dispose(); + } + + 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..58e018801 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/IHostedProcess.cs @@ -0,0 +1,49 @@ +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 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. + /// + 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/OutputChunkReader.cs b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/OutputChunkReader.cs new file mode 100644 index 000000000..249a73fd5 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Services/OutputChunkReader.cs @@ -0,0 +1,168 @@ +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Avln_TerminalHost.Services; + +/// +/// Reads a text stream and emits complete lines immediately plus partial chunks after a short inactivity timeout. +/// +public sealed class OutputChunkReader +{ + private const int ReadBufferSize = 256; + private readonly TextReader _reader; + private readonly TimeSpan _flushDelay; + private readonly Action _lineCallback; + private readonly Action _partialCallback; + private readonly StringBuilder _buffer = new(); +#if NET9_0_OR_GREATER + private readonly Lock _syncRoot = new(); +#else + private readonly object _syncRoot = new(); +#endif + private CancellationTokenSource? _flushCancellationTokenSource; + + /// + /// Initializes a new instance of the class. + /// + /// The text reader to consume. + /// The inactivity delay before partial output is flushed. + /// The callback for complete lines. + /// The callback for partial chunks. + public OutputChunkReader(TextReader reader, TimeSpan flushDelay, Action lineCallback, Action partialCallback) + { + _reader = reader; + _flushDelay = flushDelay; + _lineCallback = lineCallback; + _partialCallback = partialCallback; + } + + /// + /// Runs the reader loop until the stream ends or cancellation is requested. + /// + /// The cancellation token. + public async Task RunAsync(CancellationToken cancellationToken) + { + var readBuffer = new char[ReadBufferSize]; + while (!cancellationToken.IsCancellationRequested) + { + var charactersRead = await _reader.ReadAsync(readBuffer.AsMemory(0, readBuffer.Length), cancellationToken).ConfigureAwait(false); + if (charactersRead == 0) + { + FlushPartialBuffer(); + return; + } + + ProcessBuffer(readBuffer.AsSpan(0, charactersRead), cancellationToken); + } + } + + private void ProcessBuffer(ReadOnlySpan buffer, CancellationToken cancellationToken) + { + var appendedPartial = false; + foreach (var character in buffer) + { + if (character == '\r') + { + continue; + } + + if (character == '\n') + { + CancelPendingFlush(); + var line = ExtractBufferedText(); + _lineCallback(line); + appendedPartial = false; + continue; + } + + lock (_syncRoot) + { + _buffer.Append(character); + } + + appendedPartial = true; + } + + if (appendedPartial) + { + ScheduleFlush(cancellationToken); + } + } + + private void ScheduleFlush(CancellationToken cancellationToken) + { + CancelPendingFlush(); + + CancellationTokenSource flushCancellationTokenSource; + lock (_syncRoot) + { + _flushCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + flushCancellationTokenSource = _flushCancellationTokenSource; + } + + _ = WaitForFlushAsync(flushCancellationTokenSource); + } + + private async Task WaitForFlushAsync(CancellationTokenSource flushCancellationTokenSource) + { + try + { + await Task.Delay(_flushDelay, flushCancellationTokenSource.Token).ConfigureAwait(false); + FlushPartialBuffer(); + } + catch (OperationCanceledException) + { + } + finally + { + flushCancellationTokenSource.Dispose(); + lock (_syncRoot) + { + if (ReferenceEquals(_flushCancellationTokenSource, flushCancellationTokenSource)) + { + _flushCancellationTokenSource = null; + } + } + } + } + + private void CancelPendingFlush() + { + CancellationTokenSource? flushCancellationTokenSource; + lock (_syncRoot) + { + flushCancellationTokenSource = _flushCancellationTokenSource; + _flushCancellationTokenSource = null; + } + + flushCancellationTokenSource?.Cancel(); + } + + private void FlushPartialBuffer() + { + CancelPendingFlush(); + var partial = ExtractBufferedText(); + if (partial.Length > 0) + { + _partialCallback(partial); + } + } + + private string ExtractBufferedText() + { + lock (_syncRoot) + { + if (_buffer.Length == 0) + { + return string.Empty; + } + + var result = _buffer.ToString(); + _buffer.Clear(); + return result; + } + } +} 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..2ead4f7d7 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,247 @@ +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; + +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 readonly StringBuilder _interactiveInputBuffer = new(); + 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(); + _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. + /// + [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); + Console.WriteLine($"> {currentInput}", ConsoleColor.Green, ConsoleColor.Black); + _interactiveInputBuffer.Clear(); + 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); + Console.WriteLine("Stopping shell process.", ConsoleColor.Yellow, ConsoleColor.Black); + } + + /// + public async ValueTask DisposeAsync() + { + if (_hostedProcess is not null) + { + await DetachAndDisposeProcessAsync(_hostedProcess); + _hostedProcess = null; + } + + _interactiveInputBuffer.Clear(); + InputText = string.Empty; + } + + private bool CanStart() => !IsRunning; + + private bool CanSendInput() => IsRunning && !string.IsNullOrWhiteSpace(InputText); + + private bool CanStop() => IsRunning; + + private void OnStandardOutputReceived(object? sender, string text) + => 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) + => 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) + { + await Dispatcher.UIThread.InvokeAsync(async () => + { + 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(); + } +} 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..d0be4b103 --- /dev/null +++ b/Avalonia_Apps/Avln_TerminalHost/Avln_TerminalHost/Views/MainWindow.axaml @@ -0,0 +1,27 @@ + + + + + + + + +