diff --git a/.gitattributes b/.gitattributes
index 1ff0c42304..5ff9000dc7 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -3,6 +3,10 @@
###############################################################################
* text=auto
+# These files are checked out using LF locally
+*.sh eol=lf
+*.txt eol=lf
+
###############################################################################
# Set default behavior for command prompt diff.
#
diff --git a/.gitignore b/.gitignore
index ee5c567d01..4613942701 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,15 +12,6 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
-# Build results
-[Dd]ebugPublic/
-x64/
-x86/
-bld/
-[Bb]in/
-[Oo]bj/
-[Ll]og/
-
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
@@ -33,16 +24,6 @@ Generated\ Files/
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
-# Assembly Info file that is automatically updated on build
-SharedAssemblyInfo.cs
-
-# TypeScript ignores
-*.js
-*.js.map
-
-# Monaco Dependency (Download from https://microsoft.github.io/monaco-editor/)
-src/dev/impl/DevToys.MonacoEditor/monaco-editor
-
# NUNIT
*.VisualState.xml
TestResult.xml
@@ -334,3 +315,131 @@ ASALocalRun/
# MFractors (Xamarin productivity tool) working folder
.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+
+##
+## Visual studio for Mac
+##
+
+
+# globs
+Makefile.in
+*.userprefs
+*.usertasks
+config.make
+config.status
+aclocal.m4
+install-sh
+autom4te.cache/
+*.tar.gz
+tarballs/
+test-results/
+
+# Mac bundle stuff
+*.dmg
+*.app
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+##
+## Visual Studio Code
+##
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+##
+## DevToys specific
+##
+
+# TypeScript ignores
+*.js
+*.js.map
+package-lock.json
+
+# Nuke Build
+.nuke/temp/
+
+# Mono auto generated files
+mono_crash.*
+
+# Monaco Dependency (Download from https://microsoft.github.io/monaco-editor/)
+src/app/dev/DevToys.MonacoEditor/monaco-editor
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+# [Oo]ut/
+[Ll]og/
+[Ll]ogs/
\ No newline at end of file
diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json
new file mode 100644
index 0000000000..eba815b551
--- /dev/null
+++ b/.nuke/build.schema.json
@@ -0,0 +1,153 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Build Schema",
+ "$ref": "#/definitions/build",
+ "definitions": {
+ "build": {
+ "type": "object",
+ "properties": {
+ "Configuration": {
+ "type": "string",
+ "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)",
+ "enum": [
+ "Debug",
+ "Release"
+ ]
+ },
+ "Continue": {
+ "type": "boolean",
+ "description": "Indicates to continue a previously failed build attempt"
+ },
+ "Help": {
+ "type": "boolean",
+ "description": "Shows the help text for this build assembly"
+ },
+ "Host": {
+ "type": "string",
+ "description": "Host for execution. Default is 'automatic'",
+ "enum": [
+ "AppVeyor",
+ "AzurePipelines",
+ "Bamboo",
+ "Bitbucket",
+ "Bitrise",
+ "GitHubActions",
+ "GitLab",
+ "Jenkins",
+ "Rider",
+ "SpaceAutomation",
+ "TeamCity",
+ "Terminal",
+ "TravisCI",
+ "VisualStudio",
+ "VSCode"
+ ]
+ },
+ "IncrementalBuild": {
+ "type": "boolean",
+ "description": "Do an incremental build"
+ },
+ "NoLogo": {
+ "type": "boolean",
+ "description": "Disables displaying the NUKE logo"
+ },
+ "Partition": {
+ "type": "string",
+ "description": "Partition to use on CI"
+ },
+ "Plan": {
+ "type": "boolean",
+ "description": "Shows the execution plan (HTML)"
+ },
+ "PlatformTargets": {
+ "type": "array",
+ "description": "The target platform",
+ "items": {
+ "type": "string",
+ "enum": [
+ "Linux",
+ "MacOS",
+ "Wasm",
+ "Windows"
+ ]
+ }
+ },
+ "Profile": {
+ "type": "array",
+ "description": "Defines the profiles to load",
+ "items": {
+ "type": "string"
+ }
+ },
+ "PublishReadyToRun": {
+ "type": "boolean",
+ "description": "https://bit.ly/3RSEo7w"
+ },
+ "PublishSelfContained": {
+ "type": "boolean",
+ "description": "https://bit.ly/2OEU0KO - Enabled by default"
+ },
+ "PublishSingleFile": {
+ "type": "boolean",
+ "description": "https://bit.ly/3xvq7FA"
+ },
+ "PublishTrimmed": {
+ "type": "boolean",
+ "description": "https://bit.ly/3RKZkNH"
+ },
+ "Root": {
+ "type": "string",
+ "description": "Root directory during build execution"
+ },
+ "RunTests": {
+ "type": "boolean",
+ "description": "Runs unit tests"
+ },
+ "Skip": {
+ "type": "array",
+ "description": "List of targets to be skipped. Empty list skips all dependencies",
+ "items": {
+ "type": "string",
+ "enum": [
+ "Clean",
+ "Compile",
+ "Publish",
+ "Restore",
+ "SetVersion",
+ "UnitTests"
+ ]
+ }
+ },
+ "Solution": {
+ "type": "string",
+ "description": "Path to a solution file that is automatically loaded"
+ },
+ "Target": {
+ "type": "array",
+ "description": "List of targets to be invoked. Default is '{default_target}'",
+ "items": {
+ "type": "string",
+ "enum": [
+ "Clean",
+ "Compile",
+ "Publish",
+ "Restore",
+ "SetVersion",
+ "UnitTests"
+ ]
+ }
+ },
+ "Verbosity": {
+ "type": "string",
+ "description": "Logging verbosity during build execution. Default is 'Normal'",
+ "enum": [
+ "Minimal",
+ "Normal",
+ "Quiet",
+ "Verbose"
+ ]
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/.nuke/parameters.json b/.nuke/parameters.json
new file mode 100644
index 0000000000..80f6b72f91
--- /dev/null
+++ b/.nuke/parameters.json
@@ -0,0 +1,4 @@
+{
+ "$schema": "./build.schema.json",
+ "Solution": "src/DevToys.sln"
+}
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9b32fc8b10..c1be9c93f9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -9,15 +9,52 @@ You can contribute to DevToys app by:
# How to Build and Run DevToys from source:
-* Make sure your machine is running on Windows 10 1903+.
-* Make sure you have Visual Studio 2019 16.10+ or Visual Studio 2022 17.0+ installed.
-* In Visual Studio Installer, install the required components by importing the [vs2022.vsconfig](vs2022.vsconfig) or [vs2019.vsconfig](vs2019.vsconfig) file.
-* Run `init.ps1` in a PowerShell command prompt to restore all the dependencies.
-* Open `src/DevToys.sln` with Visual Studio and set Solution Platform to x64*.
-* Once opened, set `src/dev/DevToys.Startup/DevToys.Startup.wapproj` as startup project.
-* Now you should be able to build and run DevToys on your machine. If it fails, try to close the solution and reopen it again.
-
-**If x64 doesn't work, use the architecture of your system*
+## On Windows
+
+### Prerequisites
+1. Make sure your machine is running on Windows 10 1903 (19h1) or later.
+1. Install [Visual Studio 2022 17.3 or later](https://visualstudio.microsoft.com/vs/) installed with the following Workloads, or import the [vs2022.vsconfig](vs2022.vsconfig) file.
+ * ASP.NET and web development
+ * .NET Multi-Platform App UI development
+ * .NET desktop development
+ * Universal Windows Platform development
+
+### Finalize your environment
+1. Clone this repository.
+1. Open a PowerShell command prompt in the root folder of this repository.
+1. Run `init.ps1` to restore all the dependencies.
+1. Open `src/DevToys.sln` with Visual Studio.
+1. Once opened, set `app/dev/platforms/DevToys.Wasm` or `app/dev/platforms/DevToys.Windows` or `app/dev/platforms/DevToys.CLI` as startup project.
+1. Now you should be able to build and run DevToys on your machine. If it fails, try to close the solution and reopen it again.
+
+## On macOS and Linux
+
+### Prerequisites
+1. [**Visual Studio Code**](https://code.visualstudio.com/)
+1. **.NET SDK**
+ * [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/6.0) (**version 6.0 (SDK 6.0.100)** or later)
+ > Use `dotnet --version` from the terminal to get the version installed.
+1. The [Uno Platform Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=unoplatform.vscode) Extension
+
+### Finalize your environment
+1. Clone this repository.
+1. Open a Terminal
+1. Install the Uno Platform tool by running the following command from the command prompt:
+ ```
+ dotnet tool install -g uno.check
+ ```
+1. Run the tool from the command prompt with the following command:
+ ```
+ uno-check
+ ```
+ If the above command fails, use the following:
+ ```
+ ~/.dotnet/tools/uno-check
+ ```
+1. Follow the instructions indicated by the tool
+1. Run `init.sh` to restore all the dependencies.
+
+// TODO explain how to debug
# Internationalization and localization
@@ -45,23 +82,11 @@ This approach is more complex but has the advantage that it allows you to test y
## Main architecture
-DevToys is using [MEF](https://docs.microsoft.com/en-us/dotnet/framework/mef/) as a dependency injection framework.
-Every tool available (i.e Base64 Encoder/Decoder, JSON Formatter, Settings...) are dynamically discovered and instantiated through MEF. A tool is divided in 3 components:
-1. [IToolProvider](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/Api/Tools/IToolProvider.cs) and its metadata, which represents the tool as displayed in the main menu in the app. `IToolProvider` should be MEF exported.
-2. [IToolViewModel](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/Api/Tools/IToolViewModel.cs), which is a ViewModel as described by the [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) pattern in UWP. It doesn't have to be MEF exported but may be required depending on what the tool needs to work.
-3. A `Page` that represents the view of the tool.
-
-The tool provider is instantiated when the app starts. The view and view models are instantiated when the user selects the tool in the main menu.
+// TODO
-## IToolProvider metadata
+## Develop a tool
-Several attributes can be used when implementing an `IToolProvider`. They can be used in customize the behavior of the tool in DevToys without needing to implement a special logic for it.
-You can find the attributes [here](https://github.com/veler/DevToys/tree/main/src/dev/impl/DevToys/Api/Tools). Here is a non-exhaustive list of attribute to use:
-* [CompactOverlaySizeAttribute](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/Api/Tools/CompactOverlaySizeAttribute.cs)
-* [NameAttribute](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/Api/Tools/NameAttribute.cs)
-* [NotScrollableAttribute](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/Api/Tools/NotScrollableAttribute.cs)
-* [OrderAttribute](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/Api/Tools/OrderAttribute.cs)
-* [ProtocolNameAttribute](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/Api/Tools/ProtocolNameAttribute.cs)
+// TODO
## Iconography
@@ -70,10 +95,7 @@ For the icons of the tools, a custom font is used. See [documentation](https://g
## Sample
-A good tool to take an example on is `Json <> Yaml` converter.
-* [The tool provider](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/ViewModels/Tools/Converters/JsonYaml/JsonYamlToolProvider.cs)
-* [The view model](https://github.com/veler/DevToys/blob/main/src/dev/impl/DevToys/ViewModels/Tools/Converters/JsonYaml/JsonYamlToolViewModel.cs)
-* [The view](https://github.com/veler/DevToys/tree/main/src/dev/impl/DevToys/Views/Tools/Converters/JsonYaml)
+// TODO
## Things to keep in mind
diff --git a/build.cmd b/build.cmd
new file mode 100644
index 0000000000..b08cc590f4
--- /dev/null
+++ b/build.cmd
@@ -0,0 +1,7 @@
+:; set -eo pipefail
+:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
+:; ${SCRIPT_DIR}/build.sh "$@"
+:; exit $?
+
+@ECHO OFF
+powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %*
diff --git a/build.ps1 b/build.ps1
new file mode 100644
index 0000000000..99d2e7ad8a
--- /dev/null
+++ b/build.ps1
@@ -0,0 +1,29 @@
+[CmdletBinding()]
+Param(
+ [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
+ [string[]]$BuildArguments
+)
+
+function ExecSafe([scriptblock] $cmd) {
+ & $cmd
+ if ($LASTEXITCODE) { exit $LASTEXITCODE }
+}
+
+Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
+$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
+
+# Install .Net
+ExecSafe { & $PSScriptRoot\tools\Install-DotNet.ps1 -RootFolder $PSScriptRoot }
+
+# Build the builder project.
+Write-Host "Building the pipeline"
+$BuildProjectFile = "$PSScriptRoot\src\build\_build.csproj"
+
+ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
+Write-Host "Done."
+Write-Output "---------------------------------------"
+
+# Run the builder
+ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
+Write-Host "Done."
+Write-Output "---------------------------------------"
diff --git a/build.sh b/build.sh
new file mode 100644
index 0000000000..2fb921511d
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+set -eo pipefail
+SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
+
+# Install .Net
+. "./tools/Install-DotNet.sh" $SCRIPT_DIR
+
+# Build the build project.
+echo "Building the pipeline"
+BUILD_PROJECT_FILE="$SCRIPT_DIR/src/build/_build.csproj"
+
+"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
+echo "Done."
+echo "--------------------------------------"
+
+# Run the building
+"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
+echo "Done."
+echo "--------------------------------------"
diff --git a/global.json b/global.json
new file mode 100644
index 0000000000..cd688df743
--- /dev/null
+++ b/global.json
@@ -0,0 +1,6 @@
+{
+ "sdk": {
+ "version": "6.0.403",
+ "rollForward": "latestFeature"
+ }
+}
\ No newline at end of file
diff --git a/init.cmd b/init.cmd
index 21ddf1d122..21d2279baf 100644
--- a/init.cmd
+++ b/init.cmd
@@ -1,4 +1,3 @@
@set PS1UnderCmd=1
powershell.exe -ExecutionPolicy bypass -Command "& '%~dpn0.ps1'" %*
-@set PS1UnderCmd=
-pause
+@set PS1UnderCmd=
\ No newline at end of file
diff --git a/init.ps1 b/init.ps1
index b42f442e06..3d97908438 100644
--- a/init.ps1
+++ b/init.ps1
@@ -1,7 +1,7 @@
-param (
- [Parameter(Mandatory = $false)]
- [Boolean]$VsPreview=$true
-)
+function ExecSafe([scriptblock] $cmd) {
+ & $cmd
+ if ($LASTEXITCODE) { exit $LASTEXITCODE }
+}
Function Get-MsBuildPath($useVsPreview) {
if (-not (Test-Path "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"))
@@ -31,39 +31,37 @@ Function Get-MsBuildPath($useVsPreview) {
return $path
}
-Function Restore-PackagesUnder($searchRoot) {
- # Restore VS solution dependencies
- Get-ChildItem $searchRoot -rec |? { $_.FullName.EndsWith('.sln') } |% {
- Write-Host "Restoring packages for $($_.FullName)..." -ForegroundColor $HeaderColor
- & "$toolsPath\Restore-NuGetPackages.ps1" -Path $_.FullName -Verbosity $nugetVerbosity -MSBuildPath $MSBuildPath
- }
-}
+Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
+$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
-Function Restore-MonacoEditor() {
- # Restore Monaco Editor
- Write-Host "Restoring Monaco Editor..." -ForegroundColor $HeaderColor
- & "$toolsPath\Restore-MonacoEditor.ps1"
-}
+# Install .Net
+ExecSafe { & $PSScriptRoot\tools\Install-DotNet.ps1 -RootFolder $PSScriptRoot }
+
+# Restore NuGet solution dependencies
+Write-Host "Restoring all dependencies"
+Get-ChildItem $PSScriptRoot\src\ -rec |? { $_.FullName.EndsWith('.sln') } |% {
+ $SolutionPath = $_.FullName;
+ Write-Host "Restoring packages for $($SolutionPath)..."
+ ExecSafe { & $env:DOTNET_EXE restore -v:quiet $SolutionPath }
-Push-Location $PSScriptRoot
-try {
- $EnvVarsSet = $false
- $HeaderColor = 'Green'
- $toolsPath = "$PSScriptRoot\tools"
- $nugetVerbosity = 'minimal'
- $MSBuildPath = Get-MsBuildPath $VsPreview
- if ($VerbosePreference -eq 'continue') { $nugetVerbosity = 'Detailed' }
+ # If we run on Windows
+ if ([System.Boolean](Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction SilentlyContinue)) {
+ $MSBuildPath = Get-MsBuildPath true
+ ExecSafe { & "$MSBuildPath" $SolutionPath /t:restore /p:Configuration=Release /p:platform=x86 /v:Quiet }
+ ExecSafe { & "$MSBuildPath" $SolutionPath /t:restore /p:Configuration=Release /p:platform=x64 /v:Quiet }
+ ExecSafe { & "$MSBuildPath" $SolutionPath /t:restore /p:Configuration=Release /p:platform=arm64 /v:Quiet }
+ ExecSafe { & "$MSBuildPath" $SolutionPath /t:restore /p:Configuration=Debug /p:platform=x86 /v:Quiet }
+ ExecSafe { & "$MSBuildPath" $SolutionPath /t:restore /p:Configuration=Debug /p:platform=x64 /v:Quiet }
+ ExecSafe { & "$MSBuildPath" $SolutionPath /t:restore /p:Configuration=Debug /p:platform=arm64 /v:Quiet }
+ }
+}
- Restore-PackagesUnder "$PSScriptRoot\src"
+Write-Host "Done."
+Write-Output "---------------------------------------"
- Restore-MonacoEditor
+# Restore Monaco Editor
+Write-Host "Restoring Monaco Editor"
+ExecSafe { & $PSScriptRoot\tools\Restore-MonacoEditor.ps1 -RootFolder $PSScriptRoot }
- Write-Host "Successfully restored all dependencies" -ForegroundColor Yellow
-}
-catch {
- Write-Error $error[0]
- exit $lastexitcode
-}
-finally {
- Pop-Location
-}
+Write-Host "Done."
+Write-Output "---------------------------------------"
\ No newline at end of file
diff --git a/init.sh b/init.sh
new file mode 100644
index 0000000000..52e843cccb
--- /dev/null
+++ b/init.sh
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+bash --version 2>&1 | head -n 1
+
+set -eo pipefail
+SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
+SCRIPT_DIR="${SCRIPT_DIR}/"
+
+# Install .Net
+. "./tools/Install-DotNet.sh" "$SCRIPT_DIR"
+
+# Restore NuGet solution dependencies
+echo "Restoring all dependencies"
+SOLUTIONS=$(find ./src/ -iname "*.sln" -print)
+for SOLUTION_FILE in $SOLUTIONS
+do
+ echo "Restoring packages for $SOLUTION_FILE..."
+ "$DOTNET_EXE" restore -v:quiet $SOLUTION_FILE
+done
+
+echo "Done."
+echo "---------------------------------------"
+
+# Restore Monaco Editor
+echo "Restoring Monaco Editor"
+. "./tools/Restore-MonacoEditor.sh" "$SCRIPT_DIR"
+
+echo "Done."
+echo "---------------------------------------"
diff --git a/nuget.config b/nuget.config
index 6c53736391..7e839468fa 100644
--- a/nuget.config
+++ b/nuget.config
@@ -8,4 +8,13 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.editorconfig b/src/.editorconfig
index 906db32dcf..bf0b7b37c1 100644
--- a/src/.editorconfig
+++ b/src/.editorconfig
@@ -1,10 +1,22 @@
-# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
-###############################
-# Core EditorConfig Options #
-###############################
-# All files
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Use CRLF for end of line
+[*]
+end_of_line = crlf
+
+# Don't use tabs for indentation.
[*]
indent_style = space
+# (Please don't specify an indent_size here; that has too many unintended consequences.)
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+insert_final_newline = true
+charset = utf-8-bom
# XML project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
@@ -14,103 +26,155 @@ indent_size = 2
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
-# Code files
-[*.{cs,csx,vb,vbx}]
-indent_size = 4
-insert_final_newline = true
-charset = utf-8-bom
-###############################
-# .NET Coding Conventions #
-###############################
+# JSON files
+[*.json]
+indent_size = 2
+
+# Powershell files
+[*.ps1]
+indent_size = 2
+
+# Shell script files
+[*.sh]
+end_of_line = lf
+indent_size = 2
+
+# Dotnet code style settings:
[*.{cs,vb}]
-# Organize usings
+
+# IDE0055: Fix formatting
+dotnet_diagnostic.IDE0055.severity = warning
+
+# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
-# this. preferences
-dotnet_style_qualification_for_field =false:warning
-dotnet_style_qualification_for_property =false:warning
-dotnet_style_qualification_for_method =false:warning
-dotnet_style_qualification_for_event =false:warning
-# Language keywords vs BCL types preferences
-dotnet_style_predefined_type_for_locals_parameters_members = true:silent
-dotnet_style_predefined_type_for_member_access = true:silent
-# Parentheses preferences
-dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
-# Modifier preferences
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
-dotnet_style_readonly_field =true:warning
-# Expression-level preferences
+dotnet_separate_import_directive_groups = false
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = false:refactoring
+dotnet_style_qualification_for_property = false:refactoring
+dotnet_style_qualification_for_method = false:refactoring
+dotnet_style_qualification_for_event = false:refactoring
+
+# Use language keywords instead of framework type names for type references
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
-dotnet_style_explicit_tuple_names =true:warning
-dotnet_style_null_propagation =true:warning
dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_prefer_is_null_check_over_reference_equality_method =true:warning
-dotnet_style_prefer_inferred_tuple_names = true:suggestion
-dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
-dotnet_style_prefer_auto_properties = true:silent
-dotnet_style_prefer_conditional_expression_over_assignment = true:silent
-dotnet_style_prefer_conditional_expression_over_return = true:silent
-###############################
-# Naming Conventions #
-###############################
-# Style Definitions
-dotnet_naming_style.pascal_case_style.capitalization = pascal_case
-# Use PascalCase for constant fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
-dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
-dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
-dotnet_naming_symbols.constant_fields.applicable_kinds = field
-dotnet_naming_symbols.constant_fields.applicable_accessibilities = *
-dotnet_naming_symbols.constant_fields.required_modifiers = const
-dotnet_style_namespace_match_folder=true:warning
-dotnet_style_allow_multiple_blank_lines_experimental=true:warning
-dotnet_code_quality_unused_parameters=all:warning
-# CS8600: Converting null literal or possible null value to non-nullable type.
-dotnet_diagnostic.CS8600.severity = error
-# CS8604: Possible null reference argument.
-dotnet_diagnostic.CS8604.severity = error
-# CS8765: Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes).
-dotnet_diagnostic.CS8765.severity = error
-# CS8602: Dereference of a possibly null reference.
-dotnet_diagnostic.CS8602.severity = error
-# CS0162: Unreachable code detected
-dotnet_diagnostic.CS0162.severity = error
-###############################
-# C# Coding Conventions #
-###############################
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+
+# Whitespace options
+dotnet_style_allow_multiple_blank_lines_experimental = false
+
+# Non-private static fields are PascalCase
+dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields
+dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
+
+dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
+dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
+
+dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
+
+# Non-private readonly fields are PascalCase
+dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields
+dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style
+
+dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
+dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly
+
+dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case
+
+# Constants are PascalCase
+dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants
+dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style
+
+dotnet_naming_symbols.constants.applicable_kinds = field, local
+dotnet_naming_symbols.constants.required_modifiers = const
+
+dotnet_naming_style.constant_style.capitalization = pascal_case
+
+# Static fields are camelCase
+dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion
+dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields
+dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style
+
+dotnet_naming_symbols.static_fields.applicable_kinds = field
+dotnet_naming_symbols.static_fields.required_modifiers = static
+
+dotnet_naming_style.static_field_style.capitalization = camel_case
+
+# Instance fields are camelCase and start with _
+dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion
+dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields
+dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style
+
+dotnet_naming_symbols.instance_fields.applicable_kinds = field
+
+dotnet_naming_style.instance_field_style.capitalization = camel_case
+dotnet_naming_style.instance_field_style.required_prefix = _
+
+# Locals and parameters are camelCase
+dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion
+dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters
+dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style
+
+dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local
+
+dotnet_naming_style.camel_case_style.capitalization = camel_case
+
+# Local functions are PascalCase
+dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+
+dotnet_naming_style.local_function_style.capitalization = pascal_case
+
+# By default, name items with PascalCase
+dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members
+dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style
+
+dotnet_naming_symbols.all_members.applicable_kinds = *
+
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+# error RS2008: Enable analyzer release tracking for the analyzer project containing rule '{0}'
+dotnet_diagnostic.RS2008.severity = none
+
+# IDE0035: Remove unreachable code
+dotnet_diagnostic.IDE0035.severity = warning
+
+# IDE0036: Order modifiers
+dotnet_diagnostic.IDE0036.severity = warning
+
+# IDE0043: Format string contains invalid placeholder
+dotnet_diagnostic.IDE0043.severity = warning
+
+# IDE0044: Make field readonly
+dotnet_diagnostic.IDE0044.severity = warning
+
+# CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings?
+# IDE0051: Remove unused private member
+dotnet_diagnostic.IDE0051.severity = warning
+
+# IDE0170: Prefer extended property pattern
+dotnet_diagnostic.IDE0170.severity = warning
+
+# RS0016: Only enable if API files are present
+dotnet_public_api_analyzer.require_api_files = true
+
+# CSharp code style settings:
[*.cs]
-# var preferences
-csharp_style_var_for_built_in_types =false:warning
-csharp_style_var_when_type_is_apparent =true:warning
-csharp_style_var_elsewhere =false:warning
-# Expression-bodied members
-csharp_style_expression_bodied_methods = false:silent
-csharp_style_expression_bodied_constructors = false:silent
-csharp_style_expression_bodied_operators = false:silent
-csharp_style_expression_bodied_properties = true:silent
-csharp_style_expression_bodied_indexers = true:silent
-csharp_style_expression_bodied_accessors = true:silent
-# Pattern matching preferences
-csharp_style_pattern_matching_over_is_with_cast_check =true:error
-csharp_style_pattern_matching_over_as_with_null_check =true:error
-# Null-checking preferences
-csharp_style_throw_expression = true:suggestion
-csharp_style_conditional_delegate_call = true:suggestion
-# Modifier preferences
-csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion
-# Expression-level preferences
-csharp_prefer_braces =true:error
-csharp_style_deconstructed_variable_declaration = true:suggestion
-csharp_prefer_simple_default_expression = true:suggestion
-csharp_style_pattern_local_over_anonymous_function = true:suggestion
-csharp_style_inlined_variable_declaration =true:warning
-###############################
-# C# Formatting Rules #
-###############################
-# New line preferences
+# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
@@ -118,34 +182,118 @@ csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
+
# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
+
+# Whitespace options
+csharp_style_allow_embedded_statements_on_same_line_experimental = false
+csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
+csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
+
+# Prefer "var" everywhere
+csharp_style_var_for_built_in_types = false:warning
+csharp_style_var_when_type_is_apparent = true:warning
+csharp_style_var_elsewhere = false:warning
+
+# Prefer method-like constructs to have a block body
+csharp_style_expression_bodied_methods = false:none
+csharp_style_expression_bodied_constructors = false:none
+csharp_style_expression_bodied_operators = false:none
+
+# Prefer property-like constructs to have an expression-body
+csharp_style_expression_bodied_properties = true:none
+csharp_style_expression_bodied_indexers = true:none
+csharp_style_expression_bodied_accessors = true:none
+
+# Suggest more modern language features when available
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+csharp_style_prefer_extended_property_pattern = true:suggestion
+
# Space preferences
csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = do_not_ignore
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
-csharp_space_before_colon_in_inheritance_clause = true
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_around_binary_operators = before_and_after
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-# Wrapping preferences
-csharp_preserve_single_line_statements = true
+csharp_space_between_square_brackets = false
+
+# Blocks are allowed
+csharp_prefer_braces = true:silent
csharp_preserve_single_line_blocks = true
-csharp_indent_braces=false
-csharp_using_directive_placement=outside_namespace:warning
-csharp_prefer_static_local_function=true:warning
-csharp_style_prefer_switch_expression=true:warning
-csharp_style_prefer_pattern_matching=true:warning
-csharp_style_prefer_not_pattern=true:warning
-###############################
-# VB Coding Conventions #
-###############################
-[*.vb]
-# Modifier preferences
-visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
+csharp_preserve_single_line_statements = true
+
+# IDE0011: Add braces
+csharp_prefer_braces = when_multiline:warning
+# NOTE: We need the below severity entry for Add Braces due to https://github.com/dotnet/roslyn/issues/44201
+dotnet_diagnostic.IDE0011.severity = warning
+
+# IDE0040: Add accessibility modifiers
+dotnet_diagnostic.IDE0040.severity = warning
+
+# IDE0052: Remove unread private member
+dotnet_diagnostic.IDE0052.severity = warning
+
+# IDE0059: Unnecessary assignment to a value
+dotnet_diagnostic.IDE0059.severity = warning
+
+# IDE0060: Remove unused parameter
+dotnet_diagnostic.IDE0060.severity = warning
+
+# CA1012: Abstract types should not have public constructors
+dotnet_diagnostic.CA1012.severity = warning
+
+# CA1822: Make member static
+dotnet_diagnostic.CA1822.severity = warning
+
+# Prefer "var" everywhere
+dotnet_diagnostic.IDE0007.severity = warning
+csharp_style_var_for_built_in_types = false:warning
+csharp_style_var_when_type_is_apparent = true:warning
+csharp_style_var_elsewhere = false:warning
+
+# IDE0160: Convert to file-scoped namespace
+csharp_style_namespace_declarations = file_scoped:warning
+
+# dotnet_style_allow_multiple_blank_lines_experimental
+dotnet_diagnostic.IDE2000.severity = warning
+
+# csharp_style_allow_embedded_statements_on_same_line_experimental
+dotnet_diagnostic.IDE2001.severity = warning
+
+# csharp_style_allow_blank_lines_between_consecutive_braces_experimental
+dotnet_diagnostic.IDE2002.severity = warning
+
+# dotnet_style_allow_statement_immediately_after_block_experimental
+dotnet_diagnostic.IDE2003.severity = warning
+
+# csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental
+dotnet_diagnostic.IDE2004.severity = warning
+
+# IDE0057: Use range operator
+dotnet_diagnostic.IDE0057.severity = none
diff --git a/src/DevToys.sln b/src/DevToys.sln
index 0caf106422..45b2f7ee60 100644
--- a/src/DevToys.sln
+++ b/src/DevToys.sln
@@ -1,159 +1,278 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.0.31903.59
+VisualStudioVersion = 17.4.33122.133
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dev", "dev", "{2C2B8C64-7BA1-4244-86E1-48CA7D85D7D9}"
- ProjectSection(SolutionItems) = preProject
- dev\Directory.Build.props = dev\Directory.Build.props
- EndProjectSection
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{0AC87D9B-BBEC-4DCE-A9D4-82A159E5E7C1}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{F8A9E843-7AC1-414A-8F27-A8F416D2D489}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "app", "app", "{C85D2D7C-3D69-482A-8AB6-BCA934220EC0}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "impl", "impl", "{93B72B9C-08BA-4838-9163-3ED67EC3EB9F}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dev", "dev", "{B20E4027-0777-4A75-A848-609594E9A6B0}"
ProjectSection(SolutionItems) = preProject
- dev\impl\Directory.Build.props = dev\impl\Directory.Build.props
- dev\impl\Directory.Build.targets = dev\impl\Directory.Build.targets
+ app\dev\Directory.Build.props = app\dev\Directory.Build.props
+ app\dev\Directory.Build.targets = app\dev\Directory.Build.targets
EndProjectSection
EndProject
-Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "DevToys.Startup", "dev\DevToys.Startup\DevToys.Startup.wapproj", "{361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4C40BF47-315A-4B60-9ED6-8D65B5E79C2C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevToys", "dev\impl\DevToys\DevToys.csproj", "{E3E4E200-B380-4207-9A7E-4C9421904502}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "platforms", "platforms", "{3D2285D5-0E44-4B1A-9B74-2BBE3108D5B7}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B4C026E3-9BBF-434B-A14D-712C6A3731E4}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{9771D1F8-D331-4B26-8E96-6024893009F4}"
ProjectSection(SolutionItems) = preProject
- .editorconfig = .editorconfig
- ..\.vsconfig = ..\.vsconfig
- ..\CONTRIBUTING.md = ..\CONTRIBUTING.md
- Directory.Build.props = Directory.Build.props
- ..\LICENSE.md = ..\LICENSE.md
- ..\PRIVACY-POLICY.md = ..\PRIVACY-POLICY.md
- ..\README.md = ..\README.md
- ..\THIRD-PARTY-NOTICES.md = ..\THIRD-PARTY-NOTICES.md
+ app\dev\shared\GlobalUsings.cs = app\dev\shared\GlobalUsings.cs
+ app\dev\shared\SharedAssemblyInfo.cs = app\dev\shared\SharedAssemblyInfo.cs
+ app\dev\shared\SharedAssemblyVersion.cs = app\dev\shared\SharedAssemblyVersion.cs
EndProjectSection
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{EE2227BC-954E-4053-AE38-4AE7A391AEEA}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{97141F3A-C655-4140-B09B-466DDC14F870}"
ProjectSection(SolutionItems) = preProject
- dev\shared\SharedAssemblyInfo.cs = dev\shared\SharedAssemblyInfo.cs
+ .editorconfig = .editorconfig
+ Directory.Build.props = Directory.Build.props
+ Directory.Build.targets = Directory.Build.targets
+ Directory.Packages.props = Directory.Packages.props
+ Environment.props = Environment.props
EndProjectSection
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevToys.Tests", "tests\DevToys.Tests\DevToys.Tests.csproj", "{E1C4AB49-CFFF-420E-A182-E3028BD0493C}"
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "desktops", "desktops", "{22EE9386-CEFA-47D8-97B2-09A63EDF7F2E}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "macos", "macos", "{8B003389-15D4-4466-A40E-50FC0D603BF6}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "windows", "windows", "{AFE37797-0A43-4838-8C1E-4D2D1446499C}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "web", "web", "{04F9E03E-B519-49C5-BC6A-2131ED9BADC0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevToys.MonacoEditor", "dev\impl\DevToys.MonacoEditor\DevToys.MonacoEditor.csproj", "{0FE678BB-9F9B-4495-B777-F9B1E11DA20C}"
+Project("{C7167F0D-BC9F-4E6E-AFE1-012C56B48DB5}") = "DevToys.Windows", "app\dev\platforms\desktop\windows\DevToys.Windows\DevToys.Windows.wapproj", "{34D966D5-5756-4192-B4AB-44AD6FF07CC5}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.OutOfProcService", "dev\impl\DevToys.OutOfProcService\DevToys.OutOfProcService.csproj", "{61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevToys.Windows.Core", "app\dev\platforms\desktop\windows\DevToys.Windows.Core\DevToys.Windows.Core.csproj", "{97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.Shared", "dev\shared\DevToys.Shared\DevToys.Shared.csproj", "{CCBCAEFE-B245-413A-9C37-A21CE99EECB9}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.CLI", "app\dev\platforms\desktop\DevToys.CLI\DevToys.CLI.csproj", "{8C72D38A-658D-4C9D-B0AD-2DA975BE7826}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.Api", "app\dev\DevToys.Api\DevToys.Api.csproj", "{89A4D88F-5AC0-436A-8EC5-8E98728CA89E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.UnitTests", "app\tests\DevToys.UnitTests\DevToys.UnitTests.csproj", "{CCAB7530-4341-4C24-B4E8-28679BB6C4FB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.Tools", "app\dev\DevToys.Tools\DevToys.Tools.csproj", "{B6D48157-5257-4B60-B065-3B231B610A54}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.Core", "app\dev\DevToys.Core\DevToys.Core.csproj", "{A730FA8A-3713-4F7A-915D-79098CA12DF0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.UI.Framework", "app\dev\DevToys.UI.Framework\DevToys.UI.Framework.csproj", "{723A3D71-2425-49EE-B669-1A0DF3A8B486}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevToys.UI", "app\dev\DevToys.UI\DevToys.UI.csproj", "{104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "DevToys.UI.EntryPoint", "app\dev\DevToys.UI.EntryPoint\DevToys.UI.EntryPoint.shproj", "{93E31B7F-903D-4F24-B30D-220489242866}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|arm64 = Debug|arm64
+ Debug|AnyCPU = Debug|AnyCPU
+ Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
- Release|arm64 = Release|arm64
+ Release|AnyCPU = Release|AnyCPU
+ Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|arm64.ActiveCfg = Debug|arm64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|arm64.Build.0 = Debug|arm64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|arm64.Deploy.0 = Debug|arm64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|x64.ActiveCfg = Debug|x64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|x64.Build.0 = Debug|x64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|x64.Deploy.0 = Debug|x64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|x86.ActiveCfg = Debug|x86
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|x86.Build.0 = Debug|x86
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Debug|x86.Deploy.0 = Debug|x86
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|arm64.ActiveCfg = Release|arm64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|arm64.Build.0 = Release|arm64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|arm64.Deploy.0 = Release|arm64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|x64.ActiveCfg = Release|x64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|x64.Build.0 = Release|x64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|x64.Deploy.0 = Release|x64
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|x86.ActiveCfg = Release|x86
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|x86.Build.0 = Release|x86
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7}.Release|x86.Deploy.0 = Release|x86
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Debug|arm64.ActiveCfg = Debug|arm64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Debug|arm64.Build.0 = Debug|arm64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Debug|x64.ActiveCfg = Debug|x64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Debug|x64.Build.0 = Debug|x64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Debug|x86.ActiveCfg = Debug|x86
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Debug|x86.Build.0 = Debug|x86
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Release|arm64.ActiveCfg = Release|arm64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Release|arm64.Build.0 = Release|arm64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Release|x64.ActiveCfg = Release|x64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Release|x64.Build.0 = Release|x64
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Release|x86.ActiveCfg = Release|x86
- {E3E4E200-B380-4207-9A7E-4C9421904502}.Release|x86.Build.0 = Release|x86
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|arm64.ActiveCfg = Debug|arm64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|arm64.Build.0 = Debug|arm64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|arm64.Deploy.0 = Debug|arm64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|x64.ActiveCfg = Debug|x64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|x64.Build.0 = Debug|x64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|x64.Deploy.0 = Debug|x64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|x86.ActiveCfg = Debug|x86
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|x86.Build.0 = Debug|x86
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Debug|x86.Deploy.0 = Debug|x86
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|arm64.ActiveCfg = Release|arm64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|arm64.Build.0 = Release|arm64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|arm64.Deploy.0 = Release|arm64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|x64.ActiveCfg = Release|x64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|x64.Build.0 = Release|x64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|x64.Deploy.0 = Release|x64
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|x86.ActiveCfg = Release|x86
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|x86.Build.0 = Release|x86
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C}.Release|x86.Deploy.0 = Release|x86
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Debug|arm64.ActiveCfg = Debug|arm64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Debug|arm64.Build.0 = Debug|arm64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Debug|x64.ActiveCfg = Debug|x64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Debug|x64.Build.0 = Debug|x64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Debug|x86.ActiveCfg = Debug|x86
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Debug|x86.Build.0 = Debug|x86
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Release|arm64.ActiveCfg = Release|arm64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Release|arm64.Build.0 = Release|arm64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Release|x64.ActiveCfg = Release|x64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Release|x64.Build.0 = Release|x64
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Release|x86.ActiveCfg = Release|x86
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C}.Release|x86.Build.0 = Release|x86
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Debug|arm64.ActiveCfg = Debug|arm64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Debug|arm64.Build.0 = Debug|arm64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Debug|x64.ActiveCfg = Debug|x64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Debug|x64.Build.0 = Debug|x64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Debug|x86.ActiveCfg = Debug|x86
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Debug|x86.Build.0 = Debug|x86
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Release|arm64.ActiveCfg = Release|arm64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Release|arm64.Build.0 = Release|arm64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Release|x64.ActiveCfg = Release|x64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Release|x64.Build.0 = Release|x64
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Release|x86.ActiveCfg = Release|x86
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58}.Release|x86.Build.0 = Release|x86
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Debug|arm64.ActiveCfg = Debug|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Debug|arm64.Build.0 = Debug|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Debug|x64.ActiveCfg = Debug|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Debug|x64.Build.0 = Debug|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Debug|x86.ActiveCfg = Debug|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Debug|x86.Build.0 = Debug|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Release|arm64.ActiveCfg = Release|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Release|arm64.Build.0 = Release|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Release|x64.ActiveCfg = Release|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Release|x64.Build.0 = Release|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Release|x86.ActiveCfg = Release|Any CPU
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9}.Release|x86.Build.0 = Release|Any CPU
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|AnyCPU.ActiveCfg = Debug|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|AnyCPU.Build.0 = Debug|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|AnyCPU.Deploy.0 = Debug|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|ARM64.Build.0 = Debug|ARM64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|ARM64.Deploy.0 = Debug|ARM64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|x64.ActiveCfg = Debug|x64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|x64.Build.0 = Debug|x64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|x64.Deploy.0 = Debug|x64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|x86.ActiveCfg = Debug|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|x86.Build.0 = Debug|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Debug|x86.Deploy.0 = Debug|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|AnyCPU.ActiveCfg = Release|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|AnyCPU.Build.0 = Release|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|AnyCPU.Deploy.0 = Release|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|ARM64.ActiveCfg = Release|ARM64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|ARM64.Build.0 = Release|ARM64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|ARM64.Deploy.0 = Release|ARM64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|x64.ActiveCfg = Release|x64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|x64.Build.0 = Release|x64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|x64.Deploy.0 = Release|x64
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|x86.ActiveCfg = Release|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|x86.Build.0 = Release|x86
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5}.Release|x86.Deploy.0 = Release|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|AnyCPU.ActiveCfg = Debug|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|AnyCPU.Build.0 = Debug|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|ARM64.ActiveCfg = Debug|arm64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|ARM64.Build.0 = Debug|arm64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|x64.ActiveCfg = Debug|x64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|x64.Build.0 = Debug|x64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|x86.ActiveCfg = Debug|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Debug|x86.Build.0 = Debug|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|AnyCPU.ActiveCfg = Release|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|AnyCPU.Build.0 = Release|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|ARM64.ActiveCfg = Release|arm64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|ARM64.Build.0 = Release|arm64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|x64.ActiveCfg = Release|x64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|x64.Build.0 = Release|x64
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|x86.ActiveCfg = Release|x86
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE}.Release|x86.Build.0 = Release|x86
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|x64.Build.0 = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Debug|x86.Build.0 = Debug|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|ARM64.Build.0 = Release|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|x64.ActiveCfg = Release|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|x64.Build.0 = Release|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|x86.ActiveCfg = Release|Any CPU
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826}.Release|x86.Build.0 = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|x64.Build.0 = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Debug|x86.Build.0 = Debug|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|ARM64.Build.0 = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|x64.ActiveCfg = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|x64.Build.0 = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|x86.ActiveCfg = Release|Any CPU
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4}.Release|x86.Build.0 = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|x64.Build.0 = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Debug|x86.Build.0 = Debug|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|ARM64.Build.0 = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|x64.ActiveCfg = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|x64.Build.0 = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|x86.ActiveCfg = Release|Any CPU
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E}.Release|x86.Build.0 = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|x64.Build.0 = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Debug|x86.Build.0 = Debug|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|ARM64.Build.0 = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|x64.ActiveCfg = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|x64.Build.0 = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|x86.ActiveCfg = Release|Any CPU
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB}.Release|x86.Build.0 = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|x64.Build.0 = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Debug|x86.Build.0 = Debug|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|ARM64.Build.0 = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|x64.ActiveCfg = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|x64.Build.0 = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|x86.ActiveCfg = Release|Any CPU
+ {B6D48157-5257-4B60-B065-3B231B610A54}.Release|x86.Build.0 = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|x64.Build.0 = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Debug|x86.Build.0 = Debug|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|ARM64.Build.0 = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|x64.ActiveCfg = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|x64.Build.0 = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|x86.ActiveCfg = Release|Any CPU
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0}.Release|x86.Build.0 = Release|Any CPU
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|ARM64.ActiveCfg = Debug|arm64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|ARM64.Build.0 = Debug|arm64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|x64.ActiveCfg = Debug|x64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|x64.Build.0 = Debug|x64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|x86.ActiveCfg = Debug|x86
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Debug|x86.Build.0 = Debug|x86
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|ARM64.ActiveCfg = Release|arm64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|ARM64.Build.0 = Release|arm64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|x64.ActiveCfg = Release|x64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|x64.Build.0 = Release|x64
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|x86.ActiveCfg = Release|x86
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486}.Release|x86.Build.0 = Release|x86
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|ARM64.ActiveCfg = Debug|arm64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|ARM64.Build.0 = Debug|arm64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|x64.ActiveCfg = Debug|x64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|x64.Build.0 = Debug|x64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|x86.ActiveCfg = Debug|x86
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Debug|x86.Build.0 = Debug|x86
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|AnyCPU.Build.0 = Release|Any CPU
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|ARM64.ActiveCfg = Release|arm64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|ARM64.Build.0 = Release|arm64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|x64.ActiveCfg = Release|x64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|x64.Build.0 = Release|x64
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|x86.ActiveCfg = Release|x86
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
- {93B72B9C-08BA-4838-9163-3ED67EC3EB9F} = {2C2B8C64-7BA1-4244-86E1-48CA7D85D7D9}
- {361DB7BC-BE7B-41A2-A29A-978A9E3B4AA7} = {2C2B8C64-7BA1-4244-86E1-48CA7D85D7D9}
- {E3E4E200-B380-4207-9A7E-4C9421904502} = {93B72B9C-08BA-4838-9163-3ED67EC3EB9F}
- {EE2227BC-954E-4053-AE38-4AE7A391AEEA} = {2C2B8C64-7BA1-4244-86E1-48CA7D85D7D9}
- {E1C4AB49-CFFF-420E-A182-E3028BD0493C} = {F8A9E843-7AC1-414A-8F27-A8F416D2D489}
- {0FE678BB-9F9B-4495-B777-F9B1E11DA20C} = {93B72B9C-08BA-4838-9163-3ED67EC3EB9F}
- {61AAE058-5FB6-43C2-BFD2-7B5D8128EF58} = {93B72B9C-08BA-4838-9163-3ED67EC3EB9F}
- {CCBCAEFE-B245-413A-9C37-A21CE99EECB9} = {EE2227BC-954E-4053-AE38-4AE7A391AEEA}
+ {B20E4027-0777-4A75-A848-609594E9A6B0} = {C85D2D7C-3D69-482A-8AB6-BCA934220EC0}
+ {4C40BF47-315A-4B60-9ED6-8D65B5E79C2C} = {C85D2D7C-3D69-482A-8AB6-BCA934220EC0}
+ {3D2285D5-0E44-4B1A-9B74-2BBE3108D5B7} = {B20E4027-0777-4A75-A848-609594E9A6B0}
+ {9771D1F8-D331-4B26-8E96-6024893009F4} = {B20E4027-0777-4A75-A848-609594E9A6B0}
+ {22EE9386-CEFA-47D8-97B2-09A63EDF7F2E} = {3D2285D5-0E44-4B1A-9B74-2BBE3108D5B7}
+ {8B003389-15D4-4466-A40E-50FC0D603BF6} = {22EE9386-CEFA-47D8-97B2-09A63EDF7F2E}
+ {AFE37797-0A43-4838-8C1E-4D2D1446499C} = {22EE9386-CEFA-47D8-97B2-09A63EDF7F2E}
+ {04F9E03E-B519-49C5-BC6A-2131ED9BADC0} = {3D2285D5-0E44-4B1A-9B74-2BBE3108D5B7}
+ {34D966D5-5756-4192-B4AB-44AD6FF07CC5} = {AFE37797-0A43-4838-8C1E-4D2D1446499C}
+ {97AA45F4-41E2-4FDA-B5B7-F4579DF2C7EE} = {AFE37797-0A43-4838-8C1E-4D2D1446499C}
+ {8C72D38A-658D-4C9D-B0AD-2DA975BE7826} = {22EE9386-CEFA-47D8-97B2-09A63EDF7F2E}
+ {745FD4CA-84CC-45D8-A3E2-814A0B9C16C4} = {0AC87D9B-BBEC-4DCE-A9D4-82A159E5E7C1}
+ {89A4D88F-5AC0-436A-8EC5-8E98728CA89E} = {B20E4027-0777-4A75-A848-609594E9A6B0}
+ {CCAB7530-4341-4C24-B4E8-28679BB6C4FB} = {4C40BF47-315A-4B60-9ED6-8D65B5E79C2C}
+ {B6D48157-5257-4B60-B065-3B231B610A54} = {B20E4027-0777-4A75-A848-609594E9A6B0}
+ {A730FA8A-3713-4F7A-915D-79098CA12DF0} = {B20E4027-0777-4A75-A848-609594E9A6B0}
+ {723A3D71-2425-49EE-B669-1A0DF3A8B486} = {B20E4027-0777-4A75-A848-609594E9A6B0}
+ {104F1E4B-E0D3-4D87-9DA0-3FE82FE2573A} = {B20E4027-0777-4A75-A848-609594E9A6B0}
+ {93E31B7F-903D-4F24-B30D-220489242866} = {B20E4027-0777-4A75-A848-609594E9A6B0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {78B10789-385B-4F7D-8E06-D86143CD76B5}
+ SolutionGuid = {C2C953F5-F97F-4198-B15C-4947A886050F}
+ EndGlobalSection
+ GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ app\dev\DevToys.UI.EntryPoint\DevToys.UI.EntryPoint.projitems*{93e31b7f-903d-4f24-b30d-220489242866}*SharedItemsImports = 13
+ app\dev\DevToys.UI.EntryPoint\DevToys.UI.EntryPoint.projitems*{97aa45f4-41e2-4fda-b5b7-f4579df2c7ee}*SharedItemsImports = 4
EndGlobalSection
EndGlobal
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 125d3ec8fe..c4a81abd91 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,42 +1,42 @@
-
- Debug
- x86
-
- 10.0.19041.0
- 10.0.19041.0
-
-
- $([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))\
- $(RepoRoot)bin\$(Configuration)\$(Platform)\
- $(BaseOutputPath)$(MSBuildProjectName)\
- $(RepoRoot)obj\$(Platform)\$(MSBuildProjectName)\
- $(BaseIntermediateOutputPath)
- $(BaseIntermediateOutputPath)Generated Files\
- $(RepoRoot)packages\
-
-
-
- 0.7.1
- 6.0.0
- 2.2.0
- 0.26.0
- 6.2.12
- 7.0.2
- 7.0.2
- 7.0.2
- 7.0.2
- 4.4.2
- 2.7.1
- 1.3.5
- 2.2.8
- 2.2.8
- 12.0.3
- 2.1.0
- 1.26.0
- 11.2.1
- 6.13.1
- 1.1.0
- 1.9.0
-
+
+
+
+ 16.0
+
+ 10.0.22000.0
+ 10.0.18362
+ $(Windows10MinSDKVersionTrimmed).0
+
+
+ netstandard2.0
+
+ net6.0
+ $(NetCore)-windows$(Windows10MinSDKVersionTrimmed)
+
+ uap$(Windows10MinSDKVersionTrimmed)
+
+
+ Debug
+ AnyCPU
+ true
+
+
+ $([System.IO.Path]::GetDirectoryName($([MSBuild]::GetPathOfFileAbove('.gitignore', '$(MSBuildThisFileDirectory)'))))\
+ $(RepoRoot)bin\$(Configuration)\$(Platform)\
+ $(BaseOutputPath)$(MSBuildProjectName)\
+ $(RepoRoot)obj\$(Platform)\$(MSBuildProjectName)\
+ $(BaseIntermediateOutputPath)\$(Configuration)
+ $(BaseIntermediateOutputPath)Generated Files\
+ $(RepoRoot)packages\
+
+
+ true
+ false
+
+
+
+ 16.0
+
+
\ No newline at end of file
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
new file mode 100644
index 0000000000..2026e8c1d6
--- /dev/null
+++ b/src/Directory.Build.targets
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
new file mode 100644
index 0000000000..75f56b44dc
--- /dev/null
+++ b/src/Directory.Packages.props
@@ -0,0 +1,54 @@
+
+
+
+ 8.0.0
+ 6.0.0
+ 6.2.14
+ 10.0.22621.1
+ 1.8.1
+ 4.6.19
+ 8.0.0-dev.65
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Environment.props b/src/Environment.props
new file mode 100644
index 0000000000..36c778872d
--- /dev/null
+++ b/src/Environment.props
@@ -0,0 +1,13 @@
+
+
+ $([MSBuild]::IsOSPlatform('Windows'))
+ $([MSBuild]::IsOSPlatform('OSX'))
+ $([MSBuild]::IsOSPlatform('Linux'))
+
+
+
+ $(DefineConstants);WINDOWS
+ $(DefineConstants);MAC
+ $(DefineConstants);LINUX
+
+
\ No newline at end of file
diff --git a/src/app/dev/DevToys.Api/Core/DictionaryExtensions.cs b/src/app/dev/DevToys.Api/Core/DictionaryExtensions.cs
new file mode 100644
index 0000000000..7e22950ae4
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Core/DictionaryExtensions.cs
@@ -0,0 +1,12 @@
+namespace DevToys.Api;
+
+public static class DictionaryExtensions
+{
+ ///
+ /// Gets the value at the given key, or a default value.
+ ///
+ public static TValue? GetValueOrDefault(this IDictionary dictionary, TKey key)
+ {
+ return dictionary.TryGetValue(key, out TValue? value) ? value : default;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Core/ExtensionOrderer.cs b/src/app/dev/DevToys.Api/Core/ExtensionOrderer.cs
new file mode 100644
index 0000000000..1f08cb88df
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Core/ExtensionOrderer.cs
@@ -0,0 +1,123 @@
+namespace DevToys.Api;
+
+public static class ExtensionOrderer
+{
+ public static IEnumerable> Order(
+ IEnumerable> extensions)
+ where TMetadata : IOrderableMetadata
+ {
+ var graph = new Graph(extensions);
+ return graph.TopologicalSort();
+ }
+
+ public static void CheckForCycles(
+ IEnumerable> extensions)
+ where TMetadata : IOrderableMetadata
+ {
+ var graph = new Graph(extensions);
+ graph.CheckForCycles();
+ }
+
+ private sealed class Node where TMetadata : IOrderableMetadata
+ {
+ internal string Name => Extension.Metadata.InternalComponentName;
+
+ internal HashSet> NodesBefore { get; }
+
+ internal Lazy Extension { get; }
+
+ internal Node(Lazy extension)
+ {
+ Extension = extension;
+ NodesBefore = new HashSet>();
+ }
+
+ internal void CheckForCycles()
+ {
+ CheckForCycles(new HashSet>());
+ }
+
+ internal void Visit(List> result, HashSet> seenNodes)
+ {
+ if (!seenNodes.Add(this))
+ {
+ return;
+ }
+
+ foreach (Node before in NodesBefore)
+ {
+ before.Visit(result, seenNodes);
+ }
+
+ result.Add(Extension);
+ }
+
+ private void CheckForCycles(HashSet> seenNodes)
+ {
+ if (!seenNodes.Add(this))
+ {
+ throw new ArgumentException($"Cycle detected in extensions. Extension Name: '{Name}'");
+ }
+
+ foreach (Node before in NodesBefore)
+ {
+ before.CheckForCycles(seenNodes);
+ }
+
+ seenNodes.Remove(this);
+ }
+ }
+
+ private sealed class Graph where TMetadata : IOrderableMetadata
+ {
+ private readonly Dictionary> _nodes = new();
+
+ internal Graph(IEnumerable> extensions)
+ {
+ foreach (Lazy extension in extensions)
+ {
+ var node = new Node(extension);
+ _nodes.Add(node.Name, node);
+ }
+
+ foreach (Node node in _nodes.Values)
+ {
+
+ foreach (string before in node.Extension.Metadata.Before)
+ {
+ Node nodeAfter = _nodes[before];
+ nodeAfter.NodesBefore.Add(node);
+ }
+
+ foreach (string after in node.Extension.Metadata.After)
+ {
+ Node nodeBefore = _nodes[after];
+ node.NodesBefore.Add(nodeBefore);
+ }
+ }
+ }
+
+ internal IList> TopologicalSort()
+ {
+ CheckForCycles();
+
+ var result = new List>();
+ var seenNodes = new HashSet>();
+
+ foreach (Node node in _nodes.Values)
+ {
+ node.Visit(result, seenNodes);
+ }
+
+ return result;
+ }
+
+ internal void CheckForCycles()
+ {
+ foreach (Node node in _nodes.Values)
+ {
+ node.CheckForCycles();
+ }
+ }
+ }
+}
diff --git a/src/app/dev/DevToys.Api/DevToys.Api.csproj b/src/app/dev/DevToys.Api/DevToys.Api.csproj
new file mode 100644
index 0000000000..8449456df1
--- /dev/null
+++ b/src/app/dev/DevToys.Api/DevToys.Api.csproj
@@ -0,0 +1,13 @@
+
+
+ $(NetStandard)
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/dev/DevToys.Api/IMefProvider.cs b/src/app/dev/DevToys.Api/IMefProvider.cs
new file mode 100644
index 0000000000..08eaffb5da
--- /dev/null
+++ b/src/app/dev/DevToys.Api/IMefProvider.cs
@@ -0,0 +1,17 @@
+namespace DevToys.Api;
+
+///
+/// Provides a way to import MEF components on the fly.
+///
+public interface IMefProvider
+{
+ ///
+ /// Imports the given type.
+ ///
+ TExport Import();
+
+ ///
+ /// Imports the given type.
+ ///
+ IEnumerable> ImportMany();
+}
diff --git a/src/app/dev/DevToys.Api/IsExternalInit.cs b/src/app/dev/DevToys.Api/IsExternalInit.cs
new file mode 100644
index 0000000000..4351fad112
--- /dev/null
+++ b/src/app/dev/DevToys.Api/IsExternalInit.cs
@@ -0,0 +1,3 @@
+namespace System.Runtime.CompilerServices;
+
+internal static class IsExternalInit { }
diff --git a/src/app/dev/DevToys.Api/Settings/ISettingsProvider.cs b/src/app/dev/DevToys.Api/Settings/ISettingsProvider.cs
new file mode 100644
index 0000000000..1403ef67b8
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Settings/ISettingsProvider.cs
@@ -0,0 +1,33 @@
+namespace DevToys.Api;
+
+///
+/// Provides a set of methods to manage the application's settings.
+///
+public interface ISettingsProvider
+{
+ ///
+ /// Raised when a setting value has changed.
+ ///
+ event EventHandler? SettingChanged;
+
+ ///
+ /// Gets the value of a defined setting.
+ ///
+ /// The type of value that will be retrieved.
+ /// The that defines the targetted setting.
+ /// Return the value of the setting or its default value.
+ T GetSetting(SettingDefinition settingDefinition);
+
+ ///
+ /// Sets the value of a given setting.
+ ///
+ /// The type of value that will be set.
+ /// The that defines the targetted setting.
+ /// The value to set
+ void SetSetting(SettingDefinition settingDefinition, T value);
+
+ ///
+ /// Resets a given setting to its default value.
+ ///
+ void ResetSetting(SettingDefinition settingDefinition);
+}
diff --git a/src/app/dev/DevToys.Api/Settings/SettingChangedEventArgs.cs b/src/app/dev/DevToys.Api/Settings/SettingChangedEventArgs.cs
new file mode 100644
index 0000000000..086e898741
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Settings/SettingChangedEventArgs.cs
@@ -0,0 +1,14 @@
+namespace DevToys.Api;
+
+public sealed class SettingChangedEventArgs : EventArgs
+{
+ public string SettingName { get; }
+
+ public object? NewValue { get; }
+
+ public SettingChangedEventArgs(string settingName, object? newValue)
+ {
+ SettingName = settingName;
+ NewValue = newValue;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Settings/SettingDefinition.cs b/src/app/dev/DevToys.Api/Settings/SettingDefinition.cs
new file mode 100644
index 0000000000..e1d315e071
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Settings/SettingDefinition.cs
@@ -0,0 +1,79 @@
+namespace DevToys.Api;
+
+///
+/// Represents the definition of a setting in the application.
+///
+/// The type of value of the setting
+public readonly struct SettingDefinition : IEquatable>
+{
+ ///
+ /// Gets whether the setting can be synchronized with the user's Microsoft account.
+ ///
+ public bool IsRoaming { get; }
+
+ ///
+ /// Gets the name of the setting.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Gets the default value of the setting.
+ ///
+ public T DefaultValue { get; }
+
+ ///
+ /// Initializes a new instance of the structure.
+ ///
+ /// The name of the setting. Should be unique.
+ /// Defines whether the setting can be synchronized with the user's Microsoft account.
+ /// The default value of the setting.
+ public SettingDefinition(string name, bool isRoaming, T defaultValue)
+ {
+ if (string.IsNullOrEmpty(name) || name.Length > 255)
+ {
+ // For both LocalSettings and RoamingSettings on Windows, the name of each setting
+ // can be 255 characters in length at most.
+ // see https://docs.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.localsettings?view=winrt-22000#remarks
+ throw new ArgumentOutOfRangeException(nameof(name));
+ }
+
+ IsRoaming = isRoaming;
+ Name = name;
+ DefaultValue = defaultValue;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (obj is SettingDefinition definition)
+ {
+ return Equals(definition);
+ }
+
+ return false;
+ }
+
+ public bool Equals(SettingDefinition other)
+ {
+ return other.IsRoaming == IsRoaming
+ && string.Equals(other.Name, Name, StringComparison.Ordinal)
+ && other.DefaultValue is not null
+ && other.DefaultValue.Equals(DefaultValue);
+ }
+
+ public override int GetHashCode()
+ {
+ return (IsRoaming.GetHashCode() ^ 137)
+ * (Name.GetHashCode() ^ 47)
+ * (DefaultValue is null ? 13 : DefaultValue.GetHashCode() ^ 73);
+ }
+
+ public static bool operator ==(SettingDefinition left, SettingDefinition right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(SettingDefinition left, SettingDefinition right)
+ {
+ return !(left == right);
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/CLI/ICommandLineTool.cs b/src/app/dev/DevToys.Api/Tool/CLI/ICommandLineTool.cs
new file mode 100644
index 0000000000..4b2de7b3bb
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/CLI/ICommandLineTool.cs
@@ -0,0 +1,52 @@
+using Microsoft.Extensions.Logging;
+
+namespace DevToys.Api;
+
+///
+/// Represents the factory for command line tool.
+///
+///
+///
+/// [Export(typeof(ICommandLineTool))]
+/// [Name("Base64 Encode / Decoder")]
+/// [Author("John Doe")]
+/// [CommandName(
+/// Name = "base64",
+/// Alias = "b64",
+/// DescriptionResourceName = nameof(Strings.Base64Description),
+/// ResourceManagerBaseName = "MyProject.Strings")]
+/// [TargetPlatform(Platform.Windows)] // Optional
+/// [TargetPlatform(Platform.MacOS)] // Optional
+/// internal sealed class Base64CommandLineTool : ICommandLineTool
+/// {
+/// [CommandLineOption(Name = "file", Alias = "f", IsRequired = true, DescriptionResourceName = nameof(Strings.Base64FileOptionDescription))]
+/// internal FileInfo? File { get; set; }
+///
+/// [CommandLineOption(Name = "utf8", DescriptionResourceName = nameof(Strings.Utf8OptionDescription))]
+/// internal bool Utf8 { get; set; } = true; // Default value is true.
+///
+/// public ValueTask InvokeAsync(CancellationToken cancellationToken)
+/// {
+/// // [...]
+/// return 0; // Exit code.
+/// }
+/// }
+///
+///
+public interface ICommandLineTool
+{
+ ///
+ /// Invoked when the user ran the app using the command and options defined by the current .
+ ///
+ /// A logger, for reporting relevant telemetry information about health and performance of the tool.
+ /// Gets canceled when the user wants to quit the app.
+ /// An Exit Code.
+ ///
+ /// Using :
+ /// - DO report errors.
+ /// - DO report information about performance of some tasks, if relevant.
+ /// - DO report some system information, but only if it can truly be helpful when investigating performance or compatiblity issues.
+ /// - DO NOT report what the user input in the app as it might contains user personal information.
+ ///
+ ValueTask InvokeAsync(ILogger logger, CancellationToken cancellationToken);
+}
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/IGuiTool.cs b/src/app/dev/DevToys.Api/Tool/GUI/IGuiTool.cs
new file mode 100644
index 0000000000..8c9172f39b
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/GUI/IGuiTool.cs
@@ -0,0 +1,40 @@
+namespace DevToys.Api;
+
+///
+/// Represents the factory for tool with a GUI.
+///
+///
+///
+/// [Export(typeof(IGuiTool))]
+/// [Name("Base64 Encode / Decoder")]
+/// [Author("John Doe")]
+/// [ToolDisplayInformation(
+/// IconFontName = "Fluent System-Regular",
+/// IconGlyph = "\u0108",
+/// ResourceManagerBaseName = "MyProject.Strings",
+/// MenuDisplayTitleResourceName = nameof(Strings.MenuDisplayTitle),
+/// SearchDisplayTitleResourceName = nameof(Strings.SearchDisplayTitle),
+/// DescriptionResourceName = nameof(Strings.Description),
+/// AccessibleNameResourceName = nameof(Strings.AccessibleName),
+/// SearchKeywordsResourceName = nameof(Strings.SearchKeywords))]
+/// [TargetPlatform(Platform.Windows)] // Optional
+/// [TargetPlatform(Platform.WASM)] // Optional
+/// [Parent("Encoders / Decoders")] // Optional
+/// [Order(Before = "Base64 Image Decoder")] // Optional
+/// [NonSearchable] // Optional
+/// [NonFavorable] // Optional
+/// [NoCompactOverlaySupport] // Optional
+/// [MenuPlacement(MenuPlacement.Footer)] // Optional
+/// [CompactOverlaySize(height: 200, width: 250)] // Optional
+/// internal sealed class Base64GuiTool : IGuiTool
+/// {
+/// }
+///
+///
+public interface IGuiTool
+{
+ ///
+ /// Gets the view for the tool.
+ ///
+ UIElement View { get; }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/Models/UIBase.cs b/src/app/dev/DevToys.Api/Tool/GUI/Models/UIBase.cs
new file mode 100644
index 0000000000..59349774b1
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/GUI/Models/UIBase.cs
@@ -0,0 +1,21 @@
+namespace DevToys.Api;
+
+///
+/// The base for all UI components.
+///
+public abstract class UIBase
+{
+ ///
+ /// Creates a new instance of .
+ ///
+ ///
+ protected UIBase(string id)
+ {
+ Id = id;
+ }
+
+ ///
+ /// An identifier for this component.
+ ///
+ public string Id { get; }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/Models/UIElement.cs b/src/app/dev/DevToys.Api/Tool/GUI/Models/UIElement.cs
new file mode 100644
index 0000000000..c397e4c5b7
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/GUI/Models/UIElement.cs
@@ -0,0 +1,55 @@
+namespace DevToys.Api;
+
+///
+/// A base class for all UI elements.
+///
+public abstract class UIElement : UIBase
+{
+ private bool _isVisible;
+ private bool _isEnabled;
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ ///
+ protected UIElement(string id)
+ : base(id)
+ {
+ }
+
+ ///
+ /// Gets or sets whether this element should be visible or hidden in the UI.
+ ///
+ public bool IsVisible
+ {
+ get => _isVisible;
+ set
+ {
+ _isVisible = value;
+ IsVisibleChanged?.Invoke(this, _isVisible);
+ }
+ }
+
+ ///
+ /// Gets or sets whether this element and its children should be enabled or disabled.
+ ///
+ public bool IsEnabled
+ {
+ get => _isEnabled;
+ set
+ {
+ _isEnabled = value;
+ IsEnabledChanged?.Invoke(this, _isEnabled);
+ }
+ }
+
+ ///
+ /// Raised when is changed.
+ ///
+ public event EventHandler? IsVisibleChanged;
+
+ ///
+ /// Raised when is changed.
+ ///
+ public event EventHandler? IsEnabledChanged;
+}
diff --git a/src/app/dev/DevToys.Api/Tool/GUI/Models/UITitledElement.cs b/src/app/dev/DevToys.Api/Tool/GUI/Models/UITitledElement.cs
new file mode 100644
index 0000000000..ab31ee6647
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/GUI/Models/UITitledElement.cs
@@ -0,0 +1,36 @@
+namespace DevToys.Api;
+
+///
+/// A base class for all UI elements that can have a title / header on top of the element.
+///
+public abstract class UITitledElement : UIElement
+{
+ private string? _title;
+
+ ///
+ /// Creates a new instance of a .
+ ///
+ ///
+ protected UITitledElement(string id)
+ : base(id)
+ {
+ }
+
+ ///
+ /// Gets or sets a title to display for this element.
+ ///
+ public string? Title
+ {
+ get => _title;
+ set
+ {
+ _title = value;
+ TitleChanged?.Invoke(this, _title);
+ }
+ }
+
+ ///
+ /// Raised when is changed.
+ ///
+ public event EventHandler? TitleChanged;
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/AuthorAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/AuthorAttribute.cs
new file mode 100644
index 0000000000..cdcd500d49
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/AuthorAttribute.cs
@@ -0,0 +1,17 @@
+namespace DevToys.Api;
+
+///
+/// Defines the internal name of this component. This name can be used to explicitly request this component to be invoked.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class AuthorAttribute : Attribute
+{
+ public string Author { get; }
+
+ public AuthorAttribute(string author)
+ {
+ Guard.IsNotEmpty(author);
+ Author = author;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CommandLineOptionAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CommandLineOptionAttribute.cs
new file mode 100644
index 0000000000..37d3183537
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CommandLineOptionAttribute.cs
@@ -0,0 +1,34 @@
+namespace DevToys.Api;
+
+///
+/// Defines an option for a .
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
+public sealed class CommandLineOptionAttribute : CommandNameAttribute
+{
+ ///
+ /// Gets the name of the option. Example, "file".
+ /// Implicitly, an option named "file" will be usable in a command line through "--file ".
+ ///
+ public new string Name
+ {
+ get => base.Name;
+ set => base.Name = value;
+ }
+
+ ///
+ /// Gets or sets the alias name of the option. Example, "f".
+ /// Implicitly, an option named "f" will be usable in a command line through "-f ".
+ ///
+ public new string Alias
+ {
+ get => base.Alias;
+ set => base.Alias = value;
+ }
+
+ ///
+ /// Gets or sets whether the option is required.
+ ///
+ public bool IsRequired { get; set; }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CommandNameAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CommandNameAttribute.cs
new file mode 100644
index 0000000000..be10438030
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CommandNameAttribute.cs
@@ -0,0 +1,66 @@
+namespace DevToys.Api;
+
+///
+/// Defines the name of the CLI command of a .
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public class CommandNameAttribute : Attribute
+{
+ private string _name = string.Empty;
+ private string _alias = string.Empty;
+ private string _descriptionResourceName = string.Empty;
+ private string _resourceManagerBaseName = string.Empty;
+
+ ///
+ /// Gets the name of the command. Example, "file".
+ ///
+ public string Name
+ {
+ get => _name;
+ set
+ {
+ Guard.IsNotNullOrEmpty(value);
+ _name = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the alias name of the option. Example, "f".
+ ///
+ public string Alias
+ {
+ get => _alias;
+ set
+ {
+ Guard.IsNotNullOrEmpty(value);
+ _alias = value;
+ }
+ }
+
+ ///
+ /// Gets or sets name of the localized resource that provides a description.
+ ///
+ public string DescriptionResourceName
+ {
+ get => _descriptionResourceName;
+ set
+ {
+ Guard.IsNotNullOrEmpty(value);
+ _descriptionResourceName = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the name of the resource manager's base name to use when looking for .
+ ///
+ public string ResourceManagerBaseName
+ {
+ get => _resourceManagerBaseName;
+ set
+ {
+ Guard.IsNotNullOrEmpty(value);
+ _resourceManagerBaseName = value;
+ }
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CompactOverlaySizeAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CompactOverlaySizeAttribute.cs
new file mode 100644
index 0000000000..1e4a4a3d1d
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/CompactOverlaySizeAttribute.cs
@@ -0,0 +1,19 @@
+namespace DevToys.Api.Tool.Metadata.Attributes;
+
+///
+/// Indicates the size that the window should take in Compact Overlay mode when this is active.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class CompactOverlaySizeAttribute : Attribute
+{
+ public int? CompactOverlayHeight { get; set; }
+
+ public int? CompactOverlayWidth { get; set; }
+
+ public CompactOverlaySizeAttribute(int height, int width)
+ {
+ CompactOverlayHeight = height;
+ CompactOverlayWidth = width;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/DataTypeNameAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/DataTypeNameAttribute.cs
new file mode 100644
index 0000000000..ce44e271f9
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/DataTypeNameAttribute.cs
@@ -0,0 +1,20 @@
+namespace DevToys.Api;
+
+///
+/// Defines a data type name attached to a .
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class DataTypeNameAttribute : Attribute
+{
+ public string DataTypeName { get; }
+
+ public string? DataTypeBaseName { get; }
+
+ public DataTypeNameAttribute(string name, string? baseName = null)
+ {
+ Guard.IsNotEmpty(name);
+ DataTypeName = name;
+ DataTypeBaseName = baseName;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/MenuPlacement.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/MenuPlacement.cs
new file mode 100644
index 0000000000..0825917416
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/MenuPlacement.cs
@@ -0,0 +1,8 @@
+namespace DevToys.Api;
+
+public enum MenuPlacement
+{
+ Body,
+ Header,
+ Footer
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/MenuPlacementAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/MenuPlacementAttribute.cs
new file mode 100644
index 0000000000..a11b269464
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/MenuPlacementAttribute.cs
@@ -0,0 +1,16 @@
+namespace DevToys.Api;
+
+///
+/// Indicates where the should be displayed in the navigation view.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class MenuPlacementAttribute : Attribute
+{
+ public MenuPlacement MenuPlacement { get; }
+
+ public MenuPlacementAttribute(MenuPlacement menuPlacement)
+ {
+ MenuPlacement = menuPlacement;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NameAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NameAttribute.cs
new file mode 100644
index 0000000000..ee2c97c1f7
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NameAttribute.cs
@@ -0,0 +1,17 @@
+namespace DevToys.Api;
+
+///
+/// Defines the internal name of this component. This name can be used to explicitly request this component to be invoked.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class NameAttribute : Attribute
+{
+ public string InternalComponentName { get; }
+
+ public NameAttribute(string internalComponentName)
+ {
+ Guard.IsNotEmpty(internalComponentName);
+ InternalComponentName = internalComponentName;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NoCompactOverlaySupportAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NoCompactOverlaySupportAttribute.cs
new file mode 100644
index 0000000000..e8514eccd9
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NoCompactOverlaySupportAttribute.cs
@@ -0,0 +1,11 @@
+namespace DevToys.Api;
+
+///
+/// Indicates that the does not support Compact Overlay mode.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class NoCompactOverlaySupportAttribute : Attribute
+{
+ public bool NoCompactOverlaySupport { get; } = true;
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NotFavorableAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NotFavorableAttribute.cs
new file mode 100644
index 0000000000..8e5910e03a
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NotFavorableAttribute.cs
@@ -0,0 +1,11 @@
+namespace DevToys.Api;
+
+///
+/// Indicates that the can not be added to the favorites.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class NotFavorableAttribute : Attribute
+{
+ public bool NotFavorable { get; } = true;
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NotSearchableAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NotSearchableAttribute.cs
new file mode 100644
index 0000000000..9371dea954
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/NotSearchableAttribute.cs
@@ -0,0 +1,11 @@
+namespace DevToys.Api;
+
+///
+/// Indicates that the can not be searched.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class NotSearchableAttribute : Attribute
+{
+ public bool NotSearchable { get; } = true;
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/OrderAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/OrderAttribute.cs
new file mode 100644
index 0000000000..db8ea296f8
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/OrderAttribute.cs
@@ -0,0 +1,40 @@
+namespace DevToys.Api;
+
+///
+/// Defines the priority of this component over others.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public sealed class OrderAttribute : Attribute
+{
+ private string _before = string.Empty;
+ private string _after = string.Empty;
+
+ ///
+ /// Gets or sets the internal name of a component to compare with.
+ /// The value should corresponds to an existing value.
+ ///
+ public string Before
+ {
+ get => _before;
+ set
+ {
+ Guard.IsNotNullOrEmpty(value);
+ _before = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the internal name of a component to compare with.
+ /// The value should corresponds to an existing value.
+ ///
+ public string After
+ {
+ get => _after;
+ set
+ {
+ Guard.IsNotNullOrEmpty(value);
+ _after = value;
+ }
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/ParentAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/ParentAttribute.cs
new file mode 100644
index 0000000000..81aa5e9690
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/ParentAttribute.cs
@@ -0,0 +1,17 @@
+namespace DevToys.Api;
+
+///
+/// Indicates the parent tool of the current one.
+/// The name should corresponds to an existing value, or null if no parent.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class ParentAttribute : Attribute
+{
+ public string Parent { get; set; }
+
+ public ParentAttribute(string? name)
+ {
+ Parent = name ?? string.Empty;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/Platform.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/Platform.cs
new file mode 100644
index 0000000000..42086a4e7e
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/Platform.cs
@@ -0,0 +1,9 @@
+namespace DevToys.Api;
+
+public enum Platform
+{
+ Windows,
+ MacOS,
+ Linux,
+ WASM
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/TargetPlatformAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/TargetPlatformAttribute.cs
new file mode 100644
index 0000000000..c6d60ccbbc
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/TargetPlatformAttribute.cs
@@ -0,0 +1,16 @@
+namespace DevToys.Api;
+
+///
+/// Defines the targeted platform for this component.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public sealed class TargetPlatformAttribute : Attribute
+{
+ public Platform TargetPlatform { get; }
+
+ public TargetPlatformAttribute(Platform platform)
+ {
+ TargetPlatform = platform;
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/ToolDisplayInformationAttribute.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/ToolDisplayInformationAttribute.cs
new file mode 100644
index 0000000000..c0426aa4db
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Attributes/ToolDisplayInformationAttribute.cs
@@ -0,0 +1,128 @@
+namespace DevToys.Api;
+
+///
+/// Defines the resources to get the information about the to be displayed in the UI.
+///
+[MetadataAttribute]
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+public sealed class ToolDisplayInformationAttribute : Attribute
+{
+ private string _resourceManagerBaseName = string.Empty;
+ private string _menuDisplayTitleResourceName = string.Empty;
+ private string _searchDisplayTitleResourceName = string.Empty;
+ private string _descriptionResourceName = string.Empty;
+ private string _accessibleNameResourceName = string.Empty;
+ private string _searchkeywordsResourceName = string.Empty;
+ private string _iconGlyph = string.Empty;
+
+ ///
+ /// Gets or sets the name of the resource manager's base name to use when looking for resource string
+ /// for , ,
+ /// , and .
+ ///
+ public string ResourceManagerBaseName
+ {
+ get => _resourceManagerBaseName;
+ set
+ {
+ Guard.IsNotNullOrEmpty(value);
+ _resourceManagerBaseName = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the title of the tool in the main menu of the app, for example "JSON".
+ ///
+ public string MenuDisplayTitleResourceName
+ {
+ get => _menuDisplayTitleResourceName;
+ set
+ {
+ Guard.IsNotNullOrWhiteSpace(value);
+ _menuDisplayTitleResourceName = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the title of the tool that will be displayed in the search bar. Sometimes
+ /// it is needed to have a different one than the name showed in the menu to increase
+ /// result accuracy. For example, while could be "JSON"
+ /// for a tool that is under the Formatter category,
+ /// could be "JSON Formatter", which can be helpful to differentiate from other similar
+ /// tools like "JSON Converter".
+ ///
+ public string SearchDisplayTitleResourceName
+ {
+ get => _searchDisplayTitleResourceName;
+ set
+ {
+ Guard.IsNotNullOrWhiteSpace(value);
+ _searchDisplayTitleResourceName = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the description of the tool.
+ ///
+ public string DescriptionResourceName
+ {
+ get => _descriptionResourceName;
+ set
+ {
+ Guard.IsNotNullOrWhiteSpace(value);
+ _descriptionResourceName = value;
+ }
+ }
+
+ ///
+ /// (optional) Gets or sets the name of the tool that will be told to the user when using screen reader.
+ ///
+ public string AccessibleNameResourceName
+ {
+ get => _accessibleNameResourceName;
+ set
+ {
+ Guard.IsNotNullOrWhiteSpace(value);
+ _accessibleNameResourceName = value;
+ }
+ }
+
+ ///
+ /// (optional) Gets or sets the keywords of the tool that are searched in the localized environment.
+ ///
+ public string SearchKeywordsResourceName
+ {
+ get => _searchkeywordsResourceName;
+ set
+ {
+ Guard.IsNotNullOrWhiteSpace(value);
+ _searchkeywordsResourceName = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a glyph for the icon of the tool.
+ ///
+ public string IconGlyph
+ {
+ get => _iconGlyph;
+ set
+ {
+ Guard.IsNotNullOrWhiteSpace(value);
+ _iconGlyph = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the name of the font to use to display the .
+ ///
+ public string IconFontName
+ {
+ get => _iconGlyph;
+ set
+ {
+ Guard.IsNotNullOrWhiteSpace(value);
+ _iconGlyph = value;
+ }
+ }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/Metadata/Metadata/IOrderableMetadata.cs b/src/app/dev/DevToys.Api/Tool/Metadata/Metadata/IOrderableMetadata.cs
new file mode 100644
index 0000000000..6033a4bab5
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/Metadata/Metadata/IOrderableMetadata.cs
@@ -0,0 +1,10 @@
+namespace DevToys.Api;
+
+public interface IOrderableMetadata
+{
+ IReadOnlyList Before { get; }
+
+ IReadOnlyList After { get; }
+
+ string InternalComponentName { get; }
+}
diff --git a/src/app/dev/DevToys.Api/Tool/SmartDetection/DataDetectionResult.cs b/src/app/dev/DevToys.Api/Tool/SmartDetection/DataDetectionResult.cs
new file mode 100644
index 0000000000..869a145236
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/SmartDetection/DataDetectionResult.cs
@@ -0,0 +1,6 @@
+namespace DevToys.Api;
+
+public record DataDetectionResult(bool Succeess, object? Data)
+{
+ public readonly DataDetectionResult Unsuccessful = new(false, null);
+}
diff --git a/src/app/dev/DevToys.Api/Tool/SmartDetection/IDataTypeDetector.cs b/src/app/dev/DevToys.Api/Tool/SmartDetection/IDataTypeDetector.cs
new file mode 100644
index 0000000000..21e879db53
--- /dev/null
+++ b/src/app/dev/DevToys.Api/Tool/SmartDetection/IDataTypeDetector.cs
@@ -0,0 +1,29 @@
+namespace DevToys.Api;
+
+///
+/// Represents a way to detect the type of data coming from an external source such as the OS's clipboard.
+///
+///
+///
+/// [Export(typeof(IDataTypeDetector))]
+/// [DataTypeName("jwt-header", baseName: "json")] // jwt-header type inheriting from json type.
+/// [TargetPlatform(Platform.Windows)] // Optional
+/// [TargetPlatform(Platform.WASM)] // Optional
+/// internal sealed class JwtDetector : IDataTypeDetector
+/// {
+/// }
+///
+///
+public interface IDataTypeDetector
+{
+ ///
+ /// Tries to detect whether the given match the expected format known by this
+ /// , often by trying to reading and/or parsing it.
+ /// When the data successfully got parsed, output that parsed value to .
+ ///
+ /// The data to analyze, often coming from the OS's clipboard.
+ /// Returns a that indicates whether the data could be analyzed / parsed / read
+ /// correctly, along with the parsed data, if any change has been made to it during parsing (for example, string to
+ /// integer conversion).
+ ValueTask TryDetectDataAsync(object data);
+}
diff --git a/src/app/dev/DevToys.Core/DevToys.Core.csproj b/src/app/dev/DevToys.Core/DevToys.Core.csproj
new file mode 100644
index 0000000000..510c71463b
--- /dev/null
+++ b/src/app/dev/DevToys.Core/DevToys.Core.csproj
@@ -0,0 +1,14 @@
+
+
+ $(NetStandard)
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/dev/DevToys.Core/Mef/MefComposer.cs b/src/app/dev/DevToys.Core/Mef/MefComposer.cs
new file mode 100644
index 0000000000..01c57bfd9d
--- /dev/null
+++ b/src/app/dev/DevToys.Core/Mef/MefComposer.cs
@@ -0,0 +1,112 @@
+using System.ComponentModel.Composition.Hosting;
+using System.Reflection;
+using DevToys.Api;
+
+namespace DevToys.Core.Mef;
+
+///
+/// Provides a set of methods to initialize and manage MEF.
+///
+public sealed class MefComposer : IDisposable
+{
+ private readonly Assembly[] _assemblies;
+ private readonly object[] _customExports;
+ private bool _isExportProviderDisposed = true;
+
+ public IMefProvider Provider { get; }
+
+ public ExportProvider ExportProvider { get; private set; }
+
+ public MefComposer(Assembly[]? assemblies = null, params object[] customExports)
+ {
+ if (Provider is not null)
+ {
+ throw new InvalidOperationException("Mef composer already initialized.");
+ }
+
+ _assemblies = assemblies ?? Array.Empty();
+ _customExports = customExports ?? Array.Empty