diff --git a/.config/configuration.vsEnterprise.winget b/.config/configuration.vsEnterprise.winget index 84e05ed511..4d19d37137 100644 --- a/.config/configuration.vsEnterprise.winget +++ b/.config/configuration.vsEnterprise.winget @@ -6,12 +6,16 @@ properties: directives: description: Enable Developer Mode allowPrerelease: true + # Requires elevation for the set operation + securityContext: elevated settings: Ensure: Present - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: description: Install Visual Studio 2022 Enterprise (Any edition will work) + # Requires elevation for the set operation + securityContext: elevated settings: id: Microsoft.VisualStudio.2022.Enterprise source: winget @@ -21,6 +25,8 @@ properties: directives: description: Install required VS workloads allowPrerelease: true + # Requires elevation for the get and set operations + securityContext: elevated settings: productId: Microsoft.VisualStudio.Product.Enterprise channelId: VisualStudio.17.Release diff --git a/.config/configuration.vsProfessional.winget b/.config/configuration.vsProfessional.winget index 6ac0babf9f..78fb1c13d5 100644 --- a/.config/configuration.vsProfessional.winget +++ b/.config/configuration.vsProfessional.winget @@ -6,12 +6,16 @@ properties: directives: description: Enable Developer Mode allowPrerelease: true + # Requires elevation for the set operation + securityContext: elevated settings: Ensure: Present - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: description: Install Visual Studio 2022 Professional (Any edition will work) + # Requires elevation for the set operation + securityContext: elevated settings: id: Microsoft.VisualStudio.2022.Professional source: winget @@ -21,6 +25,8 @@ properties: directives: description: Install required VS workloads allowPrerelease: true + # Requires elevation for the get and set operations + securityContext: elevated settings: productId: Microsoft.VisualStudio.Product.Professional channelId: VisualStudio.17.Release diff --git a/.config/configuration.winget b/.config/configuration.winget index df3eeea441..456eca47f2 100644 --- a/.config/configuration.winget +++ b/.config/configuration.winget @@ -6,12 +6,16 @@ properties: directives: description: Enable Developer Mode allowPrerelease: true + # Requires elevation for the set operation + securityContext: elevated settings: Ensure: Present - resource: Microsoft.WinGet.DSC/WinGetPackage id: vsPackage directives: description: Install Visual Studio 2022 Community (Any edition will work) + # Requires elevation for the set operation + securityContext: elevated settings: id: Microsoft.VisualStudio.2022.Community source: winget @@ -21,6 +25,8 @@ properties: directives: description: Install required VS workloads allowPrerelease: true + # Requires elevation for the get and set operations + securityContext: elevated settings: productId: Microsoft.VisualStudio.Product.Community channelId: VisualStudio.17.Release diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 08bbc04c55..e55d081c91 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,6 +1,5 @@ name: "🕷️ Bug report" description: Report errors or unexpected behavior -type: Bug labels: - Issue-Bug - Needs-Triage @@ -8,7 +7,8 @@ body: - type: markdown attributes: value: Please make sure to [search for existing issues](https://github.com/microsoft/PowerToys/issues) before filing a new one! -- type: input +- id: version + type: input attributes: label: Microsoft PowerToys version placeholder: X.XX.X @@ -16,7 +16,8 @@ body: validations: required: true -- type: dropdown +- id: installed + type: dropdown attributes: label: Installation method description: How / Where was PowerToys installed from? @@ -33,14 +34,6 @@ body: validations: required: true -- type: dropdown - attributes: - label: Running as admin - description: Are you running PowerToys as Admin? - options: - - "Yes" - - "No" - - type: dropdown attributes: label: Area(s) with issue? @@ -67,7 +60,7 @@ body: - Keyboard Manager - Mouse Utilities - Mouse Without Borders - - New+ + - New+ - Peek - PowerRename - PowerToys Run @@ -106,6 +99,19 @@ body: validations: required: false +- id: additionalInfo + type: textarea + attributes: + label: Additional Information + placeholder: | + OS version + .Net version + System Language + User or System Installation + Running as admin + validations: + required: false + - type: textarea attributes: label: Other Software @@ -116,3 +122,4 @@ body: My Cool Application v0.3 (include a code snippet if it would help!) validations: required: false + diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt index 619a036b32..10e9473258 100644 --- a/.github/actions/spell-check/allow/code.txt +++ b/.github/actions/spell-check/allow/code.txt @@ -273,4 +273,4 @@ mengyuanchen testhost #Tools -OIP +OIP \ No newline at end of file diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 44131fecfb..eeda5b9ae1 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -188,6 +188,8 @@ zhaoqpcn Zoltan Zykova Sameerjs +ruslanlap +vednig # OTHERS diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index b688d6bae0..43447793a0 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -8,6 +8,7 @@ Acceleratorkeys ACCEPTFILES ACCESSDENIED ACCESSTOKEN +acfs AClient AColumn acrt @@ -56,12 +57,10 @@ APIIs Apm APPBARDATA APPEXECLINK -APPICONREFERENCE APPLICATIONFRAMEHOST appmanifest APPMODEL APPNAME -APPPUBLISHER appref appsettings appwindow @@ -173,9 +172,9 @@ CCHFORMNAME CCom CContext CDeclaration -cdn CElems CENTERALIGN +cer certlm certmgr cfp @@ -199,6 +198,7 @@ CLIPBOARDUPDATE CLIPCHILDREN CLIPSIBLINGS closesocket +clp CLSCTX clsids Clusion @@ -254,8 +254,6 @@ createdump CREATEPROCESS CREATESCHEDULEDTASK CREATESTRUCT -CREATETHREAD -CREATEWINDOW CREATEWINDOWFAILED CRECT CRH @@ -277,6 +275,7 @@ CURSORINFO cursorpos customaction CUSTOMACTIONTEST +CUSTOMFORMATPLACEHOLDER CVal cvd CVirtual @@ -308,11 +307,7 @@ DCOM DComposition DCR ddd -DDEAPPLICATION -DDECOMMAND DDEIf -DDEIFEXEC -DDETOPIC DDevice DDxgi Deact @@ -326,16 +321,13 @@ DEFAULTFLAGS DEFAULTICON defaultlib DEFAULTONLY -DEFAULTTOFOLDER DEFAULTTONEAREST DEFAULTTONULL DEFAULTTOPRIMARY -DEFAULTTOSTAR DEFERERASE DEFPUSHBUTTON deinitialization DELA -DELEGATEEXECUTE DELETEDKEYIMAGE DELETESCANS deletethis @@ -355,6 +347,7 @@ DEVMODE DEVMODEW devpal DIALOGEX +digicert dimm DISABLEASACTIONKEY DISABLENOSCROLL @@ -389,7 +382,6 @@ dreamsofameaningfullife drivedetectionwarning Droid DROPFILES -DROPTARGET DSTINVERT DSurface DTexture @@ -510,12 +502,10 @@ Fira FIXEDFILEINFO FIXEDSYS flac -flaticon flyouts FMask fmtid FOF -WANTNUKEWARNING FOFX FOLDERID folderpath @@ -525,17 +515,18 @@ FORCEMINIMIZE FORMATDLGORD formatetc FORPARSING -fpvm Fqc FRAMECHANGED frm Froml FROMTOUCH +fsanitize fsmgmt FZE gacutil Gaeilge Gaidhlig +gameid GC'ed GCLP gdi @@ -559,7 +550,6 @@ GETSECKEY GETSTICKYKEYS GETTEXTLENGTH GHND -gifv GMEM GNumber gpedit @@ -643,6 +633,7 @@ HOTKEYF hotkeys hotlight hotspot +Hostx HPAINTBUFFER HRAWINPUT HREDRAW @@ -653,7 +644,6 @@ HROW hsb HSCROLL hsi -HSSH HTCLIENT hthumbnail HTOUCHINPUT @@ -691,12 +681,12 @@ iextn IFACEMETHOD IFACEMETHODIMP IFile -IGNOREBASECLASS IGNOREUNKNOWN IGo iid Iindex Ijwhost +ILD IMAGEHLP IMAGERESIZERCONTEXTMENU IMAGERESIZEREXT @@ -727,6 +717,7 @@ INPUTSINK INPUTTYPE INSTALLDESKTOPSHORTCUT INSTALLDIR +installdir INSTALLFOLDER INSTALLFOLDERTOBOOTSTRAPPERINSTALLFOLDER INSTALLFOLDERTOPREVIOUSINSTALLFOLDER @@ -740,8 +731,6 @@ Inste Interlop INTRESOURCE INVALIDARG -INVALIDCALL -INVALIDINDEX invalidoperatioexception ipcmanager IPREVIEW @@ -803,7 +792,6 @@ LEVELID LExit lhwnd LIBID -libraryincludes LIMITSIZE LIMITTEXT lindex @@ -905,7 +893,6 @@ MBM MBR MDICHILD MDL -mdpvm mdtext mdtxt mdwn @@ -961,6 +948,7 @@ mpmc MRM MRT mru +MSAL msc mscorlib msctls @@ -1024,6 +1012,7 @@ netsh newcolor NEWDIALOGSTYLE NEWFILE +NEWFILEHEADER newitem newpath newplus @@ -1053,11 +1042,11 @@ NOCRLF nodeca NODRAWCAPTION NODRAWICON -NOFIXUPS NOINHERITLAYOUT NOINTERFACE NOINVERT NOLINKINFO +nologo NOMCX NOMINMAX NOMIRRORBITMAP @@ -1069,13 +1058,11 @@ NONELEVATED NONINFRINGEMENT nonspace nonstd -NOOPEN NOOWNERZORDER NOPARENTNOTIFY NOPREFIX NOREDIRECTIONBITMAP NOREDRAW -NOREMAPCLSID NOREMOVE norename NOREPEAT @@ -1094,11 +1081,10 @@ NOTIFYICONDATAW NOTIMPL NOTOPMOST NOTRACK -NOTRUNCATE NOTSRCCOPY NOTSRCERASE NOTXORPEN -NOUSERSETTINGS +notwindows NOZORDER NPH npmjs @@ -1224,6 +1210,7 @@ Podcasts POINTERID POINTERUPDATE Pokedex +Pomodoro Popups POPUPWINDOW POSITIONITEM @@ -1292,6 +1279,7 @@ pstm PStr pstream pstrm +pswd PSYSTEM psz ptb @@ -1318,11 +1306,11 @@ Quarternary QUERYENDSESSION QUERYOPEN QUEUESYNC -QUICKTIP QUNS QXZ RAII RAlt +Rappl randi Rasterization Rasterize @@ -1347,7 +1335,6 @@ REFCLSID REFIID REGCLS regfile -REGISTERCLASSEX REGISTERCLASSFAILED REGISTRYHEADER registrypath @@ -1359,7 +1346,6 @@ REINSTALLMODE reloadable Relogger remappings -REMAPRUNDLL REMAPSUCCESSFUL REMAPUNSUCCESSFUL Remotable @@ -1421,6 +1407,7 @@ sacl safeprojectname SAMEKEYPREVIOUSLYMAPPED SAMESHORTCUTPREVIOUSLYMAPPED +sancov SAVEFAILED scanled schedtasks @@ -1436,10 +1423,10 @@ SDDL SDKDDK sdns searchterm -searchtext SEARCHUI SECONDARYDISPLAY secpol +securestring SEEMASKINVOKEIDLIST SELCHANGE SENDCHANGE @@ -1485,6 +1472,7 @@ shellex SHELLEXECUTEINFO SHELLEXECUTEINFOW SHELLEXTENSION +SHELLICONSIZE SHELLNEWVALUE SHFILEINFO SHFILEOPSTRUCT @@ -1493,9 +1481,9 @@ SHGDNF SHGFI SHGFIICON SHGFILARGEICON +SHIL shinfo shlwapi -SHNAMEMAPPING shobjidl SHORTCUTATLEAST SHORTCUTMAXONEACTIONKEY @@ -1525,6 +1513,7 @@ SIDs siex sigdn SIGNINGSCENARIO +signtool Signtool SINGLEKEY sipolicy @@ -1546,7 +1535,6 @@ SNAPPROCESS snwprintf softline SOURCECLIENTAREAONLY -sourced sourcedoc SOURCEHEADER sourcesdirectory @@ -1592,6 +1580,7 @@ stdcpp stdcpplatest STDMETHODCALLTYPE STDMETHODIMP +steamapps STGC STGM STGMEDIUM @@ -1673,6 +1662,7 @@ telephon templatenamespace testprocess TEXCOORD +TEXTBOXNEWLINE TEXTEXTRACTOR TEXTINCLUDE tfopen @@ -1685,7 +1675,6 @@ THotkey throughs TIcon TILEDWINDOW -TILEINFO TILLSON timedate timediff @@ -1699,9 +1688,6 @@ TLayout tlb tlbimp tlc -TPMLEFTALIGN -TPMRETURNCMD -TMPVAR TNP Toolhelp toolkitconverters @@ -1709,6 +1695,8 @@ toolwindow TOPDOWNDIB TOUCHEVENTF TOUCHINPUT +TPMLEFTALIGN +TPMRETURNCMD TRACEHANDLE tracelogging tracerpt @@ -1782,7 +1770,6 @@ USRDLL UType uuidv uwp -ums uxt uxtheme vabdq @@ -1809,7 +1796,6 @@ VERTSIZE VFT vget vgetq -videourl viewmodel VIRTKEY VIRTUALDESK @@ -1843,7 +1829,7 @@ vstprintf VSTT vswhere Vtbl -WANTMAPPINGHANDLE +WANTNUKEWARNING WANTPALM wasdk wbem @@ -1958,7 +1944,7 @@ WUX Wwanpp XAxis xclip -xdoc +xcopy XDocument XElement xfd @@ -1981,13 +1967,13 @@ Yeet YIncrement yinle yinyue -youtube YPels YResolution YStr YTM YVIRTUALSCREEN ZEROINIT +Zhiwei zonable zoneset Zoneszonabletester @@ -1997,3 +1983,11 @@ ZOOMITX ZXk ZXNs zzz +ACIE +AOklab +BCIE +BOklab +culori +Evercoder +LCh +CIELCh diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..0db3dc6595 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,26 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# As recommended by Microsoft's security guidelines (https://docs.opensource.microsoft.com/security/tsg/actions/#requirements-for-security-hardening-your-own-github-actions), +# 3rd-party actions should be pinned to a specific commit hash to prevent supply chain attacks. +# This update aligns with best practices; 1st/2nd-party actions is not required hash pinning. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v4 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v4 \ No newline at end of file diff --git a/.github/workflows/spelling2.yml b/.github/workflows/spelling2.yml index e79708399f..03bb66fff6 100644 --- a/.github/workflows/spelling2.yml +++ b/.github/workflows/spelling2.yml @@ -93,7 +93,7 @@ jobs: steps: - name: check-spelling id: spelling - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 with: config: .github/actions/spell-check suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} @@ -156,7 +156,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' steps: - name: comment - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 with: config: .github/actions/spell-check checkout: true @@ -175,7 +175,7 @@ jobs: if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') steps: - name: comment - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 with: config: .github/actions/spell-check checkout: true @@ -202,7 +202,7 @@ jobs: cancel-in-progress: false steps: - name: apply spelling updates - uses: check-spelling/check-spelling@v0.0.24 + uses: check-spelling/check-spelling@67debf50669c7fc76fc8f5d7f996384535a72b77 # v0.0.24 with: experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} checkout: true diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index cffdb0144a..15542cd500 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -221,6 +221,7 @@ "WinUI3Apps\\PowerToys.Settings.exe", "PowerToys.CmdPalModuleInterface.dll", + "CmdPalKeyboardService.dll", "*Microsoft.CmdPal.UI_*.msix" ], "SigningInfo": { @@ -331,6 +332,8 @@ "TestableIO.System.IO.Abstractions.Wrappers.dll", "WinUI3Apps\\TestableIO.System.IO.Abstractions.Wrappers.dll", "WinUI3Apps\\OpenAI.dll", + "Testably.Abstractions.FileSystem.Interface.dll", + "WinUI3Apps\\Testably.Abstractions.FileSystem.Interface.dll", "ColorCode.Core.dll", "ColorCode.UWP.dll", "UnitsNet.dll", diff --git a/.pipelines/UpdateVersions.ps1 b/.pipelines/UpdateVersions.ps1 index c19dfb5dec..a1bc5bef9a 100644 --- a/.pipelines/UpdateVersions.ps1 +++ b/.pipelines/UpdateVersions.ps1 @@ -1,16 +1,24 @@ Param( - # Using the default value of 1.6 for winAppSdkVersionNumber and useExperimentalVersion as false + # Using the default value of 1.7 for winAppSdkVersionNumber and useExperimentalVersion as false [Parameter(Mandatory=$False,Position=1)] - [string]$winAppSdkVersionNumber = "1.6", + [string]$winAppSdkVersionNumber = "1.7", # When the pipeline calls the PS1 file, the passed parameters are converted to string type [Parameter(Mandatory=$False,Position=2)] - [boolean]$useExperimentalVersion = $False + [boolean]$useExperimentalVersion = $False, + + # Root folder Path for processing + [Parameter(Mandatory=$False,Position=3)] + [string]$rootPath = $(Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)), + + # Root folder Path for processing + [Parameter(Mandatory=$False,Position=4)] + [string]$sourceLink = "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json" ) function Update-NugetConfig { param ( - [string]$filePath = "nuget.config" + [string]$filePath = [System.IO.Path]::Combine($rootPath, "nuget.config") ) Write-Host "Updating nuget.config file" @@ -35,7 +43,33 @@ function Update-NugetConfig { $xml.Save($filePath) } -$sourceLink = "https://microsoft.pkgs.visualstudio.com/ProjectReunion/_packaging/Project.Reunion.nuget.internal/nuget/v3/index.json" +function Read-FileWithEncoding { + param ( + [string]$Path + ) + + $reader = New-Object System.IO.StreamReader($Path, $true) # auto-detect encoding + $content = $reader.ReadToEnd() + $encoding = $reader.CurrentEncoding + $reader.Close() + + return [PSCustomObject]@{ + Content = $content + Encoding = $encoding + } +} + +function Write-FileWithEncoding { + param ( + [string]$Path, + [string]$Content, + [System.Text.Encoding]$Encoding + ) + + $writer = New-Object System.IO.StreamWriter($Path, $false, $Encoding) + $writer.Write($Content) + $writer.Close() +} # Execute nuget list and capture the output if ($useExperimentalVersion) { @@ -79,50 +113,54 @@ if ($latestVersion) { } # Update packages.config files -Get-ChildItem -Recurse packages.config | ForEach-Object { - $content = Get-Content $_.FullName -Raw +Get-ChildItem -Path $rootPath -Recurse packages.config | ForEach-Object { + $file = Read-FileWithEncoding -Path $_.FullName + $content = $file.Content if ($content -match 'package id="Microsoft.WindowsAppSDK"') { $newVersionString = 'package id="Microsoft.WindowsAppSDK" version="' + $WinAppSDKVersion + '"' $oldVersionString = 'package id="Microsoft.WindowsAppSDK" version="[-.0-9a-zA-Z]*"' $content = $content -replace $oldVersionString, $newVersionString - Set-Content -Path $_.FullName -Value $content + Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding Write-Host "Modified " $_.FullName } } # Update Directory.Packages.props file -$propsFile = "Directory.Packages.props" +$propsFile = [System.IO.Path]::Combine($rootPath,"Directory.Packages.props") if (Test-Path $propsFile) { - $content = Get-Content $propsFile -Raw + $file = Read-FileWithEncoding -Path $propsFile + $content = $file.Content if ($content -match '' $oldVersionString = '' $content = $content -replace $oldVersionString, $newVersionString - Set-Content -Path $propsFile -Value $content + Write-FileWithEncoding -Path $propsFile -Content $content -Encoding $file.encoding Write-Host "Modified " $propsFile } } # Update .vcxproj files -Get-ChildItem -Recurse *.vcxproj | ForEach-Object { - $content = Get-Content $_.FullName -Raw +Get-ChildItem -Path $rootPath -Recurse *.vcxproj | ForEach-Object { + $file = Read-FileWithEncoding -Path $_.FullName + $content = $file.Content if ($content -match '\\Microsoft.WindowsAppSDK.') { $newVersionString = '\Microsoft.WindowsAppSDK.' + $WinAppSDKVersion + '\' $oldVersionString = '\\Microsoft.WindowsAppSDK.[-.0-9a-zA-Z]*\\' $content = $content -replace $oldVersionString, $newVersionString - Set-Content -Path $_.FullName -Value $content + Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding Write-Host "Modified " $_.FullName } } # Update .csproj files -Get-ChildItem -Recurse *.csproj | ForEach-Object { - $content = Get-Content $_.FullName -Raw +Get-ChildItem -Path $rootPath -Recurse *.csproj | ForEach-Object { + $file = Read-FileWithEncoding -Path $_.FullName + $content = $file.Content if ($content -match 'PackageReference Include="Microsoft.WindowsAppSDK"') { $newVersionString = 'PackageReference Include="Microsoft.WindowsAppSDK" Version="'+ $WinAppSDKVersion + '"' $oldVersionString = 'PackageReference Include="Microsoft.WindowsAppSDK" Version="[-.0-9a-zA-Z]*"' $content = $content -replace $oldVersionString, $newVersionString - Set-Content -Path $_.FullName -Value $content + Write-FileWithEncoding -Path $_.FullName -Content $content -Encoding $file.encoding Write-Host "Modified " $_.FullName } } diff --git a/.pipelines/tsa.json b/.pipelines/tsa.json index 351545613f..2f1e84c7f1 100644 --- a/.pipelines/tsa.json +++ b/.pipelines/tsa.json @@ -3,5 +3,5 @@ "notificationAliases": ["powertoys@microsoft.com"], "instanceUrl": "https://microsoft.visualstudio.com", "projectName": "OS", - "areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys" + "areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\DIVE\\PowerToys" } diff --git a/.pipelines/v2/ci-using-the-latest-winappsdk.yml b/.pipelines/v2/ci-using-the-latest-winappsdk.yml index cc9f00f80d..3d628b13a9 100644 --- a/.pipelines/v2/ci-using-the-latest-winappsdk.yml +++ b/.pipelines/v2/ci-using-the-latest-winappsdk.yml @@ -33,7 +33,7 @@ parameters: default: true - name: winAppSDKVersionNumber type: string - default: 1.6 + default: 1.7 - name: useExperimentalVersion type: boolean default: false diff --git a/.pipelines/v2/release.yml b/.pipelines/v2/release.yml index 26819c5d14..2a5be93aec 100644 --- a/.pipelines/v2/release.yml +++ b/.pipelines/v2/release.yml @@ -148,5 +148,7 @@ extends: parameters: versionNumber: ${{ parameters.versionNumber }} includePublicSymbolServer: ${{ parameters.publishSymbolsToPublic }} + ${{ if ne(parameters.publishSymbolsToPublic, true) }}: + symbolExpiryTime: 10 # For private builds, expire symbols within 10 days. The default is 100 years. subscription: $(SymbolPublishingServiceConnection) symbolProject: $(SymbolPublishingProject) diff --git a/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml b/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml index 1fccd6de74..9c59312844 100644 --- a/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml +++ b/.pipelines/v2/templates/steps-update-winappsdk-and-restore-nuget.yml @@ -17,6 +17,7 @@ steps: arguments: > -winAppSdkVersionNumber ${{ parameters.versionNumber }} -useExperimentalVersion $${{ parameters.useExperimentalVersion }} + -rootPath "$(build.sourcesdirectory)" - script: echo $(WinAppSDKVersion) displayName: 'Display WinAppSDK Version Found' diff --git a/COMMUNITY.md b/COMMUNITY.md index 3e7cd6719e..ccfa61decf 100644 --- a/COMMUNITY.md +++ b/COMMUNITY.md @@ -188,6 +188,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter - [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead - [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager - [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager +- [@zhiwei-ms](https://github.com/zhiwei-ms) - Zhiwei Yu - Product Manager - [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead - [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead - [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead diff --git a/Directory.Packages.props b/Directory.Packages.props index f4cfe643e9..b3b404ea51 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -10,14 +10,14 @@ - - - - - - - - + + + + + + + + @@ -29,7 +29,7 @@ - + @@ -45,7 +45,7 @@ - + @@ -55,13 +55,13 @@ --> - + - + @@ -69,7 +69,7 @@ - + @@ -84,14 +84,17 @@ - - + + + + + diff --git a/NOTICE.md b/NOTICE.md index 15693c9590..d604be7d7f 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -75,6 +75,40 @@ OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ``` +## Utility: Command Palette Built-in Extensions + +### Calculator + +#### Mages + +We use the Mages NuGet package for calculating the result of expression. + +**Source**: [https://github.com/FlorianRappl/Mages](https://github.com/FlorianRappl/Mages) + +``` +The MIT License (MIT) + +Copyright (c) 2016 - 2025 Florian Rappl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + ## Utility: File Explorer Add-ins ### Monaco Editor @@ -1393,7 +1427,6 @@ SOFTWARE. ## NuGet Packages used by PowerToys - - AdaptiveCards.ObjectModel.WinUI3 2.0.0-beta - AdaptiveCards.Rendering.WinUI3 2.1.0-beta - AdaptiveCards.Templating 2.0.2 @@ -1401,14 +1434,14 @@ SOFTWARE. - Azure.AI.OpenAI 1.0.0-beta.17 - CommunityToolkit.Common 8.4.0 - CommunityToolkit.Mvvm 8.4.0 -- CommunityToolkit.WinUI.Animations 8.2.250129-preview2 -- CommunityToolkit.WinUI.Collections 8.2.250129-preview2 -- CommunityToolkit.WinUI.Controls.Primitives 8.2.250129-preview2 -- CommunityToolkit.WinUI.Controls.Segmented 8.2.250129-preview2 -- CommunityToolkit.WinUI.Controls.SettingsControls 8.2.250129-preview2 -- CommunityToolkit.WinUI.Controls.Sizers 8.2.250129-preview2 -- CommunityToolkit.WinUI.Converters 8.2.250129-preview2 -- CommunityToolkit.WinUI.Extensions 8.2.250129-preview2 +- CommunityToolkit.WinUI.Animations 8.2.250402 +- CommunityToolkit.WinUI.Collections 8.2.250402 +- CommunityToolkit.WinUI.Controls.Primitives 8.2.250402 +- CommunityToolkit.WinUI.Controls.Segmented 8.2.250402 +- CommunityToolkit.WinUI.Controls.SettingsControls 8.2.250402 +- CommunityToolkit.WinUI.Controls.Sizers 8.2.250402 +- CommunityToolkit.WinUI.Converters 8.2.250402 +- CommunityToolkit.WinUI.Extensions 8.2.250402 - CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2 - CommunityToolkit.WinUI.UI.Controls.Markdown 7.1.2 - ControlzEx 6.0.0 @@ -1419,7 +1452,7 @@ SOFTWARE. - LazyCache 2.4.0 - Mages 3.0.0 - Markdig.Signed 0.34.0 -- MessagePack 2.5.187 +- MessagePack 3.1.3 - Microsoft.Bcl.AsyncInterfaces 9.0.4 - Microsoft.CodeAnalysis.NetAnalyzers 9.0.0 - Microsoft.Data.Sqlite 9.0.4 @@ -1439,20 +1472,20 @@ SOFTWARE. - Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWinRT 2.2.0 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 -- Microsoft.WindowsAppSDK 1.6.250205002 -- Microsoft.WindowsPackageManager.ComInterop 1.10.120-preview +- Microsoft.WindowsAppSDK 1.7.250401001 +- Microsoft.WindowsPackageManager.ComInterop 1.10.340 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 - Moq 4.18.4 -- MSTest 3.6.3 +- MSTest 3.8.3 - NLog.Extensions.Logging 5.3.8 - NLog.Schema 5.2.8 - OpenAI 2.0.0 - ReverseMarkdown 4.1.0 - ScipBe.Common.Office.OneNote 3.0.1 - SharpCompress 0.37.2 -- StreamJsonRpc 2.19.27 +- StreamJsonRpc 2.21.69 - StyleCop.Analyzers 1.2.0-beta.556 - System.CodeDom 9.0.4 - System.CommandLine 2.0.0-beta4.22272.1 @@ -1463,17 +1496,21 @@ SOFTWARE. - System.Diagnostics.EventLog 9.0.4 - System.Diagnostics.PerformanceCounter 9.0.4 - System.Drawing.Common 9.0.4 -- System.IO.Abstractions 21.0.29 -- System.IO.Abstractions.TestingHelpers 21.0.29 +- System.IO.Abstractions 22.0.13 +- System.IO.Abstractions.TestingHelpers 22.0.13 - System.Management 9.0.4 +- System.Net.Http 4.3.4 +- System.Private.Uri 4.3.2 - System.Reactive 6.0.1 - System.Runtime.Caching 9.0.4 - System.ServiceProcess.ServiceController 9.0.4 - System.Text.Encoding.CodePages 9.0.4 - System.Text.Json 9.0.4 +- System.Text.RegularExpressions 4.3.1 - UnicodeInformation 2.6.0 - UnitsNet 5.56.0 - UTF.Unknown 2.5.1 - WinUIEx 2.2.0 - WPF-UI 3.0.5 - WyHash 1.0.5 + diff --git a/PowerToys.sln b/PowerToys.sln index 9b911b388b..201c0fbefe 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -624,19 +624,19 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CommandPalette", "CommandPa EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Built-in Extensions", "Built-in Extensions", "{ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Apps", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Apps\Microsoft.CmdPal.Ext.Apps.csproj", "{6CE438DF-C245-4997-A360-0A0939E4BA34}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Apps", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Apps\Microsoft.CmdPal.Ext.Apps.csproj", "{6CE438DF-C245-4997-A360-0A0939E4BA34}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Bookmarks", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj", "{E09AA983-C755-474F-83D6-A5CDF528C070}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Bookmarks", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Bookmark\Microsoft.CmdPal.Ext.Bookmarks.csproj", "{E09AA983-C755-474F-83D6-A5CDF528C070}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Calc", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj", "{6D56B64D-FF1F-488F-AFED-9B9854A5D399}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Calc", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Calc\Microsoft.CmdPal.Ext.Calc.csproj", "{6D56B64D-FF1F-488F-AFED-9B9854A5D399}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Registry", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj", "{92EC89E4-9972-453A-8A1A-3A9E230C146A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Registry", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Registry\Microsoft.CmdPal.Ext.Registry.csproj", "{92EC89E4-9972-453A-8A1A-3A9E230C146A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsServices", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowsServices\Microsoft.CmdPal.Ext.WindowsServices.csproj", "{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsServices", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowsServices\Microsoft.CmdPal.Ext.WindowsServices.csproj", "{51939B4F-1F62-4BFF-A6A2-C08646E5BE95}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsSettings", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowsSettings\Microsoft.CmdPal.Ext.WindowsSettings.csproj", "{D1160404-D3D1-497A-883A-4059C07C2273}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsSettings", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowsSettings\Microsoft.CmdPal.Ext.WindowsSettings.csproj", "{D1160404-D3D1-497A-883A-4059C07C2273}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsTerminal", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowsTerminal\Microsoft.CmdPal.Ext.WindowsTerminal.csproj", "{40F6D69D-E321-400F-A767-5628C7AE453D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowsTerminal", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowsTerminal\Microsoft.CmdPal.Ext.WindowsTerminal.csproj", "{40F6D69D-E321-400F-A767-5628C7AE453D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extension SDK", "Extension SDK", "{F3D09629-59A2-4924-A4B9-D6BFAA2C1B49}" EndProject @@ -650,9 +650,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.Terminal.UI", "sr EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sample Extensions", "Sample Extensions", "{071E18A4-A530-46B8-AB7D-B862EE55E24E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessMonitorExtension", "src\modules\cmdpal\Exts\ProcessMonitorExtension\ProcessMonitorExtension.csproj", "{C846F7A7-792A-47D9-B0CB-417C900EE03D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProcessMonitorExtension", "src\modules\cmdpal\ext\ProcessMonitorExtension\ProcessMonitorExtension.csproj", "{C846F7A7-792A-47D9-B0CB-417C900EE03D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SamplePagesExtension", "src\modules\cmdpal\Exts\SamplePagesExtension\SamplePagesExtension.csproj", "{C831231F-891C-4572-9694-45062534B42A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SamplePagesExtension", "src\modules\cmdpal\ext\SamplePagesExtension\SamplePagesExtension.csproj", "{C831231F-891C-4572-9694-45062534B42A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{7520A2FE-00A2-49B8-83ED-DB216E874C04}" EndProject @@ -660,7 +660,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.UI", "src\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.UI.ViewModels", "src\modules\cmdpal\Microsoft.CmdPal.UI.ViewModels\Microsoft.CmdPal.UI.ViewModels.csproj", "{C66020D1-CB10-4CF7-8715-84C97FD5E5E2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.ClipboardHistory", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.ClipboardHistory\Microsoft.CmdPal.Ext.ClipboardHistory.csproj", "{79775343-7A3D-445D-9104-3DD5B2893DF9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.ClipboardHistory", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.ClipboardHistory\Microsoft.CmdPal.Ext.ClipboardHistory.csproj", "{79775343-7A3D-445D-9104-3DD5B2893DF9}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalModuleInterface", "src\modules\cmdpal\CmdPalModuleInterface\CmdPalModuleInterface.vcxproj", "{0ADEB797-C8C7-4FFA-ACD5-2AF6CAD7ECD8}" EndProject @@ -668,15 +668,15 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", " EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Indexer", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj", "{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Indexer", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj", "{453CBB73-A3CB-4D0B-8D24-6940B86FE21D}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Shell", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj", "{C0CE3B5E-16D3-495D-B335-CA791B660162}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.Shell", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.Shell\Microsoft.CmdPal.Ext.Shell.csproj", "{C0CE3B5E-16D3-495D-B335-CA791B660162}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowWalker", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj", "{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.CmdPal.Ext.WindowWalker", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WindowWalker\Microsoft.CmdPal.Ext.WindowWalker.csproj", "{3A9A7297-92C4-4F16-B6F9-8D4AB652C86C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WebSearch", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WebSearch\Microsoft.CmdPal.Ext.WebSearch.csproj", "{605E914B-7232-4789-AF46-BF5D3DDFC14E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WebSearch", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WebSearch\Microsoft.CmdPal.Ext.WebSearch.csproj", "{605E914B-7232-4789-AF46-BF5D3DDFC14E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WinGet", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj", "{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WinGet", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.WinGet\Microsoft.CmdPal.Ext.WinGet.csproj", "{E81A7D20-9862-ABDB-0AAE-9BC5B517A9F9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedPaste.UnitTests", "src\modules\AdvancedPaste\AdvancedPaste.UnitTests\AdvancedPaste.UnitTests.csproj", "{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}" EndProject @@ -690,7 +690,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItModuleInterface", "sr EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ZoomItSettingsInterop", "src\modules\ZoomIt\ZoomItSettingsInterop\ZoomItSettingsInterop.vcxproj", "{CA7D8106-30B9-4AEC-9D05-B69B31B8C461}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.TimeDate", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.TimeDate\Microsoft.CmdPal.Ext.TimeDate.csproj", "{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.TimeDate", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.TimeDate\Microsoft.CmdPal.Ext.TimeDate.csproj", "{DCC6BD67-17BB-47AA-B507-FB0FE43A7449}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITestAutomation", "src\common\UITestAutomation\UITestAutomation.csproj", "{A558C25D-2007-498E-8B6F-43405AFAE9E2}" EndProject @@ -704,7 +704,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hosts.UITests", "src\module EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegistryPreview.FuzzTests", "src\modules\registrypreview\RegistryPreview.FuzzTests\RegistryPreview.FuzzTests.csproj", "{5702B3CC-8575-48D5-83D8-15BB42269CD3}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\Exts\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.System", "src\modules\cmdpal\ext\Microsoft.CmdPal.Ext.System\Microsoft.CmdPal.Ext.System.csproj", "{64B88F02-CD88-4ED8-9624-989A800230F9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CmdPalKeyboardService", "src\modules\cmdpal\CmdPalKeyboardService\CmdPalKeyboardService.vcxproj", "{5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerRename.FuzzingTest", "src\modules\powerrename\PowerRename.FuzzingTest\PowerRename.FuzzingTest.vcxproj", "{2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -2574,14 +2578,24 @@ Global {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|ARM64.Build.0 = Debug|ARM64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.ActiveCfg = Debug|x64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x64.Build.0 = Debug|x64 - {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.ActiveCfg = Debug|x64 - {64B88F02-CD88-4ED8-9624-989A800230F9}.Debug|x86.Build.0 = Debug|x64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.ActiveCfg = Release|ARM64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|ARM64.Build.0 = Release|ARM64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.ActiveCfg = Release|x64 {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x64.Build.0 = Release|x64 - {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.ActiveCfg = Release|x64 - {64B88F02-CD88-4ED8-9624-989A800230F9}.Release|x86.Build.0 = Release|x64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|ARM64.Build.0 = Debug|ARM64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.ActiveCfg = Debug|x64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Debug|x64.Build.0 = Debug|x64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|ARM64.ActiveCfg = Release|ARM64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|ARM64.Build.0 = Release|ARM64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|x64.ActiveCfg = Release|x64 + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2}.Release|x64.Build.0 = Release|x64 + {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|x64.ActiveCfg = Debug|x64 + {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Debug|x64.Build.0 = Debug|x64 + {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|ARM64.ActiveCfg = Release|ARM64 + {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.ActiveCfg = Release|x64 + {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2852,6 +2866,8 @@ Global {4E0AE3A4-2EE0-44D7-A2D0-8769977254A0} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA} {5702B3CC-8575-48D5-83D8-15BB42269CD3} = {929C1324-22E8-4412-A9A8-80E85F3985A5} {64B88F02-CD88-4ED8-9624-989A800230F9} = {ECB8E0D1-7603-4E5C-AB10-D1E545E6F8E2} + {5F63C743-F6CE-4DBA-A200-2B3F8A14E8C2} = {3846508C-77EB-4034-A702-F8BB263C4F79} + {2694E2FB-DCD5-4BFF-A418-B6C3C7CE3B8E} = {89E20BCE-EB9C-46C8-8B50-E01A82E6FDC3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/doc/Fuzzing/CppFuzzingGuide.md b/doc/Fuzzing/CppFuzzingGuide.md new file mode 100644 index 0000000000..448d60a7d4 --- /dev/null +++ b/doc/Fuzzing/CppFuzzingGuide.md @@ -0,0 +1,117 @@ +# 🧪 C++ Project Fuzzing Test Guide + +This guide walks you through setting up a **fuzzing test** project for a C++ module using [libFuzzer](https://llvm.org/docs/LibFuzzer.html). +. + +--- + +## 🏗️ Step-by-Step Setup + +### 1. Create a New C++ Project + +- Use **Empty Project** template. +- Name it `.FuzzingTest`. + +--- + +### 2. Update Build Configuration + +- In **Configuration Manager**, Uncheck Build for both Release|ARM64, Debug|ARM64 and Debug|x64 configurations. +- Note: ARM64 is not supported in this case, so leave ARM64 configurations build disabled. +--- + +### 3. Enable ASan and libFuzzer in `.vcxproj` + +Edit the project file to enable fuzzing: + +```xml + + true + true + +``` + +--- + +### 4. Add Fuzzing Compiler Flags + +Add this to `AdditionalOptions` under the `Fuzzing` configuration: + +```xml +/fsanitize=address +/fsanitize-coverage=inline-8bit-counters +/fsanitize-coverage=edge +/fsanitize-coverage=trace-cmp +/fsanitize-coverage=trace-div +%(AdditionalOptions) +``` + +--- + +### 5. Link the Sanitizer Coverage Runtime + +In `Linker → Input → Additional Dependencies`, add: + +```text +$(VCToolsInstallDir)lib\$(Platform)\libsancov.lib +``` + +--- + +### 6. Copy Required Runtime DLL + +Add a `PostBuildEvent` to copy the ASAN DLL: + +```xml + + xcopy /y "$(VCToolsInstallDir)bin\Hostx64\x64\clang_rt.asan_dynamic-x86_64.dll" "$(OutDir)" + +``` + +--- + +### 7. Add Preprocessor Definitions + +To avoid annotation issues, add these to the `Preprocessor Definitions`: + +```text +_DISABLE_VECTOR_ANNOTATION;_DISABLE_STRING_ANNOTATION +``` + +--- + +## 🧬 Required Code + +### `LLVMFuzzerTestOneInput` Entry Point + +Every fuzzing project must expose this function: + +```cpp +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + std::string input(reinterpret_cast(data), size); + + try + { + // Call your module with the input here. + } + catch (...) {} + + return 0; +} +``` + +--- + +## ⚙️ [Test run in the cloud](https://eng.ms/docs/cloud-ai-platform/azure-edge-platform-aep/aep-security/epsf-edge-and-platform-security-fundamentals/the-onefuzz-service/onefuzz/faq/notwindows/walkthrough) + +To submit a job to the cloud you can run with this command: + +``` +oip submit --config .\OneFuzzConfig.json --drop-path --platform windows --do-not-file-bugs --duration 1 +``` +You want to run with --do-not-file-bugs because if there is an issue with running the parser in the cloud (which is very possible), you don't want bugs to be created if there is an issue. The --duration task is the number of hours you want the task to run. I recommend just running for 1 hour to make sure things work initially. If you don't specify this parameter, it will default to 48 hours. You can find more about submitting a test job here. + +OneFuzz will send you an email when the job has started. + +--- diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md index 90966ead20..ec8ffc69a4 100644 --- a/doc/thirdPartyRunPlugins.md +++ b/doc/thirdPartyRunPlugins.md @@ -43,6 +43,9 @@ Contact the developers of a plugin directly for assistance with a specific plugi | [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS | | [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes | | [SVGL](https://github.com/Sameerjs6/powertoys-svgl) | [SameerJS6](https://github.com/SameerJS6) | Search, Browse and copy SVG logos from SVGL. | +| [QuickNotes](https://github.com/ruslanlap/CommunityPowerToysRunPlugin-QuickNotes) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and search notes directly from PowerToys Run. | +| [Weather](https://github.com/ruslanlap/PowerToysRun-Weather) | [ruslanlap](https://github.com/ruslanlap) | Get real-time weather information directly from PowerToys Run. | +| [Pomodoro](https://github.com/ruslanlap/PowerToysRun-Pomodoro) | [ruslanlap](https://github.com/ruslanlap) | Manage Pomodoro productivity sessions directly from PowerToys Run. | ## Extending software plugins @@ -65,3 +68,4 @@ Below are community created plugins that target a website or software. They are | [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili | | [YubicoOauthOTP](https://github.com/dlnilsson/Community.PowerToys.Run.Plugin.YubicoOauthOTP) | [dlnilsson](https://github.com/dlnilsson) | Display generated codes from OATH accounts stored on the YubiKey in powerToys Run | | [Firefox Bookmark](https://github.com/8LWXpg/PowerToysRun-FirefoxBookmark) | [8LWXpg](https://github.com/8LWXpg) | Open bookmarks in Firefox based browser | +[Linear](https://github.com/vednig/powertoys-linear) | [vednig](https://github.com/vednig) | Create Linear Issues directly from Powertoys Run | diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 37256bdd68..f15b8a4714 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -79,10 +79,7 @@ - - - - + diff --git a/src/Common.Dotnet.CsWinRT.props b/src/Common.Dotnet.CsWinRT.props index e4731ce2fd..cde2ce69ce 100644 --- a/src/Common.Dotnet.CsWinRT.props +++ b/src/Common.Dotnet.CsWinRT.props @@ -1,6 +1,8 @@ + + 10.0.22621.57 net9.0-windows10.0.22621.0 diff --git a/src/Common.Dotnet.PrepareGeneratedFolder.targets b/src/Common.Dotnet.PrepareGeneratedFolder.targets new file mode 100644 index 0000000000..d017590064 --- /dev/null +++ b/src/Common.Dotnet.PrepareGeneratedFolder.targets @@ -0,0 +1,16 @@ + + + + + + $(ProjectDir)obj\g + + + + + + + + + + \ No newline at end of file diff --git a/src/codeAnalysis/GlobalSuppressions.cs b/src/codeAnalysis/GlobalSuppressions.cs index c05e5f8820..d5ff98e548 100644 --- a/src/codeAnalysis/GlobalSuppressions.cs +++ b/src/codeAnalysis/GlobalSuppressions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation +// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -63,8 +63,16 @@ using System.Diagnostics.CodeAnalysis; [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")] [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "", Scope = "namespaceanddescendants", Target = "MouseWithoutBorders")] -// AOT +// AOT MVVMTK0045 +[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "AdvancedPaste.ViewModels")] [assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "HostsUILib")] +[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "EnvironmentVariablesUILib")] +[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")] [assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI")] [assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.UI.Views")] +[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0045:Using [ObservableProperty] on fields is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")] + +// AOT MVVMTK0049 +[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "Peek.FilePreviewer")] [assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.INotifyPropertyChangedGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "type", Target = "~T:Peek.UI.Views.TitleBar")] +[assembly: SuppressMessage("CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator", "MVVMTK0049:Using [INotifyPropertyChanged] is not AOT compatible for WinRT", Justification = "Updated MVVM toolkit package introduced this.", Scope = "namespaceanddescendants", Target = "RegistryPreviewUILib")] diff --git a/src/common/AllExperiments/Logger.cs b/src/common/AllExperiments/Logger.cs index 7604618bdf..b9e1f20969 100644 --- a/src/common/AllExperiments/Logger.cs +++ b/src/common/AllExperiments/Logger.cs @@ -25,7 +25,7 @@ namespace AllExperiments } // Using InvariantCulture since this is used for a log file name - var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); + var logFilePath = Path.Combine(ApplicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log"); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); diff --git a/src/common/ManagedCommon/ColorFormatHelper.cs b/src/common/ManagedCommon/ColorFormatHelper.cs index 08e62b921d..471104f215 100644 --- a/src/common/ManagedCommon/ColorFormatHelper.cs +++ b/src/common/ManagedCommon/ColorFormatHelper.cs @@ -141,6 +141,40 @@ namespace ManagedCommon return lab; } + /// + /// Convert a given to a Oklab color + /// + /// The to convert + /// The perceptual lightness [0..1] and two chromaticities [-0.5..0.5] + public static (double Lightness, double ChromaticityA, double ChromaticityB) ConvertToOklabColor(Color color) + { + var linear = ConvertSRGBToLinearRGB(color.R / 255d, color.G / 255d, color.B / 255d); + var oklab = GetOklabColorFromLinearRGB(linear.R, linear.G, linear.B); + return oklab; + } + + /// + /// Convert a given to a Oklch color + /// + /// The to convert + /// The perceptual lightness [0..1], the chroma [0..0.5], and the hue angle [0°..360°] + public static (double Lightness, double Chroma, double Hue) ConvertToOklchColor(Color color) + { + var oklab = ConvertToOklabColor(color); + var oklch = GetOklchColorFromOklab(oklab.Lightness, oklab.ChromaticityA, oklab.ChromaticityB); + + return oklch; + } + + public static (double R, double G, double B) ConvertSRGBToLinearRGB(double r, double g, double b) + { + // inverse companding, gamma correction must be undone + double rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92); + double gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92); + double bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92); + return (rLinear, gLinear, bLinear); + } + /// /// Convert a given to a CIE XYZ color (XYZ) /// The constants of the formula matches this Wikipedia page, but at a higher precision: @@ -156,10 +190,7 @@ namespace ManagedCommon double g = color.G / 255d; double b = color.B / 255d; - // inverse companding, gamma correction must be undone - double rLinear = (r > 0.04045) ? Math.Pow((r + 0.055) / 1.055, 2.4) : (r / 12.92); - double gLinear = (g > 0.04045) ? Math.Pow((g + 0.055) / 1.055, 2.4) : (g / 12.92); - double bLinear = (b > 0.04045) ? Math.Pow((b + 0.055) / 1.055, 2.4) : (b / 12.92); + (double rLinear, double gLinear, double bLinear) = ConvertSRGBToLinearRGB(r, g, b); return ( (rLinear * 0.41239079926595948) + (gLinear * 0.35758433938387796) + (bLinear * 0.18048078840183429), @@ -210,6 +241,63 @@ namespace ManagedCommon return (l, a, b); } + /// + /// Convert a linear RGB color to an Oklab color. + /// The constants of this formula come from https://github.com/Evercoder/culori/blob/2bedb8f0507116e75f844a705d0b45cf279b15d0/src/oklab/convertLrgbToOklab.js + /// and the implementation is based on https://bottosson.github.io/posts/oklab/ + /// + /// Linear R value + /// Linear G value + /// Linear B value + /// The perceptual lightness [0..1] and two chromaticities [-0.5..0.5] + private static (double Lightness, double ChromaticityA, double ChromaticityB) + GetOklabColorFromLinearRGB(double r, double g, double b) + { + double l = (0.41222147079999993 * r) + (0.5363325363 * g) + (0.0514459929 * b); + double m = (0.2119034981999999 * r) + (0.6806995450999999 * g) + (0.1073969566 * b); + double s = (0.08830246189999998 * r) + (0.2817188376 * g) + (0.6299787005000002 * b); + + double l_ = Math.Cbrt(l); + double m_ = Math.Cbrt(m); + double s_ = Math.Cbrt(s); + + return ( + (0.2104542553 * l_) + (0.793617785 * m_) - (0.0040720468 * s_), + (1.9779984951 * l_) - (2.428592205 * m_) + (0.4505937099 * s_), + (0.0259040371 * l_) + (0.7827717662 * m_) - (0.808675766 * s_) + ); + } + + /// + /// Convert an Oklab color from Cartesian form to its polar form Oklch + /// https://bottosson.github.io/posts/oklab/#the-oklab-color-space + /// + /// The + /// The + /// The + /// The perceptual lightness [0..1], the chroma [0..0.5], and the hue angle [0°..360°] + private static (double Lightness, double Chroma, double Hue) + GetOklchColorFromOklab(double lightness, double chromaticity_a, double chromaticity_b) + { + return GetLCHColorFromLAB(lightness, chromaticity_a, chromaticity_b); + } + + /// + /// Convert a color in Cartesian form (Lab) to its polar form (LCh) + /// + /// The + /// The + /// The + /// The lightness, chroma, and hue angle + private static (double Lightness, double Chroma, double Hue) + GetLCHColorFromLAB(double lightness, double chromaticity_a, double chromaticity_b) + { + // Lab to LCh transformation + double chroma = Math.Sqrt(Math.Pow(chromaticity_a, 2) + Math.Pow(chromaticity_b, 2)); + double hue = Math.Round(chroma, 3) == 0 ? 0.0 : ((Math.Atan2(chromaticity_b, chromaticity_a) * 180d / Math.PI) + 360d) % 360d; + return (lightness, chroma, hue); + } + /// /// Convert a given to a natural color (hue, whiteness, blackness) /// @@ -276,12 +364,17 @@ namespace ManagedCommon { "Br", 'p' }, // brightness percent { "In", 'p' }, // intensity percent { "Ll", 'p' }, // lightness (HSL) percent - { "Lc", 'p' }, // lightness(CIELAB)percent { "Va", 'p' }, // value percent { "Wh", 'p' }, // whiteness percent { "Bn", 'p' }, // blackness percent - { "Ca", 'p' }, // chromaticityA percent - { "Cb", 'p' }, // chromaticityB percent + { "Lc", 'p' }, // lightness (CIE) percent + { "Ca", 'p' }, // chromaticityA (CIELAB) percent + { "Cb", 'p' }, // chromaticityB (CIELAB) percent + { "Lo", 'p' }, // lightness (Oklab/Oklch) percent + { "Oa", 'p' }, // chromaticityA (Oklab) percent + { "Ob", 'p' }, // chromaticityB (Oklab) percent + { "Oc", 'p' }, // chroma (Oklch) percent + { "Oh", 'p' }, // hue angle (Oklch) percent { "Xv", 'i' }, // X value int { "Yv", 'i' }, // Y value int { "Zv", 'i' }, // Z value int @@ -424,6 +517,10 @@ namespace ManagedCommon var (lightnessC, _, _) = ConvertToCIELABColor(color); lightnessC = Math.Round(lightnessC, 2); return lightnessC.ToString(CultureInfo.InvariantCulture); + case "Lo": + var (lightnessO, _, _) = ConvertToOklabColor(color); + lightnessO = Math.Round(lightnessO, 2); + return lightnessO.ToString(CultureInfo.InvariantCulture); case "Wh": var (_, whiteness, _) = ConvertToHWBColor(color); whiteness = Math.Round(whiteness * 100); @@ -440,6 +537,22 @@ namespace ManagedCommon var (_, _, chromaticityB) = ConvertToCIELABColor(color); chromaticityB = Math.Round(chromaticityB, 2); return chromaticityB.ToString(CultureInfo.InvariantCulture); + case "Oa": + var (_, chromaticityAOklab, _) = ConvertToOklabColor(color); + chromaticityAOklab = Math.Round(chromaticityAOklab, 2); + return chromaticityAOklab.ToString(CultureInfo.InvariantCulture); + case "Ob": + var (_, _, chromaticityBOklab) = ConvertToOklabColor(color); + chromaticityBOklab = Math.Round(chromaticityBOklab, 2); + return chromaticityBOklab.ToString(CultureInfo.InvariantCulture); + case "Oc": + var (_, chromaOklch, _) = ConvertToOklchColor(color); + chromaOklch = Math.Round(chromaOklch, 2); + return chromaOklch.ToString(CultureInfo.InvariantCulture); + case "Oh": + var (_, _, hueOklch) = ConvertToOklchColor(color); + hueOklch = Math.Round(hueOklch, 2); + return hueOklch.ToString(CultureInfo.InvariantCulture); case "Xv": var (x, _, _) = ConvertToCIEXYZColor(color); x = Math.Round(x * 100, 4); @@ -495,8 +608,10 @@ namespace ManagedCommon case "HSI": return "hsi(%Hu, %Si%, %In%)"; case "HWB": return "hwb(%Hu, %Wh%, %Bn%)"; case "NCol": return "%Hn, %Wh%, %Bn%"; - case "CIELAB": return "CIELab(%Lc, %Ca, %Cb)"; case "CIEXYZ": return "XYZ(%Xv, %Yv, %Zv)"; + case "CIELAB": return "CIELab(%Lc, %Ca, %Cb)"; + case "Oklab": return "oklab(%Lo, %Oa, %Ob)"; + case "Oklch": return "oklch(%Lo, %Oc, %Oh)"; case "VEC4": return "(%Reff, %Grff, %Blff, 1f)"; case "Decimal": return "%Dv"; case "HEX Int": return "0xFF%ReX%GrX%BlX"; diff --git a/src/common/ManagedCommon/IdRecoveryHelper.cs b/src/common/ManagedCommon/IdRecoveryHelper.cs new file mode 100644 index 0000000000..cd0fcb57a5 --- /dev/null +++ b/src/common/ManagedCommon/IdRecoveryHelper.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ManagedCommon +{ + public static class IdRecoveryHelper + { + /// + /// Fixes invalid IDs in the given list by assigning unique values. + /// It ensures that all IDs are non-empty and unique, correcting any duplicates or empty IDs. + /// + /// The list of items that may contain invalid IDs. + public static void RecoverInvalidIds(IEnumerable items) + where T : class, IHasId + { + var idSet = new HashSet(); + int newId = 0; + var sortedItems = items.OrderBy(i => i.Id).ToList(); // Sort items by ID for consistent processing + + // Iterate through the list and fix invalid IDs + foreach (var item in sortedItems) + { + // If the ID is invalid or already exists in the set (duplicate), assign a new unique ID + if (!idSet.Add(item.Id)) + { + // Find the next available unique ID + while (idSet.Contains(newId)) + { + newId++; + } + + item.Id = newId; + idSet.Add(newId); // Add the newly assigned ID to the set + } + } + } + } + + public interface IHasId + { + int Id { get; set; } + } +} diff --git a/src/common/ManagedCommon/Logger.cs b/src/common/ManagedCommon/Logger.cs index b67d63c3b5..cd85d500e1 100644 --- a/src/common/ManagedCommon/Logger.cs +++ b/src/common/ManagedCommon/Logger.cs @@ -54,7 +54,7 @@ namespace ManagedCommon Directory.CreateDirectory(applicationLogPath); } - var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".txt"); + var logFilePath = Path.Combine(applicationLogPath, "Log_" + DateTime.Now.ToString(@"yyyy-MM-dd", CultureInfo.InvariantCulture) + ".log"); Trace.Listeners.Add(new TextWriterTraceListener(logFilePath)); diff --git a/src/common/UITestAutomation/UITestAutomation.csproj b/src/common/UITestAutomation/UITestAutomation.csproj index 0da17d85b8..bc7a4ada54 100644 --- a/src/common/UITestAutomation/UITestAutomation.csproj +++ b/src/common/UITestAutomation/UITestAutomation.csproj @@ -14,6 +14,9 @@ + + + diff --git a/src/common/logger/logger_settings.h b/src/common/logger/logger_settings.h index dab7ad64a2..c880614237 100644 --- a/src/common/logger/logger_settings.h +++ b/src/common/logger/logger_settings.h @@ -8,49 +8,49 @@ struct LogSettings inline const static std::wstring logLevelOption = L"logLevel"; inline const static std::string runnerLoggerName = "runner"; inline const static std::wstring logPath = L"Logs\\"; - inline const static std::wstring runnerLogPath = L"RunnerLogs\\runner-log.txt"; + inline const static std::wstring runnerLogPath = L"RunnerLogs\\runner-log.log"; inline const static std::string actionRunnerLoggerName = "action-runner"; - inline const static std::wstring actionRunnerLogPath = L"RunnerLogs\\action-runner-log.txt"; + inline const static std::wstring actionRunnerLogPath = L"RunnerLogs\\action-runner-log.log"; inline const static std::string updateLoggerName = "update"; - inline const static std::wstring updateLogPath = L"UpdateLogs\\update-log.txt"; + inline const static std::wstring updateLogPath = L"UpdateLogs\\update-log.log"; inline const static std::string fileExplorerLoggerName = "FileExplorer"; - inline const static std::wstring fileExplorerLogPath = L"Logs\\file-explorer-log.txt"; + inline const static std::wstring fileExplorerLogPath = L"Logs\\file-explorer-log.log"; inline const static std::string gcodePrevLoggerName = "GcodePrevHandler"; - inline const static std::wstring gcodePrevLogPath = L"logs\\FileExplorer_localLow\\GcodePreviewHandler\\gcode-prev-handler-log.txt"; + inline const static std::wstring gcodePrevLogPath = L"logs\\FileExplorer_localLow\\GcodePreviewHandler\\gcode-prev-handler-log.log"; inline const static std::string gcodeThumbLoggerName = "GcodeThumbnailProvider"; - inline const static std::wstring gcodeThumbLogPath = L"logs\\FileExplorer_localLow\\GcodeThumbnailProvider\\gcode-thumbnail-provider-log.txt"; + inline const static std::wstring gcodeThumbLogPath = L"logs\\FileExplorer_localLow\\GcodeThumbnailProvider\\gcode-thumbnail-provider-log.log"; inline const static std::string mdPrevLoggerName = "MDPrevHandler"; - inline const static std::wstring mdPrevLogPath = L"logs\\FileExplorer_localLow\\MDPrevHandler\\md-prev-handler-log.txt"; + inline const static std::wstring mdPrevLogPath = L"logs\\FileExplorer_localLow\\MDPrevHandler\\md-prev-handler-log.log"; inline const static std::string monacoPrevLoggerName = "MonacoPrevHandler"; - inline const static std::wstring monacoPrevLogPath = L"logs\\FileExplorer_localLow\\MonacoPrevHandler\\monaco-prev-handler-log.txt"; + inline const static std::wstring monacoPrevLogPath = L"logs\\FileExplorer_localLow\\MonacoPrevHandler\\monaco-prev-handler-log.log"; inline const static std::string pdfPrevLoggerName = "PdfPrevHandler"; - inline const static std::wstring pdfPrevLogPath = L"logs\\FileExplorer_localLow\\PdfPrevHandler\\pdf-prev-handler-log.txt"; + inline const static std::wstring pdfPrevLogPath = L"logs\\FileExplorer_localLow\\PdfPrevHandler\\pdf-prev-handler-log.log"; inline const static std::string pdfThumbLoggerName = "PdfThumbnailProvider"; - inline const static std::wstring pdfThumbLogPath = L"logs\\FileExplorer_localLow\\PdfThumbnailProvider\\pdf-thumbnail-provider-log.txt"; + inline const static std::wstring pdfThumbLogPath = L"logs\\FileExplorer_localLow\\PdfThumbnailProvider\\pdf-thumbnail-provider-log.log"; inline const static std::string qoiPrevLoggerName = "QoiPrevHandler"; - inline const static std::wstring qoiPrevLogPath = L"logs\\FileExplorer_localLow\\QoiPreviewHandler\\qoi-prev-handler-log.txt"; + inline const static std::wstring qoiPrevLogPath = L"logs\\FileExplorer_localLow\\QoiPreviewHandler\\qoi-prev-handler-log.log"; inline const static std::string qoiThumbLoggerName = "QoiThumbnailProvider"; - inline const static std::wstring qoiThumbLogPath = L"logs\\FileExplorer_localLow\\QoiThumbnailProvider\\qoi-thumbnail-provider-log.txt"; + inline const static std::wstring qoiThumbLogPath = L"logs\\FileExplorer_localLow\\QoiThumbnailProvider\\qoi-thumbnail-provider-log.log"; inline const static std::string stlThumbLoggerName = "StlThumbnailProvider"; - inline const static std::wstring stlThumbLogPath = L"logs\\FileExplorer_localLow\\StlThumbnailProvider\\stl-thumbnail-provider-log.txt"; + inline const static std::wstring stlThumbLogPath = L"logs\\FileExplorer_localLow\\StlThumbnailProvider\\stl-thumbnail-provider-log.log"; inline const static std::string svgPrevLoggerName = "SvgPrevHandler"; - inline const static std::wstring svgPrevLogPath = L"logs\\FileExplorer_localLow\\SvgPrevHandler\\svg-prev-handler-log.txt"; + inline const static std::wstring svgPrevLogPath = L"logs\\FileExplorer_localLow\\SvgPrevHandler\\svg-prev-handler-log.log"; inline const static std::string svgThumbLoggerName = "SvgThumbnailProvider"; - inline const static std::wstring svgThumbLogPath = L"logs\\FileExplorer_localLow\\SvgThumbnailProvider\\svg-thumbnail-provider-log.txt"; + inline const static std::wstring svgThumbLogPath = L"logs\\FileExplorer_localLow\\SvgThumbnailProvider\\svg-thumbnail-provider-log.log"; inline const static std::string launcherLoggerName = "launcher"; - inline const static std::wstring launcherLogPath = L"LogsModuleInterface\\launcher-log.txt"; + inline const static std::wstring launcherLogPath = L"LogsModuleInterface\\launcher-log.log"; inline const static std::string mouseWithoutBordersLoggerName = "mouseWithoutBorders"; - inline const static std::wstring mouseWithoutBordersLogPath = L"LogsModuleInterface\\mouseWithoutBorders-log.txt"; - inline const static std::wstring awakeLogPath = L"Logs\\awake-log.txt"; - inline const static std::wstring powerAccentLogPath = L"quick-accent-log.txt"; + inline const static std::wstring mouseWithoutBordersLogPath = L"LogsModuleInterface\\mouseWithoutBorders-log.log"; + inline const static std::wstring awakeLogPath = L"Logs\\awake-log.log"; + inline const static std::wstring powerAccentLogPath = L"quick-accent-log.log"; inline const static std::string fancyZonesLoggerName = "fancyzones"; - inline const static std::wstring fancyZonesLogPath = L"fancyzones-log.txt"; + inline const static std::wstring fancyZonesLogPath = L"fancyzones-log.log"; inline const static std::wstring fancyZonesOldLogPath = L"FancyZonesLogs\\"; // needed to clean up old logs inline const static std::string shortcutGuideLoggerName = "shortcut-guide"; - inline const static std::wstring shortcutGuideLogPath = L"ShortcutGuideLogs\\shortcut-guide-log.txt"; - inline const static std::wstring powerOcrLogPath = L"Logs\\text-extractor-log.txt"; + inline const static std::wstring shortcutGuideLogPath = L"ShortcutGuideLogs\\shortcut-guide-log.log"; + inline const static std::wstring powerOcrLogPath = L"Logs\\text-extractor-log.log"; inline const static std::string keyboardManagerLoggerName = "keyboard-manager"; - inline const static std::wstring keyboardManagerLogPath = L"Logs\\keyboard-manager-log.txt"; + inline const static std::wstring keyboardManagerLogPath = L"Logs\\keyboard-manager-log.log"; inline const static std::string findMyMouseLoggerName = "find-my-mouse"; inline const static std::string mouseHighlighterLoggerName = "mouse-highlighter"; inline const static std::string mouseJumpLoggerName = "mouse-jump"; @@ -60,22 +60,22 @@ struct LogSettings inline const static std::string alwaysOnTopLoggerName = "always-on-top"; inline const static std::string powerOcrLoggerName = "TextExtractor"; inline const static std::string fileLocksmithLoggerName = "FileLocksmith"; - inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.txt"; + inline const static std::wstring alwaysOnTopLogPath = L"always-on-top-log.log"; inline const static std::string hostsLoggerName = "hosts"; - inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.txt"; + inline const static std::wstring hostsLogPath = L"Logs\\hosts-log.log"; inline const static std::string registryPreviewLoggerName = "registrypreview"; inline const static std::string cropAndLockLoggerName = "crop-and-lock"; - inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.txt"; + inline const static std::wstring registryPreviewLogPath = L"Logs\\registryPreview-log.log"; inline const static std::string environmentVariablesLoggerName = "environment-variables"; - inline const static std::wstring cmdNotFoundLogPath = L"Logs\\cmd-not-found-log.txt"; + inline const static std::wstring cmdNotFoundLogPath = L"Logs\\cmd-not-found-log.log"; inline const static std::string cmdNotFoundLoggerName = "cmd-not-found"; inline const static std::string newLoggerName = "NewPlus"; inline const static std::string workspacesLauncherLoggerName = "workspaces-launcher"; - inline const static std::wstring workspacesLauncherLogPath = L"workspaces-launcher-log.txt"; + inline const static std::wstring workspacesLauncherLogPath = L"workspaces-launcher-log.log"; inline const static std::string workspacesWindowArrangerLoggerName = "workspaces-window-arranger"; - inline const static std::wstring workspacesWindowArrangerLogPath = L"workspaces-window-arranger-log.txt"; + inline const static std::wstring workspacesWindowArrangerLogPath = L"workspaces-window-arranger-log.log"; inline const static std::string workspacesSnapshotToolLoggerName = "workspaces-snapshot-tool"; - inline const static std::wstring workspacesSnapshotToolLogPath = L"workspaces-snapshot-tool-log.txt"; + inline const static std::wstring workspacesSnapshotToolLogPath = L"workspaces-snapshot-tool-log.log"; inline const static std::string zoomItLoggerName = "zoom-it"; inline const static int retention = 30; std::wstring logLevel; diff --git a/src/common/utils/logger_helper.h b/src/common/utils/logger_helper.h index c2207525e3..2deec22155 100644 --- a/src/common/utils/logger_helper.h +++ b/src/common/utils/logger_helper.h @@ -91,7 +91,7 @@ namespace LoggerHelpers currentFolder.append(get_product_version()); auto logsPath = currentFolder; - logsPath.append(L"log.txt"); + logsPath.append(L"log.log"); Logger::init(loggerName, logsPath.wstring(), PTSettingsHelper::get_log_settings_file_location()); delete_other_versions_log_folders(rootFolder.wstring(), currentFolder); diff --git a/src/common/utils/package.h b/src/common/utils/package.h index 60bde7ea53..138f3b8e5b 100644 --- a/src/common/utils/package.h +++ b/src/common/utils/package.h @@ -301,6 +301,7 @@ namespace package if (!std::filesystem::exists(directoryPath)) { Logger::error(L"The directory '" + directoryPath + L"' does not exist."); + return {}; } const std::regex pattern(R"(^.+\.(appx|msix|msixbundle)$)", std::regex_constants::icase); diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs index b56868ece8..218349b32b 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs @@ -18,10 +18,19 @@ public static class OcrHelpers { public static async Task ExtractTextAsync(SoftwareBitmap bitmap, CancellationToken cancellationToken) { - var ocrLanguage = GetOCRLanguage() ?? throw new InvalidOperationException("Unable to determine OCR language"); + var ocrLanguage = GetOCRLanguage(); cancellationToken.ThrowIfCancellationRequested(); - var ocrEngine = OcrEngine.TryCreateFromLanguage(ocrLanguage) ?? throw new InvalidOperationException("Unable to create OCR engine"); + OcrEngine ocrEngine; + if (ocrLanguage is not null) + { + ocrEngine = OcrEngine.TryCreateFromLanguage(ocrLanguage) ?? throw new InvalidOperationException("Unable to create OCR engine from specified language"); + } + else + { + ocrEngine = OcrEngine.TryCreateFromUserProfileLanguages() ?? throw new InvalidOperationException("Unable to create OCR engine from user profile language"); + } + cancellationToken.ThrowIfCancellationRequested(); var ocrResult = await ocrEngine.RecognizeAsync(bitmap); diff --git a/src/modules/EnvironmentVariables/EnvironmentVariablesUILib/EnvironmentVariablesUILib.csproj b/src/modules/EnvironmentVariables/EnvironmentVariablesUILib/EnvironmentVariablesUILib.csproj index 24820b7d83..3973f8d2eb 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariablesUILib/EnvironmentVariablesUILib.csproj +++ b/src/modules/EnvironmentVariables/EnvironmentVariablesUILib/EnvironmentVariablesUILib.csproj @@ -15,8 +15,6 @@ PowerToys.EnvironmentVariablesUILib.pri true true - - $(ProjectDir)obj\g @@ -56,7 +54,4 @@ - - - diff --git a/src/modules/Hosts/Hosts.UITests/Hosts.UITests.csproj b/src/modules/Hosts/Hosts.UITests/Hosts.UITests.csproj index f9a7f4247b..e94a0d9b4b 100644 --- a/src/modules/Hosts/Hosts.UITests/Hosts.UITests.csproj +++ b/src/modules/Hosts/Hosts.UITests/Hosts.UITests.csproj @@ -20,6 +20,9 @@ + + + diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index 86d258854f..f0b12187ad 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,6 +1,6 @@  - + @@ -141,7 +141,7 @@ - + @@ -153,7 +153,7 @@ - - + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index 61ff4b9f07..996736442d 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp index 4d4b47ea12..7075da2ea7 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp @@ -121,6 +121,22 @@ namespace AppLauncher // packaged apps: try launching first by AppUserModel.ID // usage example: elevated Terminal if (!launched && !app.appUserModelId.empty() && !app.packageFullName.empty()) + { + Logger::trace(L"Launching {} as {} - {app.packageFullName}", app.name, app.appUserModelId, app.packageFullName); + auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated); + if (res.isOk()) + { + launched = true; + } + else + { + launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() }); + } + } + + // win32 app with appUserModelId: + // usage example: steam games + if (!launched && !app.appUserModelId.empty()) { Logger::trace(L"Launching {} as {}", app.name, app.appUserModelId); auto res = LaunchApp(L"shell:AppsFolder\\" + app.appUserModelId, app.commandLineArgs, app.isElevated); diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 19b33214b7..a37d82f8ca 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "AppUtils.h" +#include "SteamHelper.h" #include #include @@ -34,6 +35,8 @@ namespace Utils constexpr const wchar_t* EdgeFilename = L"msedge.exe"; constexpr const wchar_t* ChromeFilename = L"chrome.exe"; + + constexpr const wchar_t* SteamUrlProtocol = L"steam:"; } AppList IterateAppsFolder() @@ -138,6 +141,34 @@ namespace Utils else if (prop == NonLocalizable::PackageInstallPathProp || prop == NonLocalizable::InstallPathProp) { data.installPath = propVariantString.m_pData; + + if (!data.installPath.empty()) + { + const bool isSteamProtocol = data.installPath.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0; + + if (isSteamProtocol) + { + Logger::info(L"Found steam game: protocol path: {}", data.installPath); + data.protocolPath = data.installPath; + + try + { + auto gameId = Steam::GetGameIdFromUrlProtocolPath(data.installPath); + auto gameFolder = Steam::GetSteamGameInfoFromAcfFile(gameId); + + if (gameFolder) + { + data.installPath = gameFolder->gameInstallationPath; + Logger::info(L"Found steam game: physical path: {}", data.installPath); + } + } + catch (std::exception ex) + { + Logger::error(L"Failed to get installPath for game {}", data.installPath); + Logger::error("Error: {}", ex.what()); + } + } + } } } @@ -397,5 +428,10 @@ namespace Utils { return installPath.ends_with(NonLocalizable::ChromeFilename); } + + bool AppData::IsSteamGame() const + { + return protocolPath.rfind(NonLocalizable::SteamUrlProtocol, 0) == 0; + } } } \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.h b/src/modules/Workspaces/WorkspacesLib/AppUtils.h index 3c81049f83..80b5e2fd49 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.h +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.h @@ -13,10 +13,12 @@ namespace Utils std::wstring packageFullName; std::wstring appUserModelId; std::wstring pwaAppId; + std::wstring protocolPath; bool canLaunchElevated = false; bool IsEdge() const; bool IsChrome() const; + bool IsSteamGame() const; }; using AppList = std::vector; diff --git a/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp new file mode 100644 index 0000000000..404002e284 --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib/SteamGameHelper.cpp @@ -0,0 +1,171 @@ +#include "pch.h" +#include "SteamHelper.h" +#include +#include +#include +#include +#include +#include + +namespace Utils +{ + + static std::wstring Utf8ToWide(const std::string& utf8) + { + if (utf8.empty()) + return L""; + + int size = MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), nullptr, 0); + if (size <= 0) + return L""; + + std::wstring wide(size, L'\0'); + MultiByteToWideChar(CP_UTF8, 0, utf8.data(), static_cast(utf8.size()), wide.data(), size); + return wide; + } + + namespace Steam + { + using namespace std; + namespace fs = std::filesystem; + + static std::optional GetSteamExePathFromRegistry() + { + static std::optional cachedPath; + if (cachedPath.has_value()) + { + return cachedPath; + } + + const std::vector roots = { HKEY_CLASSES_ROOT, HKEY_LOCAL_MACHINE, HKEY_USERS }; + const std::vector subKeys = { + L"steam\\shell\\open\\command", + L"Software\\Classes\\steam\\shell\\open\\command", + }; + + for (HKEY root : roots) + { + for (const auto& subKey : subKeys) + { + HKEY hKey; + if (RegOpenKeyExW(root, subKey.c_str(), 0, KEY_READ, &hKey) == ERROR_SUCCESS) + { + wchar_t value[512]; + DWORD size = sizeof(value); + DWORD type = 0; + + if (RegQueryValueExW(hKey, nullptr, nullptr, &type, reinterpret_cast(value), &size) == ERROR_SUCCESS && + (type == REG_SZ || type == REG_EXPAND_SZ)) + { + std::wregex exeRegex(LR"delim("([^"]+steam\.exe)")delim"); + std::wcmatch match; + if (std::regex_search(value, match, exeRegex) && match.size() > 1) + { + RegCloseKey(hKey); + cachedPath = match[1].str(); + return cachedPath; + } + } + + RegCloseKey(hKey); + } + } + } + + cachedPath = std::nullopt; + return std::nullopt; + } + + static fs::path GetSteamBasePath() + { + auto steamFolderOpt = GetSteamExePathFromRegistry(); + if (!steamFolderOpt) + { + return {}; + } + + return fs::path(*steamFolderOpt).parent_path() / L"steamapps"; + } + + static fs::path GetAcfFilePath(const std::wstring& gameId) + { + auto steamFolderOpt = GetSteamExePathFromRegistry(); + if (!steamFolderOpt) + { + return {}; + } + + return GetSteamBasePath() / (L"appmanifest_" + gameId + L".acf"); + } + + static fs::path GetGameInstallPath(const std::wstring& gameFolderName) + { + auto steamFolderOpt = GetSteamExePathFromRegistry(); + if (!steamFolderOpt) + { + return {}; + } + + return GetSteamBasePath() / L"common" / gameFolderName; + } + + static unordered_map ParseAcfFile(const fs::path& acfPath) + { + unordered_map result; + + ifstream file(acfPath); + if (!file.is_open()) + return result; + + string line; + while (getline(file, line)) + { + smatch matches; + static const regex pattern(R"delim("([^"]+)"\s+"([^"]+)")delim"); + + if (regex_search(line, matches, pattern) && matches.size() == 3) + { + wstring key = Utf8ToWide(matches[1].str()); + wstring value = Utf8ToWide(matches[2].str()); + result[key] = value; + } + } + + return result; + } + + std::unique_ptr GetSteamGameInfoFromAcfFile(const std::wstring& gameId) + { + fs::path acfPath = Steam::GetAcfFilePath(gameId); + + if (!fs::exists(acfPath)) + return nullptr; + + auto kv = ParseAcfFile(acfPath); + if (kv.empty() || kv.find(L"installdir") == kv.end()) + return nullptr; + + fs::path gamePath = Steam::GetGameInstallPath(kv[L"installdir"]); + if (!fs::exists(gamePath)) + return nullptr; + + auto game = std::make_unique(); + game->gameId = gameId; + game->gameInstallationPath = gamePath.wstring(); + return game; + } + + std::wstring GetGameIdFromUrlProtocolPath(const std::wstring& urlPath) + { + const std::wstring steamGamePrefix = L"steam://rungameid/"; + + if (urlPath.rfind(steamGamePrefix, 0) == 0) + { + return urlPath.substr(steamGamePrefix.length()); + } + + return L""; + } + + } +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/SteamHelper.h b/src/modules/Workspaces/WorkspacesLib/SteamHelper.h new file mode 100644 index 0000000000..a80a942f4a --- /dev/null +++ b/src/modules/Workspaces/WorkspacesLib/SteamHelper.h @@ -0,0 +1,24 @@ +#pragma once + +#include "pch.h" + +namespace Utils +{ + namespace NonLocalizable + { + const std::wstring AcfFileNameTemplate = L"appmanifest_.acfs"; + } + + namespace Steam + { + struct SteamGame + { + std::wstring gameId; + std::wstring gameInstallationPath; + }; + + std::unique_ptr GetSteamGameInfoFromAcfFile(const std::wstring& gameId); + + std::wstring GetGameIdFromUrlProtocolPath(const std::wstring& urlPath); + } +} diff --git a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj index 27394e29ee..7d29741a0d 100644 --- a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj +++ b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj @@ -41,6 +41,7 @@ + @@ -57,6 +58,7 @@ Create + diff --git a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters index b066c16a57..f4f17c55ee 100644 --- a/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters +++ b/src/modules/Workspaces/WorkspacesLib/WorkspacesLib.vcxproj.filters @@ -53,6 +53,9 @@ Header Files + + Header Files + @@ -88,6 +91,9 @@ Source Files + + Source Files + diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp index 1d5bc8a179..a8b7c13108 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp @@ -71,6 +71,8 @@ namespace SnapshotUtils continue; } + Logger::info("Try to get window app:{}", reinterpret_cast(window)); + DWORD pid{}; GetWindowThreadProcessId(window, &pid); @@ -118,10 +120,19 @@ namespace SnapshotUtils auto data = Utils::Apps::GetApp(processPath, pid, installedApps); if (!data.has_value() || data->name.empty()) { - Logger::info(L"Installed app not found: {}", processPath); + Logger::info(L"Installed app not found:{},{}", reinterpret_cast(window), processPath); continue; } + if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window)) + { + // Only care about steam games if it has no thick frame to remain consistent with + // the behavior as before. + continue; + } + + Logger::info(L"Found app for window:{},{}", reinterpret_cast(window), processPath); + auto appData = data.value(); bool isEdge = appData.IsEdge(); diff --git a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp index 7b04135d1a..538579979f 100644 --- a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp +++ b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp @@ -200,6 +200,14 @@ std::optional WindowArranger::GetNearestWindow(const Workspa } auto data = Utils::Apps::GetApp(processPath, pid, m_installedApps); + + if (!data->IsSteamGame() && !WindowUtils::HasThickFrame(window)) + { + // Only care about steam games if it has no thick frame to remain consistent with + // the behavior as before. + continue; + } + if (!data.has_value()) { continue; diff --git a/src/modules/Workspaces/workspaces-common/WindowFilter.h b/src/modules/Workspaces/workspaces-common/WindowFilter.h index c76ad81237..8ae1a5411b 100644 --- a/src/modules/Workspaces/workspaces-common/WindowFilter.h +++ b/src/modules/Workspaces/workspaces-common/WindowFilter.h @@ -9,10 +9,12 @@ namespace WindowFilter { auto style = GetWindowLong(window, GWL_STYLE); bool isPopup = WindowUtils::HasStyle(style, WS_POPUP); - bool hasThickFrame = WindowUtils::HasStyle(style, WS_THICKFRAME); bool hasCaption = WindowUtils::HasStyle(style, WS_CAPTION); bool hasMinimizeMaximizeButtons = WindowUtils::HasStyle(style, WS_MINIMIZEBOX) || WindowUtils::HasStyle(style, WS_MAXIMIZEBOX); - if (isPopup && !(hasThickFrame && (hasCaption || hasMinimizeMaximizeButtons))) + + Logger::info("Style for window: {}, {:#x}", reinterpret_cast(window), style); + + if (isPopup && !(hasCaption || hasMinimizeMaximizeButtons)) { // popup windows we want to snap: e.g. Calculator, Telegram // popup windows we don't want to snap: start menu, notification popup, tray window, etc. diff --git a/src/modules/Workspaces/workspaces-common/WindowUtils.h b/src/modules/Workspaces/workspaces-common/WindowUtils.h index 8424591dfa..79051f4ea2 100644 --- a/src/modules/Workspaces/workspaces-common/WindowUtils.h +++ b/src/modules/Workspaces/workspaces-common/WindowUtils.h @@ -121,4 +121,11 @@ namespace WindowUtils return std::wstring(title); } + + + inline bool HasThickFrame(HWND window) + { + auto style = GetWindowLong(window, GWL_STYLE); + return WindowUtils::HasStyle(style, WS_THICKFRAME); + } } \ No newline at end of file diff --git a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.def b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.def new file mode 100644 index 0000000000..24e7c1235c --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.def @@ -0,0 +1,3 @@ +EXPORTS +DllCanUnloadNow = WINRT_CanUnloadNow PRIVATE +DllGetActivationFactory = WINRT_GetActivationFactory PRIVATE diff --git a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.rc b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.rc new file mode 100644 index 0000000000..5fa3c8b90d --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.rc @@ -0,0 +1,40 @@ +#include +#include "resource.h" +#include "../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +1 VERSIONINFO +FILEVERSION FILE_VERSION +PRODUCTVERSION PRODUCT_VERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG +FILEFLAGS VS_FF_DEBUG +#else +FILEFLAGS 0x0L +#endif +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset + END +END diff --git a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj new file mode 100644 index 0000000000..e110d736ae --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj @@ -0,0 +1,187 @@ + + + + + true + true + {5f63c743-f6ce-4dba-a200-2b3f8a14e8c2} + CmdPalKeyboardService + CmdPalKeyboardService + false + + + + + true + false + + + + + true + true + en-US + 17.0 + 10.0 + + + true + true + true + Windows Store + false + + + + <_VC_Target_Library_Platform>Desktop + <_NoWinAPIFamilyApp>true + + + + + + DynamicLibrary + v143 + Unicode + false + + + true + true + + + false + true + false + + + + + + + + + + + + + + + + + CmdPalKeyboardService + ..\..\..\..\$(Platform)\$(Configuration)\ + + + + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + _WINRT_DLL;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + ../../..;%(AdditionalIncludeDirectories) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + Console + false + CmdPalKeyboardService.def + Shell32.lib;user32.lib;%(AdditionalDependencies) + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + KeyboardListener.idl + + + + + + Create + + + KeyboardListener.idl + + + + + + + + + + + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + MultiThreadedDebug + + + + %(IgnoreSpecificDefaultLibraries);libucrtd.lib + %(AdditionalOptions) /defaultlib:ucrtd.lib + + + + + + MultiThreaded + + + + %(IgnoreSpecificDefaultLibraries);libucrt.lib + %(AdditionalOptions) /defaultlib:ucrt.lib + + + + + \ No newline at end of file diff --git a/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters new file mode 100644 index 0000000000..301ac57f3a --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/CmdPalKeyboardService.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + accd3aa8-1ba0-4223-9bbe-0c431709210b + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + {926ab91d-31b4-48c3-b9a4-e681349f27f0} + + + + + + + + + + + + + + + + + + + + + + + Resources + + + \ No newline at end of file diff --git a/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.cpp b/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.cpp new file mode 100644 index 0000000000..cf52fe1863 --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.cpp @@ -0,0 +1,165 @@ +#include "pch.h" +#include "KeyboardListener.h" +#include "KeyboardListener.g.cpp" + +// #include +// #include +#include + +namespace +{ +} + +namespace winrt::CmdPalKeyboardService::implementation +{ + KeyboardListener::KeyboardListener() + { + s_instance = this; + } + + void KeyboardListener::Start() + { +#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED) + const bool hook_disabled = IsDebuggerPresent(); +#else + const bool hook_disabled = false; +#endif + if (!hook_disabled) + { + if (!s_llKeyboardHook) + { + s_llKeyboardHook = SetWindowsHookExW(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, NULL); + if (!s_llKeyboardHook) + { + DWORD errorCode = GetLastError(); + show_last_error_message(L"SetWindowsHookEx", errorCode, L"CmdPalKeyboardService"); + } + } + } + } + + void KeyboardListener::Stop() + { + if (s_llKeyboardHook && UnhookWindowsHookEx(s_llKeyboardHook)) + { + s_llKeyboardHook = NULL; + } + } + + void KeyboardListener::SetHotkeyAction(bool win, bool ctrl, bool shift, bool alt, uint8_t key, hstring const& id) + { + Hotkey hotkey = { .win = win, .ctrl = ctrl, .shift = shift, .alt = alt, .key = key }; + std::unique_lock lock{ mutex }; + + HotkeyDescriptor desc = { .hotkey = hotkey, .id = std::wstring(id) }; + hotkeyDescriptors.insert(desc); + } + + void KeyboardListener::ClearHotkey(hstring const& id) + { + { + std::unique_lock lock{ mutex }; + auto it = hotkeyDescriptors.begin(); + while (it != hotkeyDescriptors.end()) + { + if (it->id == id) + { + it = hotkeyDescriptors.erase(it); + } + else + { + ++it; + } + } + } + } + + void KeyboardListener::ClearHotkeys() + { + { + std::unique_lock lock{ mutex }; + auto it = hotkeyDescriptors.begin(); + while (it != hotkeyDescriptors.end()) + { + it = hotkeyDescriptors.erase(it); + } + } + } + + void KeyboardListener::SetProcessCommand(ProcessCommand processCommand) + { + m_processCommandCb = [trigger = std::move(processCommand)](hstring const& id) { + trigger(id); + }; + } + + LRESULT KeyboardListener::DoLowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) + { + const auto& keyPressInfo = *reinterpret_cast(lParam); + + if ((wParam != WM_KEYDOWN) && (wParam != WM_SYSKEYDOWN)) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + Hotkey hotkey{ + .win = (GetAsyncKeyState(VK_LWIN) & 0x8000) || (GetAsyncKeyState(VK_RWIN) & 0x8000), + .ctrl = static_cast(GetAsyncKeyState(VK_CONTROL) & 0x8000), + .shift = static_cast(GetAsyncKeyState(VK_SHIFT) & 0x8000), + .alt = static_cast(GetAsyncKeyState(VK_MENU) & 0x8000), + .key = static_cast(keyPressInfo.vkCode) + }; + + if (hotkey == Hotkey{}) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + bool do_action = false; + std::wstring actionId{}; + + { + // Hold the lock for the shortest possible duration + std::unique_lock lock{ mutex }; + HotkeyDescriptor dummy{ .hotkey = hotkey }; + auto it = hotkeyDescriptors.find(dummy); + if (it != hotkeyDescriptors.end()) + { + do_action = true; + actionId = it->id; + } + } + + if (do_action) + { + m_processCommandCb(hstring{ actionId }); + + // After invoking the hotkey send a dummy key to prevent Start Menu from activating + INPUT dummyEvent[1] = {}; + dummyEvent[0].type = INPUT_KEYBOARD; + dummyEvent[0].ki.wVk = 0xFF; + dummyEvent[0].ki.dwFlags = KEYEVENTF_KEYUP; + SendInput(1, dummyEvent, sizeof(INPUT)); + + // Swallow the key press + return 1; + } + + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + LRESULT KeyboardListener::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) + { + if (s_instance == nullptr) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + if (nCode < 0) + { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + return s_instance->DoLowLevelKeyboardProc(nCode, wParam, lParam); + } +} diff --git a/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.h b/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.h new file mode 100644 index 0000000000..7cd82ba77c --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.h @@ -0,0 +1,67 @@ +#pragma once + +#include "KeyboardListener.g.h" +#include +#include +#include + +namespace winrt::CmdPalKeyboardService::implementation +{ + struct KeyboardListener : KeyboardListenerT + { + struct Hotkey + { + bool win = false; + bool ctrl = false; + bool shift = false; + bool alt = false; + unsigned char key = 0; + + std::strong_ordering operator<=>(const Hotkey&) const = default; + }; + + struct HotkeyDescriptor + { + Hotkey hotkey; + std::wstring id; + + bool operator<(const HotkeyDescriptor& other) const + { + return hotkey < other.hotkey; + }; + }; + + KeyboardListener(); + + void Start(); + void Stop(); + void SetHotkeyAction(bool win, bool ctrl, bool shift, bool alt, uint8_t key, hstring const& id); + void ClearHotkey(hstring const& id); + void ClearHotkeys(); + void SetProcessCommand(ProcessCommand processCommand); + + static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + + private: + LRESULT CALLBACK DoLowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); + + static inline KeyboardListener* s_instance; + HHOOK s_llKeyboardHook = nullptr; + + // Max DWORD for key code to disable keys. + const DWORD VK_DISABLED = 0x100; + DWORD vkCodePressed = VK_DISABLED; + + std::multiset hotkeyDescriptors; + std::mutex mutex; + + std::function m_processCommandCb; + }; +} + +namespace winrt::CmdPalKeyboardService::factory_implementation +{ + struct KeyboardListener : KeyboardListenerT + { + }; +} diff --git a/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.idl b/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.idl new file mode 100644 index 0000000000..8d808e386c --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/KeyboardListener.idl @@ -0,0 +1,16 @@ + +namespace CmdPalKeyboardService +{ + [version(1.0), uuid(78ab07cd-e128-4e73-86aa-e48e6b6d01ff)] delegate void ProcessCommand(String id); + + [default_interface] runtimeclass KeyboardListener { + KeyboardListener(); + void Start(); + void Stop(); + + void SetHotkeyAction(Boolean win, Boolean ctrl, Boolean shift, Boolean alt, UInt8 key, String id); + void ClearHotkey(String id); + void ClearHotkeys(); + void SetProcessCommand(ProcessCommand processCommand); + } +} diff --git a/src/modules/cmdpal/CmdPalKeyboardService/PropertySheet.props b/src/modules/cmdpal/CmdPalKeyboardService/PropertySheet.props new file mode 100644 index 0000000000..27ad40e537 --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/PropertySheet.props @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/src/modules/cmdpal/CmdPalKeyboardService/packages.config b/src/modules/cmdpal/CmdPalKeyboardService/packages.config new file mode 100644 index 0000000000..09bfc449e2 --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/modules/cmdpal/CmdPalKeyboardService/pch.cpp b/src/modules/cmdpal/CmdPalKeyboardService/pch.cpp new file mode 100644 index 0000000000..bcb5590be1 --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/modules/cmdpal/CmdPalKeyboardService/pch.h b/src/modules/cmdpal/CmdPalKeyboardService/pch.h new file mode 100644 index 0000000000..b10d0155ca --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/pch.h @@ -0,0 +1,4 @@ +#pragma once +#include +#include +#include diff --git a/src/modules/cmdpal/CmdPalKeyboardService/resource.h b/src/modules/cmdpal/CmdPalKeyboardService/resource.h new file mode 100644 index 0000000000..f99c3adb95 --- /dev/null +++ b/src/modules/cmdpal/CmdPalKeyboardService/resource.h @@ -0,0 +1,13 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by PowerToys.MeasureToolCore.rc + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "CmdPalKeyboardService" +#define INTERNAL_NAME "CmdPalKeyboardService" +#define ORIGINAL_FILENAME "CmdPalKeyboardService.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj index 4395e340fa..2d70013009 100644 --- a/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj +++ b/src/modules/cmdpal/CmdPalModuleInterface/CmdPalModuleInterface.vcxproj @@ -2,6 +2,7 @@ + 17.0 Win32Proj @@ -49,13 +50,20 @@ - EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + + EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL; + %(PreprocessorDefinitions); + + + IS_DEV_BRANDING;%(PreprocessorDefinitions) + ..\..\..\common\inc;..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories) $(OutDir)$(TargetName)$(TargetExt) + diff --git a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp index aceadabdd8..bff7279b68 100644 --- a/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp +++ b/src/modules/cmdpal/CmdPalModuleInterface/dllmain.cpp @@ -3,6 +3,7 @@ #include +#include #include #include #include @@ -10,10 +11,11 @@ #include #include #include +#include #include #include #include -#include +#include HINSTANCE g_hInst_cmdPal = 0; @@ -37,8 +39,6 @@ BOOL APIENTRY DllMain(HMODULE hInstance, class CmdPal : public PowertoyModuleIface { private: - bool m_enabled = false; - std::wstring app_name; //contains the non localized key of the powertoy @@ -46,7 +46,10 @@ private: HANDLE m_hTerminateEvent; - void LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated) + // Track if this is the first call to enable + bool firstEnableCall = true; + + static bool LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated, bool silentFail) { std::wstring dir = std::filesystem::path(appPath).parent_path(); @@ -54,6 +57,10 @@ private: sei.cbSize = sizeof(SHELLEXECUTEINFO); sei.hwnd = nullptr; sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; + if (silentFail) + { + sei.fMask = sei.fMask | SEE_MASK_FLAG_NO_UI; + } sei.lpVerb = elevated ? L"runas" : L"open"; sei.lpFile = appPath.c_str(); sei.lpParameters = commandLineArgs.c_str(); @@ -64,7 +71,11 @@ private: { std::wstring error = get_last_error_or_default(GetLastError()); Logger::error(L"Failed to launch process. {}", error); + return false; } + + m_launched.store(true); + return true; } std::vector GetProcessesIdByName(const std::wstring& processName) @@ -122,6 +133,9 @@ private: } public: + static std::atomic m_enabled; + static std::atomic m_launched; + CmdPal() { app_name = L"CmdPal"; @@ -133,10 +147,7 @@ public: ~CmdPal() { - if (m_enabled) - { - } - m_enabled = false; + CmdPal::m_enabled.store(false); } // Destroy the powertoy and free memory @@ -203,16 +214,23 @@ public: { Logger::trace("CmdPal::enable()"); - m_enabled = true; + CmdPal::m_enabled.store(true); - try + std::wstring packageName = L"Microsoft.CommandPalette"; + std::wstring launchPath = L"shell:AppsFolder\\Microsoft.CommandPalette_8wekyb3d8bbwe!App"; +#ifdef IS_DEV_BRANDING + packageName = L"Microsoft.CommandPalette.Dev"; + launchPath = L"shell:AppsFolder\\Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App"; +#endif + + if (!package::GetRegisteredPackage(packageName, false).has_value()) { - if (!package::GetRegisteredPackage(L"Microsoft.CommandPalette", false).has_value()) + try { Logger::info(L"CmdPal not installed. Installing..."); std::wstring installationFolder = get_module_folderpath(); -#if _DEBUG +#ifdef _DEBUG std::wstring archSubdir = L"x64"; #ifdef _M_ARM64 archSubdir = L"ARM64"; @@ -234,19 +252,34 @@ public: } } } - } - catch (std::exception& e) - { - std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " }; - errorMessage += e.what(); - Logger::error(errorMessage); + catch (std::exception& e) + { + std::string errorMessage{ "Exception thrown while trying to install CmdPal package: " }; + errorMessage += e.what(); + Logger::error(errorMessage); + } } -#if _DEBUG - LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette.Dev_8wekyb3d8bbwe!App", L"RunFromPT", false); -#else - LaunchApp(std::wstring{ L"shell:AppsFolder\\" } + L"Microsoft.CommandPalette_8wekyb3d8bbwe!App", L"RunFromPT", false); -#endif + if (!package::GetRegisteredPackage(packageName, false).has_value()) + { + Logger::error("Cmdpal is not registered, quit.."); + return; + } + + if (!firstEnableCall) + { + Logger::trace("Not first attempt, try to launch"); + LaunchApp(launchPath, L"RunFromPT", false /*no elevated*/, false /*error pop up*/); + } + else + { + // If first time enable, do retry launch. + Logger::trace("First attempt, try to launch"); + std::thread launchThread(&CmdPal::RetryLaunch, launchPath); + launchThread.detach(); + } + + firstEnableCall = false; } virtual void disable() @@ -254,7 +287,44 @@ public: Logger::trace("CmdPal::disable()"); TerminateCmdPal(); - m_enabled = false; + CmdPal::m_enabled.store(false); + } + + static void RetryLaunch(std::wstring path) + { + const int base_delay_milliseconds = 1000; + int max_retry = 9; // 2**9 - 1 seconds. Control total wait time within 10 min. + int retry = 0; + do + { + auto launch_result = LaunchApp(path, L"RunFromPT", false, retry < max_retry); + if (launch_result) + { + Logger::info(L"CmdPal launched successfully after {} retries.", retry); + return; + } + else + { + Logger::error(L"Retry {} launch CmdPal launch failed.", retry); + } + + // When we got max retry, we don't need to wait for the next retry. + if (retry < max_retry) + { + int delay = base_delay_milliseconds * (1 << (retry)); + std::this_thread::sleep_for(std::chrono::milliseconds(delay)); + } + ++retry; + } while (retry <= max_retry && m_enabled.load() && !m_launched.load()); + + if (!m_enabled.load() || m_launched.load()) + { + Logger::error(L"Retry cancelled. CmdPal is disabled or already launched."); + } + else + { + Logger::error(L"CmdPal launch failed after {} attempts.", retry); + } } virtual bool on_hotkey(size_t) override @@ -269,11 +339,14 @@ public: virtual bool is_enabled() override { - return m_enabled; + return CmdPal::m_enabled.load(); } }; +std::atomic CmdPal::m_enabled{ false }; +std::atomic CmdPal::m_launched{ false }; + extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() { return new CmdPal(); -} +} \ No newline at end of file diff --git a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props index 782ec68bf5..18f07f5f10 100644 --- a/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props +++ b/src/modules/cmdpal/ExtensionTemplate/TemplateCmdPalExtension/Directory.Packages.props @@ -9,7 +9,7 @@ - + diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/CalculatorCommandProvider.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/CalculatorCommandProvider.cs deleted file mode 100644 index 04d474a31a..0000000000 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/CalculatorCommandProvider.cs +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Data; -using System.Globalization; -using System.Text; -using Microsoft.CmdPal.Ext.Calc.Properties; -using Microsoft.CommandPalette.Extensions; -using Microsoft.CommandPalette.Extensions.Toolkit; -using Windows.Foundation; - -namespace Microsoft.CmdPal.Ext.Calc; - -public partial class CalculatorCommandProvider : CommandProvider -{ - private readonly ListItem _listItem = new(new CalculatorListPage()) { Subtitle = Resources.calculator_top_level_subtitle }; - private readonly FallbackCalculatorItem _fallback = new(); - - public CalculatorCommandProvider() - { - Id = "Calculator"; - DisplayName = Resources.calculator_display_name; - Icon = IconHelpers.FromRelativePath("Assets\\Calculator.svg"); - } - - public override ICommandItem[] TopLevelCommands() => [_listItem]; - - public override IFallbackCommandItem[] FallbackCommands() => [_fallback]; -} - -// The calculator page is a dynamic list page -// * The first command is where we display the results. Title=result, Subtitle=query -// - The default command is `SaveCommand`. -// - When you save, insert into list at spot 1 -// - change SearchText to the result -// - MoreCommands: a single `CopyCommand` to copy the result to the clipboard -// * The rest of the items are previously saved results -// - Command is a CopyCommand -// - Each item also sets the TextToSuggest to the result -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "This is sample code")] -public sealed partial class CalculatorListPage : DynamicListPage -{ - private readonly List _items = []; - private readonly SaveCommand _saveCommand = new(); - private readonly CopyTextCommand _copyContextCommand; - private readonly CommandContextItem _copyContextMenuItem; - private static readonly CompositeFormat ErrorMessage = System.Text.CompositeFormat.Parse(Properties.Resources.calculator_error); - - public CalculatorListPage() - { - Icon = IconHelpers.FromRelativePath("Assets\\Calculator.svg"); - Name = Resources.calculator_title; - PlaceholderText = Resources.calculator_placeholder_text; - Id = "com.microsoft.cmdpal.calculator"; - - _copyContextCommand = new CopyTextCommand(string.Empty); - _copyContextMenuItem = new CommandContextItem(_copyContextCommand); - - _items.Add(new(_saveCommand) { Icon = new IconInfo("\uE94E") }); - - UpdateSearchText(string.Empty, string.Empty); - - _saveCommand.SaveRequested += HandleSave; - } - - private void HandleSave(object sender, object args) - { - var lastResult = _items[0].Title; - if (!string.IsNullOrEmpty(lastResult)) - { - var li = new ListItem(new CopyTextCommand(lastResult)) - { - Title = _items[0].Title, - Subtitle = _items[0].Subtitle, - TextToSuggest = lastResult, - }; - _items.Insert(1, li); - _items[0].Subtitle = string.Empty; - SearchText = lastResult; - this.RaiseItemsChanged(this._items.Count); - } - } - - public override void UpdateSearchText(string oldSearch, string newSearch) - { - var firstItem = _items[0]; - if (string.IsNullOrEmpty(newSearch)) - { - firstItem.Title = Resources.calculator_placeholder_text; - firstItem.Subtitle = string.Empty; - firstItem.MoreCommands = []; - } - else - { - _copyContextCommand.Text = ParseQuery(newSearch, out var result) ? result : string.Empty; - firstItem.Title = result; - firstItem.Subtitle = newSearch; - firstItem.MoreCommands = [_copyContextMenuItem]; - } - } - - internal static bool ParseQuery(string equation, out string result) - { - try - { - var resultNumber = new DataTable().Compute(equation, null); - result = resultNumber.ToString() ?? string.Empty; - return true; - } - catch (Exception e) - { - result = string.Format(CultureInfo.CurrentCulture, ErrorMessage, e.Message); - return false; - } - } - - public override IListItem[] GetItems() => _items.ToArray(); -} - -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "This is sample code")] -public sealed partial class SaveCommand : InvokableCommand -{ - public event TypedEventHandler SaveRequested; - - public SaveCommand() - { - Name = Resources.calculator_save_command_name; - } - - public override ICommandResult Invoke() - { - SaveRequested?.Invoke(this, this); - return CommandResult.KeepOpen(); - } -} - -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "This is sample code")] -internal sealed partial class FallbackCalculatorItem : FallbackCommandItem -{ - private readonly CopyTextCommand _copyCommand = new(string.Empty); - private static readonly IconInfo _cachedIcon = IconHelpers.FromRelativePath("Assets\\Calculator.svg"); - - public FallbackCalculatorItem() - : base(new NoOpCommand(), Resources.calculator_title) - { - Command = _copyCommand; - _copyCommand.Name = string.Empty; - Title = string.Empty; - Subtitle = Resources.calculator_placeholder_text; - Icon = _cachedIcon; - } - - public override void UpdateQuery(string query) - { - if (CalculatorListPage.ParseQuery(query, out var result)) - { - _copyCommand.Text = result; - _copyCommand.Name = string.IsNullOrWhiteSpace(query) ? string.Empty : Resources.calculator_copy_command_name; - Title = result; - - // we have to make the subtitle the equation, - // so that we will still string match the original query - // Otherwise, something like 1+2 will have a title of "3" and not match - Subtitle = query; - } - else - { - _copyCommand.Text = string.Empty; - _copyCommand.Name = string.Empty; - Title = string.Empty; - Subtitle = string.Empty; - } - } -} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/SystemCommandsCache.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/SystemCommandsCache.cs deleted file mode 100644 index a2a61b2b50..0000000000 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/SystemCommandsCache.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Collections.Generic; -using System.Threading.Tasks; -using Microsoft.CmdPal.Ext.System.Helpers; -using Microsoft.CommandPalette.Extensions; - -namespace Microsoft.CmdPal.Ext.System; - -public sealed partial class SystemCommandsCache -{ - public SystemCommandsCache(SettingsManager manager) - { - var list = new List(); - var listLock = new object(); - - var a = Task.Run(() => - { - var isBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi; - - var separateEmptyRB = manager.HideEmptyRecycleBin; - var confirmSystemCommands = manager.ShowDialogToConfirmCommand; - var showSuccessOnEmptyRB = manager.ShowSuccessMessageAfterEmptyingRecycleBin; - - // normal system commands are fast and can be returned immediately - var systemCommands = Commands.GetSystemCommands(isBootedInUefiMode, separateEmptyRB, confirmSystemCommands, showSuccessOnEmptyRB); - lock (listLock) - { - list.AddRange(systemCommands); - } - }); - - var b = Task.Run(() => - { - // Network (ip and mac) results are slow with many network cards and returned delayed. - // On global queries the first word/part has to be 'ip', 'mac' or 'address' for network results - var networkConnectionResults = Commands.GetNetworkConnectionResults(manager); - lock (listLock) - { - list.AddRange(networkConnectionResults); - } - }); - - Task.WaitAll(a, b); - CachedCommands = list.ToArray(); - } - - public IListItem[] CachedCommands { get; } -} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeAndDateHelper.cs b/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeAndDateHelper.cs deleted file mode 100644 index 3450eda627..0000000000 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeAndDateHelper.cs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Globalization; -using System.Text.RegularExpressions; - -namespace Microsoft.CmdPal.Ext.TimeDate.Helpers; - -internal static class TimeAndDateHelper -{ - /// - /// Get the format for the time string - /// - /// Type of format - /// Show date with weekday and name of month (long format) - /// Show time with seconds (long format) - /// String that identifies the time/date format () - internal static string GetStringFormat(FormatStringType targetFormat, bool timeLong, bool dateLong) - { - switch (targetFormat) - { - case FormatStringType.Time: - return timeLong ? "T" : "t"; - case FormatStringType.Date: - return dateLong ? "D" : "d"; - case FormatStringType.DateTime: - if (timeLong & dateLong) - { - return "F"; // Friday, October 31, 2008 5:04:32 PM - } - else if (timeLong & !dateLong) - { - return "G"; // 10/31/2008 5:04:32 PM - } - else if (!timeLong & dateLong) - { - return "f"; // Friday, October 31, 2008 5:04 PM - } - else - { - // (!timeLong & !dateLong) - return "g"; // 10/31/2008 5:04 PM - } - - default: - return string.Empty; // Windows default based on current culture settings - } - } - - /// - /// Returns the number week in the month (Used code from 'David Morton' from ) - /// - /// date - /// Number of week in the month - internal static int GetWeekOfMonth(DateTime date, DayOfWeek formatSettingFirstDayOfWeek) - { - var beginningOfMonth = new DateTime(date.Year, date.Month, 1); - var adjustment = 1; // We count from 1 to 7 and not from 0 to 6 - - while (date.Date.AddDays(1).DayOfWeek != formatSettingFirstDayOfWeek) - { - date = date.AddDays(1); - } - - return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + adjustment; - } - - /// - /// Returns the number of the day in the week - /// - /// Date - /// Number of the day in the week - internal static int GetNumberOfDayInWeek(DateTime date, DayOfWeek formatSettingFirstDayOfWeek) - { - var daysInWeek = 7; - var adjustment = 1; // We count from 1 to 7 and not from 0 to 6 - - return ((date.DayOfWeek + daysInWeek - formatSettingFirstDayOfWeek) % daysInWeek) + adjustment; - } - - /// - /// Convert input string to a object in local time - /// - /// String with date/time - /// The new object - /// True on success, otherwise false - internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp) - { - if (DateTime.TryParse(input, out timestamp)) - { - // Known date/time format - return true; - } - else if (Regex.IsMatch(input, @"^u[\+-]?\d{1,10}$") && long.TryParse(input.TrimStart('u'), out var secondsU)) - { - // Unix time stamp - // We use long instead of int, because int is too small after 03:14:07 UTC 2038-01-19 - timestamp = DateTimeOffset.FromUnixTimeSeconds(secondsU).LocalDateTime; - return true; - } - else if (Regex.IsMatch(input, @"^ums[\+-]?\d{1,13}$") && long.TryParse(input.TrimStart("ums".ToCharArray()), out var millisecondsUms)) - { - // Unix time stamp in milliseconds - // We use long instead of int because int is too small after 03:14:07 UTC 2038-01-19 - timestamp = DateTimeOffset.FromUnixTimeMilliseconds(millisecondsUms).LocalDateTime; - return true; - } - else if (Regex.IsMatch(input, @"^ft\d+$") && long.TryParse(input.TrimStart("ft".ToCharArray()), out var secondsFt)) - { - // Windows file time - // DateTime.FromFileTime returns as local time. - timestamp = DateTime.FromFileTime(secondsFt); - return true; - } - else - { - timestamp = new DateTime(1, 1, 1, 1, 1, 1); - return false; - } - } - - /// - /// Test if input is special parsing for Unix time, Unix time in milliseconds or File time. - /// - /// String with date/time - /// True if yes, otherwise false - internal static bool IsSpecialInputParsing(string input) - { - return Regex.IsMatch(input, @"^.*(u|ums|ft)\d"); - } - - /// - /// Returns a CalendarWeekRule enum value based on the plugin setting. - /// - internal static CalendarWeekRule GetCalendarWeekRule(int pluginSetting) - { - switch (pluginSetting) - { - case 0: - return CalendarWeekRule.FirstDay; - case 1: - return CalendarWeekRule.FirstFullWeek; - case 2: - return CalendarWeekRule.FirstFourDayWeek; - default: - // Wrong json value and system setting (-1). - return DateTimeFormatInfo.CurrentInfo.CalendarWeekRule; - } - } - - /// - /// Returns a DayOfWeek enum value based on the FirstDayOfWeek plugin setting. - /// - internal static DayOfWeek GetFirstDayOfWeek(int pluginSetting) - { - switch (pluginSetting) - { - case 0: - return DayOfWeek.Sunday; - case 1: - return DayOfWeek.Monday; - case 2: - return DayOfWeek.Tuesday; - case 3: - return DayOfWeek.Wednesday; - case 4: - return DayOfWeek.Thursday; - case 5: - return DayOfWeek.Friday; - case 6: - return DayOfWeek.Saturday; - default: - // Wrong json value and system setting (-1). - return DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek; - } - } -} - -/// -/// Type of time/date format -/// -internal enum FormatStringType -{ - Time, - Date, - DateTime, -} diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/EvilSamplesPage.cs b/src/modules/cmdpal/Exts/SamplePagesExtension/EvilSamplesPage.cs deleted file mode 100644 index 373a1f7891..0000000000 --- a/src/modules/cmdpal/Exts/SamplePagesExtension/EvilSamplesPage.cs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CommandPalette.Extensions; -using Microsoft.CommandPalette.Extensions.Toolkit; - -namespace SamplePagesExtension; - -public partial class EvilSamplesPage : ListPage -{ - private readonly IListItem[] _commands = [ - new ListItem(new EvilSampleListPage()) - { - Title = "List Page without items", - Subtitle = "Throws exception on GetItems", - }, - new ListItem(new ExplodeInFiveSeconds(false)) - { - Title = "Page that will throw an exception after loading it", - Subtitle = "Throws exception on GetItems _after_ a ItemsChanged", - }, - new ListItem(new ExplodeInFiveSeconds(true)) - { - Title = "Page that keeps throwing exceptions", - Subtitle = "Will throw every 5 seconds once you open it", - }, - new ListItem(new ExplodeOnPropChange()) - { - Title = "Throw in the middle of a PropChanged", - Subtitle = "Will throw every 5 seconds once you open it", - }, - new ListItem(new SelfImmolateCommand()) - { - Title = "Terminate this extension", - Subtitle = "Will exit this extension (while it's loaded!)", - }, - new ListItem(new NoOpCommand()) - { - Title = "I have lots of nulls", - Subtitle = null, - MoreCommands = null, - Tags = null, - Details = new Details() - { - Title = null, - HeroImage = null, - Metadata = null, - }, - }, - new ListItem(new NoOpCommand()) - { - Title = "I also have nulls", - Subtitle = null, - MoreCommands = null, - Details = new Details() - { - Title = null, - HeroImage = null, - Metadata = [new DetailsElement() { Key = "Oops all nulls", Data = new DetailsTags() { Tags = null } }], - }, - }, - new ListItem(new AnonymousCommand(action: () => - { - ToastStatusMessage toast = new("I should appear immediately"); - toast.Show(); - Thread.Sleep(5000); - }) { Result = CommandResult.KeepOpen() }) - { - Title = "I take just forever to return something", - Subtitle = "The toast should appear immediately.", - MoreCommands = null, - Details = new Details() - { - Body = "This is a test for GH#512. If it doesn't appear immediately, it's likely InvokeCommand is happening on the UI thread.", - }, - } - ]; - - public EvilSamplesPage() - { - Name = "Evil Samples"; - Icon = new IconInfo("👿"); // Info - } - - public override IListItem[] GetItems() => _commands; -} - -[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Sample code")] -internal sealed partial class ExplodeOnPropChange : ListPage -{ - private bool _explode; - - public override string Title - { - get => _explode ? Commands[9001].Title : base.Title; - set => base.Title = value; - } - - private IListItem[] Commands => [ - new ListItem(new NoOpCommand()) - { - Title = "This page will explode in five seconds!", - Subtitle = "I'll change my Name, then explode", - }, - ]; - - public ExplodeOnPropChange() - { - Icon = new IconInfo(string.Empty); - Name = "Open"; - } - - public override IListItem[] GetItems() - { - _ = Task.Run(() => - { - Thread.Sleep(1000); - Title = "Ready? 3..."; - Thread.Sleep(1000); - Title = "Ready? 2..."; - Thread.Sleep(1000); - Title = "Ready? 1..."; - Thread.Sleep(1000); - _explode = true; - Title = "boom"; - }); - return Commands; - } -} diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleListPage.cs b/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleListPage.cs deleted file mode 100644 index 3f65bef942..0000000000 --- a/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleListPage.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CommandPalette.Extensions; -using Microsoft.CommandPalette.Extensions.Toolkit; - -namespace SamplePagesExtension; - -internal sealed partial class SampleListPage : ListPage -{ - public SampleListPage() - { - Icon = new IconInfo("\uEA37"); - Name = "Sample List Page"; - } - - public override IListItem[] GetItems() - { - var confirmOnceArgs = new ConfirmationArgs - { - PrimaryCommand = new AnonymousCommand( - () => - { - var t = new ToastStatusMessage("The dialog was confirmed"); - t.Show(); - }) - { - Name = "Confirm", - Result = CommandResult.KeepOpen(), - }, - Title = "You can set a title for the dialog", - Description = "Are you really sure you want to do the thing?", - }; - var confirmTwiceArgs = new ConfirmationArgs - { - PrimaryCommand = new AnonymousCommand(() => { }) - { - Name = "How sure are you?", - Result = CommandResult.Confirm(confirmOnceArgs), - }, - Title = "You can ask twice too", - Description = "You probably don't want to though, that'd be annoying.", - }; - - return [ - new ListItem(new NoOpCommand()) - { - Title = "This is a basic item in the list", - Subtitle = "I don't do anything though", - }, - new ListItem(new SampleListPageWithDetails()) - { - Title = "This item will take you to another page", - Subtitle = "This allows for nested lists of items", - }, - new ListItem(new OpenUrlCommand("https://github.com/microsoft/powertoys")) - { - Title = "Or you can go to links", - Subtitle = "This takes you to the PowerToys repo on GitHub", - }, - new ListItem(new SampleMarkdownPage()) - { - Title = "Items can have tags", - Subtitle = "and I'll take you to a page with markdown content", - Tags = [new Tag("Sample Tag")], - }, - new ListItem(new SendMessageCommand()) - { - Title = "I send lots of messages", - Subtitle = "Status messages can be used to provide feedback to the user in-app", - }, - new SendSingleMessageItem(), - new ListItem(new IndeterminateProgressMessageCommand()) - { - Title = "Do a thing with a spinner", - Subtitle = "Messages can have progress spinners, to indicate something is happening in the background", - }, - new ListItem( - new AnonymousCommand(() => { }) - { - Result = CommandResult.Confirm(confirmOnceArgs), - }) - { - Title = "Confirm before doing something", - }, - new ListItem( - new AnonymousCommand(() => { }) - { - Result = CommandResult.Confirm(confirmTwiceArgs), - }) - { - Title = "Confirm twice before doing something", - } - ]; - } -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Contracts/IFileService.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Contracts/IFileService.cs deleted file mode 100644 index b9348eb520..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Contracts/IFileService.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CmdPal.Common.Contracts; - -public interface IFileService -{ - T Read(string folderPath, string fileName); - - void Save(string folderPath, string fileName, T content); - - void Delete(string folderPath, string fileName); -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Contracts/ILocalSettingsService.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Contracts/ILocalSettingsService.cs deleted file mode 100644 index 2350050e3e..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Contracts/ILocalSettingsService.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Threading.Tasks; - -namespace Microsoft.CmdPal.Common.Contracts; - -public interface ILocalSettingsService -{ - Task HasSettingAsync(string key); - - Task ReadSettingAsync(string key); - - Task SaveSettingAsync(string key, T value); -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Extensions/ApplicationExtensions.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Extensions/ApplicationExtensions.cs deleted file mode 100644 index a975083c7c..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Extensions/ApplicationExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CmdPal.Common.Services; -using Microsoft.UI.Xaml; - -namespace Microsoft.CmdPal.Common.Extensions; - -/// -/// Extension class implementing extension methods for . -/// -public static class ApplicationExtensions -{ - /// - /// Get registered services at the application level from anywhere in the - /// application. - /// - /// Note: - /// https://learn.microsoft.com/uwp/api/windows.ui.xaml.application.current?view=winrt-22621#windows-ui-xaml-application-current - /// "Application is a singleton that implements the static Current property - /// to provide shared access to the Application instance for the current - /// application. The singleton pattern ensures that state managed by - /// Application, including shared resources and properties, is available - /// from a single, shared location." - /// - /// Example of usage: - /// - /// Application.Current.GetService() - /// - /// - /// Service type. - /// Current application. - /// Service reference. - public static T GetService(this Application application) - where T : class - { - return (application as IApp)!.GetService(); - } -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Extensions/IHostExtensions.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Extensions/IHostExtensions.cs deleted file mode 100644 index 660dcd2931..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Extensions/IHostExtensions.cs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Microsoft.CmdPal.Common.Extensions; - -public static class IHostExtensions -{ - /// - /// - /// - public static T CreateInstance(this IHost host, params object[] parameters) - { - return ActivatorUtilities.CreateInstance(host.Services, parameters); - } - - /// - /// Gets the service object for the specified type, or throws an exception - /// if type was not registered. - /// - /// Service type - /// Host object - /// Service object - /// Throw an exception if the specified - /// type is not registered - public static T GetService(this IHost host) - where T : class - { - if (host.Services.GetService(typeof(T)) is not T service) - { - throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices."); - } - - return service; - } -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/Json.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/Json.cs deleted file mode 100644 index d865e10bdb..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/Json.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; - -namespace Microsoft.CmdPal.Common.Helpers; - -public static class Json -{ - public static async Task ToObjectAsync(string value) - { - if (typeof(T) == typeof(bool)) - { - return (T)(object)bool.Parse(value); - } - - await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(value)); - return (await JsonSerializer.DeserializeAsync(stream))!; - } - - public static async Task StringifyAsync(T value) - { - if (typeof(T) == typeof(bool)) - { - return value!.ToString()!.ToLowerInvariant(); - } - - await using var stream = new MemoryStream(); - await JsonSerializer.SerializeAsync(stream, value); - return Encoding.UTF8.GetString(stream.ToArray()); - } -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/NativeEventWaiter.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/NativeEventWaiter.cs index 6ec1885a4c..2344fbb917 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/NativeEventWaiter.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/NativeEventWaiter.cs @@ -9,7 +9,7 @@ using Microsoft.UI.Dispatching; namespace Microsoft.CmdPal.Common.Helpers; -public static class NativeEventWaiter +public static partial class NativeEventWaiter { public static void WaitForEventLoop(string eventName, Action callback) { diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/RuntimeHelper.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/RuntimeHelper.cs index 46dce07e5e..342667cf83 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/RuntimeHelper.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Helpers/RuntimeHelper.cs @@ -9,7 +9,7 @@ using Windows.Win32.Foundation; namespace Microsoft.CmdPal.Common.Helpers; -public static class RuntimeHelper +public static partial class RuntimeHelper { public static bool IsMSIX { diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Messages/HideWindowMessage.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Messages/HideWindowMessage.cs index ed698d1024..097aefdee9 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Messages/HideWindowMessage.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Messages/HideWindowMessage.cs @@ -4,6 +4,6 @@ namespace Microsoft.CmdPal.Common.Messages; -public record HideWindowMessage() +public partial record HideWindowMessage() { } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj b/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj index 970df0df58..5f83ca54e1 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/Microsoft.CmdPal.Common.csproj @@ -1,5 +1,6 @@ + Microsoft.CmdPal.Common enable diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Models/LocalSettingsOptions.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Models/LocalSettingsOptions.cs deleted file mode 100644 index bae7422878..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Models/LocalSettingsOptions.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CmdPal.Common.Models; - -public class LocalSettingsOptions -{ - public string? ApplicationDataFolder - { - get; set; - } - - public string? LocalSettingsFile - { - get; set; - } -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt b/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt index 0d456bde31..996bbd7153 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt +++ b/src/modules/cmdpal/Microsoft.CmdPal.Common/NativeMethods.txt @@ -1,16 +1,7 @@ -EnableWindow -CoCreateInstance -FileOpenDialog -FileSaveDialog -IFileOpenDialog -IFileSaveDialog -SHCreateItemFromParsingName GetCurrentPackageFullName SetWindowLong GetWindowLong WINDOW_EX_STYLE -SHLoadIndirectString -StrFormatByteSizeEx SFBS_FLAGS MAX_PATH GetDpiForWindow diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/FileService.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/FileService.cs deleted file mode 100644 index cc6ef96098..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/FileService.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.IO; -using System.Text; -using System.Text.Json; -using Microsoft.CmdPal.Common.Contracts; - -namespace Microsoft.CmdPal.Common.Services; - -public class FileService : IFileService -{ - private static readonly Encoding _encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); - -#pragma warning disable CS8603 // Possible null reference return. - public T Read(string folderPath, string fileName) - { - var path = Path.Combine(folderPath, fileName); - if (File.Exists(path)) - { - using var fileStream = File.OpenText(path); - return JsonSerializer.Deserialize(fileStream.BaseStream); - } - - return default; - } -#pragma warning restore CS8603 // Possible null reference return. - - public void Save(string folderPath, string fileName, T content) - { - if (!Directory.Exists(folderPath)) - { - Directory.CreateDirectory(folderPath); - } - - var fileContent = JsonSerializer.Serialize(content); - File.WriteAllText(Path.Combine(folderPath, fileName), fileContent, _encoding); - } - - public void Delete(string folderPath, string fileName) - { - if (fileName != null && File.Exists(Path.Combine(folderPath, fileName))) - { - File.Delete(Path.Combine(folderPath, fileName)); - } - } -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/IApp.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/IApp.cs deleted file mode 100644 index 92980dfaff..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/IApp.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CmdPal.Common.Services; - -/// -/// Interface for the current application singleton object exposing the API -/// that can be accessed from anywhere in the application. -/// -public interface IApp -{ - /// - /// Gets services registered at the application level. - /// - public T GetService() - where T : class; -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/LocalSettingsService.cs b/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/LocalSettingsService.cs deleted file mode 100644 index e4cd2a174b..0000000000 --- a/src/modules/cmdpal/Microsoft.CmdPal.Common/Services/LocalSettingsService.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Microsoft.CmdPal.Common.Contracts; -using Microsoft.CmdPal.Common.Helpers; -using Microsoft.CmdPal.Common.Models; -using Microsoft.Extensions.Options; -using Windows.Storage; - -namespace Microsoft.CmdPal.Common.Services; - -public class LocalSettingsService : ILocalSettingsService -{ - // TODO! for now, we're hardcoding the path as effectively: - // %localappdata%\CmdPal\LocalSettings.json - private const string DefaultApplicationDataFolder = "CmdPal"; - private const string DefaultLocalSettingsFile = "LocalSettings.json"; - - private readonly IFileService _fileService; - private readonly LocalSettingsOptions _options; - - private readonly string _localApplicationData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - private readonly string _applicationDataFolder; - private readonly string _localSettingsFile; - - private readonly bool _isMsix; - - private Dictionary _settings; - private bool _isInitialized; - - public LocalSettingsService(IFileService fileService, IOptions options) - { - _isMsix = false; // RuntimeHelper.IsMSIX; - - _fileService = fileService; - _options = options.Value; - - _applicationDataFolder = Path.Combine(_localApplicationData, _options.ApplicationDataFolder ?? DefaultApplicationDataFolder); - _localSettingsFile = _options.LocalSettingsFile ?? DefaultLocalSettingsFile; - - _settings = new Dictionary(); - } - - private async Task InitializeAsync() - { - if (!_isInitialized) - { - _settings = await Task.Run(() => _fileService.Read>(_applicationDataFolder, _localSettingsFile)) ?? new Dictionary(); - - _isInitialized = true; - } - } - - public async Task HasSettingAsync(string key) - { - if (_isMsix) - { - return ApplicationData.Current.LocalSettings.Values.ContainsKey(key); - } - else - { - await InitializeAsync(); - - if (_settings != null) - { - return _settings.ContainsKey(key); - } - } - - return false; - } - - public async Task ReadSettingAsync(string key) - { - if (_isMsix) - { - if (ApplicationData.Current.LocalSettings.Values.TryGetValue(key, out var obj)) - { - return await Json.ToObjectAsync((string)obj); - } - } - else - { - await InitializeAsync(); - - if (_settings != null && _settings.TryGetValue(key, out var obj)) - { - var s = obj.ToString(); - - if (s != null) - { - return await Json.ToObjectAsync(s); - } - } - } - - return default; - } - - public async Task SaveSettingAsync(string key, T value) - { - if (_isMsix) - { - ApplicationData.Current.LocalSettings.Values[key] = await Json.StringifyAsync(value!); - } - else - { - await InitializeAsync(); - - _settings[key] = await Json.StringifyAsync(value!); - - await Task.Run(() => _fileService.Save(_applicationDataFolder, _localSettingsFile, _settings)); - } - } -} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/AppStateModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/AppStateModel.cs index 69d38a8655..649e49fbc7 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/AppStateModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/AppStateModel.cs @@ -49,7 +49,7 @@ public partial class AppStateModel : ObservableObject // Read the JSON content from the file var jsonContent = File.ReadAllText(FilePath); - var loaded = JsonSerializer.Deserialize(jsonContent, _deserializerOptions); + var loaded = JsonSerializer.Deserialize(jsonContent, JsonSerializationContext.Default.AppStateModel); Debug.WriteLine(loaded != null ? "Loaded settings file" : "Failed to parse"); @@ -73,7 +73,7 @@ public partial class AppStateModel : ObservableObject try { // Serialize the main dictionary to JSON and save it to the file - var settingsJson = JsonSerializer.Serialize(model, _serializerOptions); + var settingsJson = JsonSerializer.Serialize(model, JsonSerializationContext.Default.AppStateModel); // Is it valid JSON? if (JsonNode.Parse(settingsJson) is JsonObject newSettings) @@ -89,7 +89,7 @@ public partial class AppStateModel : ObservableObject savedSettings[item.Key] = item.Value != null ? item.Value.DeepClone() : null; } - var serialized = savedSettings.ToJsonString(_serializerOptions); + var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.AppStateModel.Options); File.WriteAllText(FilePath, serialized); // TODO: Instead of just raising the event here, we should @@ -122,18 +122,19 @@ public partial class AppStateModel : ObservableObject return Path.Combine(directory, "state.json"); } - private static readonly JsonSerializerOptions _serializerOptions = new() - { - WriteIndented = true, - Converters = { new JsonStringEnumConverter() }, - }; + // [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "")] + // private static readonly JsonSerializerOptions _serializerOptions = new() + // { + // WriteIndented = true, + // Converters = { new JsonStringEnumConverter() }, + // }; - private static readonly JsonSerializerOptions _deserializerOptions = new() - { - PropertyNameCaseInsensitive = true, - IncludeFields = true, - AllowTrailingCommas = true, - PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, - ReadCommentHandling = JsonCommentHandling.Skip, - }; + // private static readonly JsonSerializerOptions _deserializerOptions = new() + // { + // PropertyNameCaseInsensitive = true, + // IncludeFields = true, + // AllowTrailingCommas = true, + // PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate, + // ReadCommentHandling = JsonCommentHandling.Skip, + // }; } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandBarViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandBarViewModel.cs index 9b5be8a973..06f55ccf02 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandBarViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandBarViewModel.cs @@ -4,9 +4,9 @@ using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Messaging; using Microsoft.CmdPal.UI.ViewModels.Messages; +using Windows.System; namespace Microsoft.CmdPal.UI.ViewModels; @@ -25,6 +25,8 @@ public partial class CommandBarViewModel : ObservableObject, field = value; SetSelectedItem(value); + + OnPropertyChanged(nameof(SelectedItem)); } } @@ -47,7 +49,9 @@ public partial class CommandBarViewModel : ObservableObject, public partial PageViewModel? CurrentPage { get; set; } [ObservableProperty] - public partial ObservableCollection ContextCommands { get; set; } = []; + public partial ObservableCollection ContextMenuStack { get; set; } = []; + + public ContextMenuStackViewModel? ContextMenu => ContextMenuStack.LastOrDefault(); public CommandBarViewModel() { @@ -100,35 +104,97 @@ public partial class CommandBarViewModel : ObservableObject, if (SelectedItem.MoreCommands.Count() > 1) { ShouldShowContextMenu = true; - ContextCommands = [.. SelectedItem.AllCommands]; + + ContextMenuStack.Clear(); + ContextMenuStack.Add(new ContextMenuStackViewModel(SelectedItem)); + OnPropertyChanged(nameof(ContextMenu)); } else { ShouldShowContextMenu = false; } + + OnPropertyChanged(nameof(HasSecondaryCommand)); + OnPropertyChanged(nameof(SecondaryCommand)); + OnPropertyChanged(nameof(ShouldShowContextMenu)); } // InvokeItemCommand is what this will be in Xaml due to source generator // this comes in when an item in the list is tapped - [RelayCommand] - private void InvokeItem(CommandContextItemViewModel item) => - WeakReferenceMessenger.Default.Send(new(item.Command.Model, item.Model)); + // [RelayCommand] + public ContextKeybindingResult InvokeItem(CommandContextItemViewModel item) => + PerformCommand(item); // this comes in when the primary button is tapped public void InvokePrimaryCommand() { - if (PrimaryCommand != null) - { - WeakReferenceMessenger.Default.Send(new(PrimaryCommand.Command.Model, PrimaryCommand.Model)); - } + PerformCommand(SecondaryCommand); } // this comes in when the secondary button is tapped public void InvokeSecondaryCommand() { - if (SecondaryCommand != null) + PerformCommand(SecondaryCommand); + } + + public ContextKeybindingResult CheckKeybinding(bool ctrl, bool alt, bool shift, bool win, VirtualKey key) + { + var matchedItem = ContextMenu?.CheckKeybinding(ctrl, alt, shift, win, key); + return matchedItem != null ? PerformCommand(matchedItem) : ContextKeybindingResult.Unhandled; + } + + private ContextKeybindingResult PerformCommand(CommandItemViewModel? command) + { + if (command == null) { - WeakReferenceMessenger.Default.Send(new(SecondaryCommand.Command.Model, SecondaryCommand.Model)); + return ContextKeybindingResult.Unhandled; + } + + if (command.HasMoreCommands) + { + ContextMenuStack.Add(new ContextMenuStackViewModel(command)); + OnPropertyChanging(nameof(ContextMenu)); + OnPropertyChanged(nameof(ContextMenu)); + return ContextKeybindingResult.KeepOpen; + } + else + { + WeakReferenceMessenger.Default.Send(new(command.Command.Model, command.Model)); + return ContextKeybindingResult.Hide; } } + + public bool CanPopContextStack() + { + return ContextMenuStack.Count > 1; + } + + public void PopContextStack() + { + if (ContextMenuStack.Count > 1) + { + ContextMenuStack.RemoveAt(ContextMenuStack.Count - 1); + } + + OnPropertyChanging(nameof(ContextMenu)); + OnPropertyChanged(nameof(ContextMenu)); + } + + public void ClearContextStack() + { + while (ContextMenuStack.Count > 1) + { + ContextMenuStack.RemoveAt(ContextMenuStack.Count - 1); + } + + OnPropertyChanging(nameof(ContextMenu)); + OnPropertyChanged(nameof(ContextMenu)); + } +} + +public enum ContextKeybindingResult +{ + Unhandled, + Hide, + KeepOpen, } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandContextItemViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandContextItemViewModel.cs index f3475fd964..dfbb1a982a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandContextItemViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandContextItemViewModel.cs @@ -9,12 +9,16 @@ namespace Microsoft.CmdPal.UI.ViewModels; public partial class CommandContextItemViewModel(ICommandContextItem contextItem, WeakReference context) : CommandItemViewModel(new(contextItem), context) { + private readonly KeyChord nullKeyChord = new(0, 0, 0); + public new ExtensionObject Model { get; } = new(contextItem); public bool IsCritical { get; private set; } public KeyChord? RequestedShortcut { get; private set; } + public bool HasRequestedShortcut => RequestedShortcut != null && (RequestedShortcut.Value != nullKeyChord); + public override void InitializeProperties() { if (IsInitialized) @@ -31,6 +35,9 @@ public partial class CommandContextItemViewModel(ICommandContextItem contextItem } IsCritical = contextItem.IsCritical; + + // I actually don't think this will ever actually be null, because + // KeyChord is a struct, which isn't nullable in WinRT if (contextItem.RequestedShortcut != null) { RequestedShortcut = new( diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandItemViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandItemViewModel.cs index 37d223b000..24dd9e1788 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandItemViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/CommandItemViewModel.cs @@ -48,7 +48,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa public List MoreCommands { get; private set; } = []; - IEnumerable ICommandBarContext.MoreCommands => MoreCommands; + IEnumerable IContextMenuContext.MoreCommands => MoreCommands; public bool HasMoreCommands => MoreCommands.Count > 0; @@ -187,23 +187,26 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa // use Initialize straight up MoreCommands.ForEach(contextItem => { - contextItem.InitializeProperties(); + contextItem.SlowInitializeProperties(); }); - _defaultCommandContextItem = new(new CommandContextItem(model.Command!), PageContext) + if (!string.IsNullOrEmpty(model.Command.Name)) { - _itemTitle = Name, - Subtitle = Subtitle, - Command = Command, + _defaultCommandContextItem = new(new CommandContextItem(model.Command!), PageContext) + { + _itemTitle = Name, + Subtitle = Subtitle, + Command = Command, - // TODO this probably should just be a CommandContextItemViewModel(CommandItemViewModel) ctor, or a copy ctor or whatever - }; + // TODO this probably should just be a CommandContextItemViewModel(CommandItemViewModel) ctor, or a copy ctor or whatever + }; - // Only set the icon on the context item for us if our command didn't - // have its own icon - if (!Command.HasIcon) - { - _defaultCommandContextItem._listItemIcon = _listItemIcon; + // Only set the icon on the context item for us if our command didn't + // have its own icon + if (!Command.HasIcon) + { + _defaultCommandContextItem._listItemIcon = _listItemIcon; + } } Initialized |= InitializedState.SelectionInitialized; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/CreatedExtensionForm.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/CreatedExtensionForm.cs index 1939162662..44bcb49cb3 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/CreatedExtensionForm.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/CreatedExtensionForm.cs @@ -13,12 +13,13 @@ internal sealed partial class CreatedExtensionForm : NewExtensionFormBase { public CreatedExtensionForm(string name, string displayName, string path) { + var serializeString = (string? s) => JsonSerializer.Serialize(s, JsonSerializationContext.Default.String); TemplateJson = CardTemplate; DataJson = $$""" { - "name": {{JsonSerializer.Serialize(name)}}, - "directory": {{JsonSerializer.Serialize(path)}}, - "displayName": {{JsonSerializer.Serialize(displayName)}} + "name": {{serializeString(name)}}, + "directory": {{serializeString(path)}}, + "displayName": {{serializeString(displayName)}} } """; _name = name; @@ -28,13 +29,13 @@ internal sealed partial class CreatedExtensionForm : NewExtensionFormBase public override ICommandResult SubmitForm(string inputs, string data) { - JsonObject? dataInput = JsonNode.Parse(data)?.AsObject(); + var dataInput = JsonNode.Parse(data)?.AsObject(); if (dataInput == null) { return CommandResult.KeepOpen(); } - string verb = dataInput["x"]?.AsValue()?.ToString() ?? string.Empty; + var verb = dataInput["x"]?.AsValue()?.ToString() ?? string.Empty; return verb switch { "sln" => OpenSolution(), @@ -47,7 +48,7 @@ internal sealed partial class CreatedExtensionForm : NewExtensionFormBase private ICommandResult OpenSolution() { string[] parts = [_path, _name, $"{_name}.sln"]; - string pathToSolution = Path.Combine(parts); + var pathToSolution = Path.Combine(parts); ShellHelpers.OpenInShell(pathToSolution); return CommandResult.Hide(); } @@ -55,7 +56,7 @@ internal sealed partial class CreatedExtensionForm : NewExtensionFormBase private ICommandResult OpenDirectory() { string[] parts = [_path, _name]; - string pathToDir = Path.Combine(parts); + var pathToDir = Path.Combine(parts); ShellHelpers.OpenInShell(pathToDir); return CommandResult.Hide(); } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs index 326b7ac2b3..aca45f3494 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Commands/NewExtensionForm.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.IO.Compression; +using System.Text.Json; using System.Text.Json.Nodes; using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; @@ -28,69 +29,69 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase "body": [ { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_page_title}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_title)}}, "size": "large" }, { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_page_text}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_page_text)}}, "wrap": true }, { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_name_header}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_header)}}, "weight": "bolder", "size": "default" }, { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_name_description}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_description)}}, "wrap": true }, { "type": "Input.Text", - "label": "{{Properties.Resources.builtin_create_extension_name_label}}", + "label": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_label)}}, "isRequired": true, - "errorMessage": "{{Properties.Resources.builtin_create_extension_name_required}}", + "errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_name_required)}}, "id": "ExtensionName", "placeholder": "ExtensionName", "regex": "^[^\\s]+$" }, { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_display_name_header}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_header)}}, "weight": "bolder", "size": "default" }, { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_display_name_description}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_description)}}, "wrap": true }, { "type": "Input.Text", - "label": "{{Properties.Resources.builtin_create_extension_display_name_label}}", + "label": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_label)}}, "isRequired": true, - "errorMessage": "{{Properties.Resources.builtin_create_extension_display_name_required}}", + "errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_display_name_required)}}, "id": "DisplayName", "placeholder": "My new extension" }, { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_directory_header}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_header)}}, "weight": "bolder", "size": "default" }, { "type": "TextBlock", - "text": "{{Properties.Resources.builtin_create_extension_directory_description}}", + "text": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_description)}}, "wrap": true }, { "type": "Input.Text", - "label": "{{Properties.Resources.builtin_create_extension_directory_label}}", + "label": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_label)}}, "isRequired": true, - "errorMessage": "{{Properties.Resources.builtin_create_extension_directory_required}}", + "errorMessage": {{FormatJsonString(Properties.Resources.builtin_create_extension_directory_required)}}, "id": "OutputPath", "placeholder": "C:\\users\\me\\dev" } @@ -98,7 +99,7 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase "actions": [ { "type": "Action.Submit", - "title": "{{Properties.Resources.builtin_create_extension_submit}}", + "title": {{FormatJsonString(Properties.Resources.builtin_create_extension_submit)}}, "associatedInputs": "auto" } ] @@ -192,4 +193,9 @@ internal sealed partial class NewExtensionForm : NewExtensionFormBase // Delete the temp dir Directory.Delete(tempDir, true); } + + private string FormatJsonString(string str) => + + // Escape the string for JSON + JsonSerializer.Serialize(str, JsonSerializationContext.Default.String); } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs index e1bbe0b604..5af1959cd6 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentFormViewModel.cs @@ -51,15 +51,16 @@ public partial class ContentFormViewModel(IFormContent _form, WeakReference JsonSerializer.Serialize(s, JsonSerializationContext.Default.String); // todo: we could probably stick Card.Errors in there too var dataJson = $$""" { - "error_message": {{JsonSerializer.Serialize(e.Message)}}, - "error_stack": {{JsonSerializer.Serialize(e.StackTrace)}}, - "inner_exception": {{JsonSerializer.Serialize(e.InnerException?.Message)}}, - "template_json": {{JsonSerializer.Serialize(TemplateJson)}}, - "data_json": {{JsonSerializer.Serialize(DataJson)}} + "error_message": {{serializeString(e.Message)}}, + "error_stack": {{serializeString(e.StackTrace)}}, + "inner_exception": {{serializeString(e.InnerException?.Message)}}, + "template_json": {{serializeString(TemplateJson)}}, + "data_json": {{serializeString(DataJson)}} } """; var cardJson = template.Expand(dataJson); diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs index 73bb041b99..f8b0e90834 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContentPageViewModel.cs @@ -167,7 +167,7 @@ public partial class ContentPageViewModel : PageViewModel, ICommandBarContext Commands.ForEach(contextItem => { - contextItem.InitializeProperties(); + contextItem.SlowInitializeProperties(); }); } else diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContextMenuStackViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContextMenuStackViewModel.cs new file mode 100644 index 0000000000..2b16bd8f47 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ContextMenuStackViewModel.cs @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.ObjectModel; +using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.CmdPal.UI.ViewModels.Messages; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.System; + +namespace Microsoft.CmdPal.UI.ViewModels; + +public partial class ContextMenuStackViewModel : ObservableObject +{ + [ObservableProperty] + public partial ObservableCollection FilteredItems { get; set; } + + private readonly IContextMenuContext _context; + private string _lastSearchText = string.Empty; + + // private Dictionary? _contextKeybindings; + public ContextMenuStackViewModel(IContextMenuContext context) + { + _context = context; + FilteredItems = [.. context.AllCommands]; + } + + public void SetSearchText(string searchText) + { + if (searchText == _lastSearchText) + { + return; + } + + _lastSearchText = searchText; + + var commands = _context.AllCommands.Where(c => c.ShouldBeVisible); + if (string.IsNullOrEmpty(searchText)) + { + ListHelpers.InPlaceUpdateList(FilteredItems, commands); + return; + } + + var newResults = ListHelpers.FilterList(commands, searchText, ScoreContextCommand); + ListHelpers.InPlaceUpdateList(FilteredItems, newResults); + } + + private static int ScoreContextCommand(string query, CommandContextItemViewModel item) + { + if (string.IsNullOrEmpty(query) || string.IsNullOrWhiteSpace(query)) + { + return 1; + } + + if (string.IsNullOrEmpty(item.Title)) + { + return 0; + } + + var nameMatch = StringMatcher.FuzzySearch(query, item.Title); + + var descriptionMatch = StringMatcher.FuzzySearch(query, item.Subtitle); + + return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max(); + } + + public CommandContextItemViewModel? CheckKeybinding(bool ctrl, bool alt, bool shift, bool win, VirtualKey key) + { + var keybindings = _context.Keybindings(); + if (keybindings != null) + { + // Does the pressed key match any of the keybindings? + var pressedKeyChord = KeyChordHelpers.FromModifiers(ctrl, alt, shift, win, key, 0); + if (keybindings.TryGetValue(pressedKeyChord, out var item)) + { + return item; + } + } + + return null; + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListViewModel.cs index f1c6aa4695..83dc4018f9 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ListViewModel.cs @@ -52,6 +52,8 @@ public partial class ListViewModel : PageViewModel, IDisposable public string SearchText { get; private set; } = string.Empty; + public string InitialSearchText { get; private set; } = string.Empty; + public CommandItemViewModel EmptyContent { get; private set; } private bool _isDynamic; @@ -128,7 +130,7 @@ public partial class ListViewModel : PageViewModel, IDisposable try { - IListItem[] newItems = _model.Unsafe!.GetItems(); + var newItems = _model.Unsafe!.GetItems(); // Collect all the items into new viewmodels Collection newViewModels = []; @@ -136,7 +138,7 @@ public partial class ListViewModel : PageViewModel, IDisposable // TODO we can probably further optimize this by also keeping a // HashSet of every ExtensionObject we currently have, and only // building new viewmodels for the ones we haven't already built. - foreach (IListItem? item in newItems) + foreach (var item in newItems) { ListItemViewModel viewModel = new(item, new(this)); @@ -147,8 +149,8 @@ public partial class ListViewModel : PageViewModel, IDisposable } } - IEnumerable firstTwenty = newViewModels.Take(20); - foreach (ListItemViewModel? item in firstTwenty) + var firstTwenty = newViewModels.Take(20); + foreach (var item in firstTwenty) { item?.SafeInitializeProperties(); } @@ -233,7 +235,7 @@ public partial class ListViewModel : PageViewModel, IDisposable iterable = Items.ToArray(); } - foreach (ListItemViewModel item in iterable) + foreach (var item in iterable) { ct.ThrowIfCancellationRequested(); @@ -266,8 +268,8 @@ public partial class ListViewModel : PageViewModel, IDisposable return 1; } - MatchResult nameMatch = StringMatcher.FuzzySearch(query, listItem.Title); - MatchResult descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle); + var nameMatch = StringMatcher.FuzzySearch(query, listItem.Title); + var descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle); return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max(); } @@ -280,7 +282,7 @@ public partial class ListViewModel : PageViewModel, IDisposable // Similarly stolen from ListHelpers.FilterList public static IEnumerable FilterList(IEnumerable items, string query) { - IOrderedEnumerable scores = items + var scores = items .Where(i => !i.IsInErrorState) .Select(li => new ScoredListItemViewModel() { ViewModel = li, Score = ScoreListItem(query, li) }) .Where(score => score.Score > 0) @@ -359,7 +361,7 @@ public partial class ListViewModel : PageViewModel, IDisposable { base.InitializeProperties(); - IListPage? model = _model.Unsafe; + var model = _model.Unsafe; if (model == null) { return; // throw? @@ -373,8 +375,9 @@ public partial class ListViewModel : PageViewModel, IDisposable _modelPlaceholderText = model.PlaceholderText; UpdateProperty(nameof(PlaceholderText)); - SearchText = model.SearchText; + InitialSearchText = SearchText = model.SearchText; UpdateProperty(nameof(SearchText)); + UpdateProperty(nameof(InitialSearchText)); EmptyContent = new(new(model.EmptyContent), PageContext); EmptyContent.SlowInitializeProperties(); @@ -385,7 +388,7 @@ public partial class ListViewModel : PageViewModel, IDisposable public void LoadMoreIfNeeded() { - IListPage? model = this._model.Unsafe; + var model = this._model.Unsafe; if (model == null) { return; @@ -412,7 +415,7 @@ public partial class ListViewModel : PageViewModel, IDisposable { base.FetchProperty(propertyName); - IListPage? model = this._model.Unsafe; + var model = this._model.Unsafe; if (model == null) { return; // throw? @@ -431,7 +434,7 @@ public partial class ListViewModel : PageViewModel, IDisposable break; case nameof(EmptyContent): EmptyContent = new(new(model.EmptyContent), PageContext); - EmptyContent.InitializeProperties(); + EmptyContent.SlowInitializeProperties(); break; case nameof(IsLoading): UpdateEmptyContent(); @@ -449,6 +452,8 @@ public partial class ListViewModel : PageViewModel, IDisposable return; } + UpdateProperty(nameof(EmptyContent)); + DoOnUiThread( () => { @@ -475,13 +480,13 @@ public partial class ListViewModel : PageViewModel, IDisposable lock (_listLock) { - foreach (ListItemViewModel item in Items) + foreach (var item in Items) { item.SafeCleanup(); } Items.Clear(); - foreach (ListItemViewModel item in FilteredItems) + foreach (var item in FilteredItems) { item.SafeCleanup(); } @@ -489,7 +494,7 @@ public partial class ListViewModel : PageViewModel, IDisposable FilteredItems.Clear(); } - IListPage? model = _model.Unsafe; + var model = _model.Unsafe; if (model != null) { model.ItemsChanged -= Model_ItemsChanged; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/PerformCommandMessage.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/PerformCommandMessage.cs index 9bc0c730e8..b0e5e4829a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/PerformCommandMessage.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/PerformCommandMessage.cs @@ -51,6 +51,12 @@ public record PerformCommandMessage Context = context.Unsafe; } + public PerformCommandMessage(CommandContextItemViewModel contextCommand) + { + Command = contextCommand.Command.Model; + Context = contextCommand.Model.Unsafe; + } + public PerformCommandMessage(ConfirmResultViewModel vm) { Command = vm.PrimaryCommand.Model; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/TryCommandKeybindingMessage.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/TryCommandKeybindingMessage.cs new file mode 100644 index 0000000000..3df48ec3a0 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/TryCommandKeybindingMessage.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Windows.System; + +namespace Microsoft.CmdPal.UI.ViewModels.Messages; + +public record TryCommandKeybindingMessage(bool Ctrl, bool Alt, bool Shift, bool Win, VirtualKey Key) +{ + public bool Handled { get; set; } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/UpdateCommandBarMessage.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/UpdateCommandBarMessage.cs index 0a540c7408..929b5995c5 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/UpdateCommandBarMessage.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Messages/UpdateCommandBarMessage.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; +using Microsoft.CommandPalette.Extensions; namespace Microsoft.CmdPal.UI.ViewModels.Messages; @@ -13,22 +14,42 @@ public record UpdateCommandBarMessage(ICommandBarContext? ViewModel) { } -// Represents everything the command bar needs to know about to show command -// buttons at the bottom. -// -// This is implemented by both ListItemViewModel and ContentPageViewModel, -// the two things with sub-commands. -public interface ICommandBarContext : INotifyPropertyChanged +public interface IContextMenuContext : INotifyPropertyChanged { public IEnumerable MoreCommands { get; } public bool HasMoreCommands { get; } + public List AllCommands { get; } + + /// + /// Generates a mapping of key -> command item for this particular item's + /// MoreCommands. (This won't include the primary Command, but it will + /// include the secondary one). This map can be used to quickly check if a + /// shortcut key was pressed + /// + /// a dictionary of KeyChord -> Context commands, for all commands + /// that have a shortcut key set. + public Dictionary Keybindings() + { + return MoreCommands + .Where(c => c.HasRequestedShortcut) + .ToDictionary( + c => c.RequestedShortcut ?? new KeyChord(0, 0, 0), + c => c); + } +} + +// Represents everything the command bar needs to know about to show command +// buttons at the bottom. +// +// This is implemented by both ListItemViewModel and ContentPageViewModel, +// the two things with sub-commands. +public interface ICommandBarContext : IContextMenuContext +{ public string SecondaryCommandName { get; } public CommandItemViewModel? PrimaryCommand { get; } public CommandItemViewModel? SecondaryCommand { get; } - - public List AllCommands { get; } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Microsoft.CmdPal.UI.ViewModels.csproj b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Microsoft.CmdPal.UI.ViewModels.csproj index ed8831915a..8057bb09be 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Microsoft.CmdPal.UI.ViewModels.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Microsoft.CmdPal.UI.ViewModels.csproj @@ -1,5 +1,7 @@  + + enable enable @@ -31,7 +33,7 @@ - + @@ -67,4 +69,15 @@ + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionService.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionService.cs index 576ea08140..6d59aa66b4 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionService.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionService.cs @@ -11,7 +11,7 @@ using Windows.Foundation.Collections; namespace Microsoft.CmdPal.UI.ViewModels.Models; -public class ExtensionService : IExtensionService, IDisposable +public partial class ExtensionService : IExtensionService, IDisposable { public event TypedEventHandler>? OnExtensionAdded; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionWrapper.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionWrapper.cs index ed15268507..83644c8d44 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionWrapper.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/Models/ExtensionWrapper.cs @@ -12,6 +12,7 @@ using Windows.Win32; using Windows.Win32.System.Com; using WinRT; +// [assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling] namespace Microsoft.CmdPal.UI.ViewModels.Models; public class ExtensionWrapper : IExtensionWrapper @@ -113,25 +114,36 @@ public class ExtensionWrapper : IExtensionWrapper // -2147467262: E_NOINTERFACE // -2147024893: E_PATH_NOT_FOUND var guid = typeof(IExtension).GUID; - var hr = PInvoke.CoCreateInstance(Guid.Parse(ExtensionClassId), null, CLSCTX.CLSCTX_LOCAL_SERVER, guid, out var extensionObj); - if (hr.Value == -2147024893) + unsafe { - Logger.LogDebug($"Failed to find {ExtensionDisplayName}: {hr}. It may have been uninstalled or deleted."); + var hr = PInvoke.CoCreateInstance(Guid.Parse(ExtensionClassId), null, CLSCTX.CLSCTX_LOCAL_SERVER, guid, out var extensionObj); - // We don't really need to throw this exception. - // We'll just return out nothing. - return; + if (hr.Value == -2147024893) + { + Logger.LogDebug($"Failed to find {ExtensionDisplayName}: {hr}. It may have been uninstalled or deleted."); + + // We don't really need to throw this exception. + // We'll just return out nothing. + return; + } + + extensionPtr = Marshal.GetIUnknownForObject((nint)extensionObj); + if (hr < 0) + { + Logger.LogDebug($"Failed to instantiate {ExtensionDisplayName}: {hr}"); + Marshal.ThrowExceptionForHR(hr); + } + + // extensionPtr = Marshal.GetIUnknownForObject(extensionObj); + extensionPtr = (nint)extensionObj; + if (hr < 0) + { + Marshal.ThrowExceptionForHR(hr); + } + + _extensionObject = MarshalInterface.FromAbi(extensionPtr); } - - extensionPtr = Marshal.GetIUnknownForObject(extensionObj); - if (hr < 0) - { - Logger.LogDebug($"Failed to instantiate {ExtensionDisplayName}: {hr}"); - Marshal.ThrowExceptionForHR(hr); - } - - _extensionObject = MarshalInterface.FromAbi(extensionPtr); } finally { diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/NativeMethods.json b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/NativeMethods.json new file mode 100644 index 0000000000..59fa7259c4 --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/NativeMethods.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs index a082f0acd2..126efa83ca 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/PageViewModel.cs @@ -99,7 +99,7 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext //// Run on background thread from ListPage.xaml.cs [RelayCommand] - private Task InitializeAsync() + internal Task InitializeAsync() { // TODO: We may want a SemaphoreSlim lock here. @@ -182,6 +182,7 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext return; // throw? } + var updateProperty = true; switch (propertyName) { case nameof(Name): @@ -198,9 +199,21 @@ public partial class PageViewModel : ExtensionObjectViewModel, IPageContext case nameof(Icon): this.Icon = new(model.Icon); break; + default: + updateProperty = false; + break; } - UpdateProperty(propertyName); + // GH #38829: If we always UpdateProperty here, then there's a possible + // race condition, where we raise the PropertyChanged(SearchText) + // before the subclass actually retrieves the new SearchText from the + // model. In that race situation, if the UI thread handles the + // PropertyChanged before ListViewModel fetches the SearchText, it'll + // think that the old search text is the _new_ value. + if (updateProperty) + { + UpdateProperty(propertyName); + } } public new void ShowException(Exception ex, string? extensionHint = null) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/RecentCommandsManager.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/RecentCommandsManager.cs index 9e971ae510..c740341c7a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/RecentCommandsManager.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/RecentCommandsManager.cs @@ -10,7 +10,7 @@ namespace Microsoft.CmdPal.UI.ViewModels; public partial class RecentCommandsManager : ObservableObject { [JsonInclude] - private List History { get; set; } = []; + internal List History { get; set; } = []; public RecentCommandsManager() { diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs index 785c27c3c9..ae97849f7a 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsModel.cs @@ -26,6 +26,8 @@ public partial class SettingsModel : ObservableObject public HotkeySettings? Hotkey { get; set; } = DefaultActivationShortcut; + public bool UseLowLevelGlobalHotkey { get; set; } + public bool ShowAppDetails { get; set; } public bool HotkeyGoesHome { get; set; } @@ -36,6 +38,10 @@ public partial class SettingsModel : ObservableObject public bool HighlightSearchOnActivate { get; set; } = true; + public bool ShowSystemTrayIcon { get; set; } = true; + + public bool IgnoreShortcutWhenFullscreen { get; set; } = true; + public Dictionary ProviderSettings { get; set; } = []; public Dictionary Aliases { get; set; } = []; @@ -87,7 +93,7 @@ public partial class SettingsModel : ObservableObject // Read the JSON content from the file var jsonContent = File.ReadAllText(FilePath); - var loaded = JsonSerializer.Deserialize(jsonContent, _deserializerOptions); + var loaded = JsonSerializer.Deserialize(jsonContent, JsonSerializationContext.Default.SettingsModel); Debug.WriteLine(loaded != null ? "Loaded settings file" : "Failed to parse"); @@ -111,7 +117,7 @@ public partial class SettingsModel : ObservableObject try { // Serialize the main dictionary to JSON and save it to the file - var settingsJson = JsonSerializer.Serialize(model, _serializerOptions); + var settingsJson = JsonSerializer.Serialize(model, JsonSerializationContext.Default.SettingsModel); // Is it valid JSON? if (JsonNode.Parse(settingsJson) is JsonObject newSettings) @@ -127,7 +133,7 @@ public partial class SettingsModel : ObservableObject savedSettings[item.Key] = item.Value != null ? item.Value.DeepClone() : null; } - var serialized = savedSettings.ToJsonString(_serializerOptions); + var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.Options); File.WriteAllText(FilePath, serialized); // TODO: Instead of just raising the event here, we should @@ -160,19 +166,34 @@ public partial class SettingsModel : ObservableObject return Path.Combine(directory, "settings.json"); } - private static readonly JsonSerializerOptions _serializerOptions = new() - { - WriteIndented = true, - Converters = { new JsonStringEnumConverter() }, - }; + // [UnconditionalSuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "")] + // private static readonly JsonSerializerOptions _serializerOptions = new() + // { + // WriteIndented = true, + // Converters = { new JsonStringEnumConverter() }, + // }; + // private static readonly JsonSerializerOptions _deserializerOptions = new() + // { + // PropertyNameCaseInsensitive = true, + // IncludeFields = true, + // Converters = { new JsonStringEnumConverter() }, + // AllowTrailingCommas = true, + // }; +} - private static readonly JsonSerializerOptions _deserializerOptions = new() - { - PropertyNameCaseInsensitive = true, - IncludeFields = true, - Converters = { new JsonStringEnumConverter() }, - AllowTrailingCommas = true, - }; +[JsonSerializable(typeof(float))] +[JsonSerializable(typeof(int))] +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(bool))] +[JsonSerializable(typeof(HistoryItem))] +[JsonSerializable(typeof(SettingsModel))] +[JsonSerializable(typeof(AppStateModel))] +[JsonSerializable(typeof(List), TypeInfoPropertyName = "HistoryList")] +[JsonSerializable(typeof(Dictionary), TypeInfoPropertyName = "Dictionary")] +[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true, IncludeFields = true, PropertyNameCaseInsensitive = true, AllowTrailingCommas = true)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Just used here")] +internal sealed partial class JsonSerializationContext : JsonSerializerContext +{ } public enum MonitorBehavior diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs index 190d75b0c3..2083d35191 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/SettingsViewModel.cs @@ -27,6 +27,17 @@ public partial class SettingsViewModel : INotifyPropertyChanged } } + public bool UseLowLevelGlobalHotkey + { + get => _settings.UseLowLevelGlobalHotkey; + set + { + _settings.UseLowLevelGlobalHotkey = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey))); + Save(); + } + } + public bool ShowAppDetails { get => _settings.ShowAppDetails; @@ -87,6 +98,26 @@ public partial class SettingsViewModel : INotifyPropertyChanged } } + public bool ShowSystemTrayIcon + { + get => _settings.ShowSystemTrayIcon; + set + { + _settings.ShowSystemTrayIcon = value; + Save(); + } + } + + public bool IgnoreShortcutWhenFullscreen + { + get => _settings.IgnoreShortcutWhenFullscreen; + set + { + _settings.IgnoreShortcutWhenFullscreen = value; + Save(); + } + } + public ObservableCollection CommandProviders { get; } = []; public SettingsViewModel(SettingsModel settings, IServiceProvider serviceProvider, TaskScheduler scheduler) diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs index 043598196b..d86831d0a1 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/ShellViewModel.cs @@ -109,9 +109,12 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched // TODO GH #239 switch back when using the new MD text block // _ = _queue.EnqueueAsync(() => _ = Task.Factory.StartNew( - () => + async () => { - var result = (bool)viewModel.InitializeCommand.ExecutionTask.GetResultOrDefault()!; + // bool f = await viewModel.InitializeCommand.ExecutionTask.; + // var result = viewModel.InitializeCommand.ExecutionTask.GetResultOrDefault()!; + // var result = viewModel.InitializeCommand.ExecutionTask.GetResultOrDefault()!; + var result = await viewModel.InitializeAsync(); CurrentPage = viewModel; // result ? viewModel : null; ////LoadedState = result ? ViewModelLoadedState.Loaded : ViewModelLoadedState.Error; diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs index 4b8a2b49a9..5ff8337494 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/TopLevelViewModel.cs @@ -152,7 +152,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem { GenerateId(); - UpdateAlias(); + FetchAliasFromAliasManager(); UpdateHotkey(); UpdateTags(); } @@ -163,24 +163,31 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem private void HandleChangeAlias() { - SetAlias(Alias); + SetAlias(); Save(); } - public void SetAlias(CommandAlias? newAlias) + public void SetAlias() { - _serviceProvider.GetService()!.UpdateAlias(Id, newAlias); - UpdateAlias(); + var commandAlias = Alias is null + ? null + : new CommandAlias(Alias.Alias, Alias.CommandId, Alias.IsDirect); + + _serviceProvider.GetService()!.UpdateAlias(Id, commandAlias); UpdateTags(); } - private void UpdateAlias() + private void FetchAliasFromAliasManager() { - // Add tags for the alias, if we have one. - var aliases = _serviceProvider.GetService(); - if (aliases != null) + var am = _serviceProvider.GetService(); + if (am != null) { - Alias = aliases.AliasFromId(Id); + var commandAlias = am.AliasFromId(Id); + if (commandAlias is not null) + { + // Decouple from the alias manager alias object + Alias = new CommandAlias(commandAlias.Alias, commandAlias.CommandId, commandAlias.IsDirect); + } } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml index 9f7b4d4071..203009f763 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml @@ -36,7 +36,7 @@ - + @@ -53,37 +53,16 @@ Grid.Column="1" VerticalAlignment="Center" Text="{x:Bind Title, Mode=OneWay}" /> - + Text="{x:Bind RequestedShortcut, Mode=OneWay, Converter={StaticResource KeyChordToStringConverter}}" /> - - - - - - - - - @@ -92,9 +71,9 @@ Padding="4" ColumnSpacing="8"> - + @@ -157,6 +133,8 @@ VerticalAlignment="Center" Style="{StaticResource CaptionTextBlockStyle}" Text="{x:Bind CurrentPageViewModel.Title, Mode=OneWay}" + TextTrimming="CharacterEllipsis" + TextWrapping="NoWrap" Visibility="{x:Bind CurrentPageViewModel.IsNested, Mode=OneWay}" /> @@ -245,35 +219,48 @@ x:Name="MoreCommandsButton" x:Uid="MoreCommandsButton" Padding="4" - animations:Implicit.HideAnimations="{StaticResource HideAnimations}" - animations:Implicit.ShowAnimations="{StaticResource ShowAnimations}" - ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5" Content="{ui:FontIcon Glyph=, FontSize=16}" Style="{StaticResource SubtleButtonStyle}" - ToolTipService.ToolTip="Ctrl+k" + ToolTipService.ToolTip="Ctrl+K" Visibility="{x:Bind ViewModel.ShouldShowContextMenu, Mode=OneWay}"> - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs index 382e7ba7ea..f079f4b513 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/CommandBar.xaml.cs @@ -6,18 +6,22 @@ using CommunityToolkit.Mvvm.Messaging; using Microsoft.CmdPal.UI.ViewModels; using Microsoft.CmdPal.UI.ViewModels.Messages; using Microsoft.CmdPal.UI.Views; +using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Controls.Primitives; using Microsoft.UI.Xaml.Input; +using Windows.System; +using Windows.UI.Core; namespace Microsoft.CmdPal.UI.Controls; public sealed partial class CommandBar : UserControl, IRecipient, + IRecipient, ICurrentPageAware { - public CommandBarViewModel ViewModel { get; set; } = new(); + public CommandBarViewModel ViewModel { get; } = new(); public PageViewModel? CurrentPageViewModel { @@ -35,6 +39,9 @@ public sealed partial class CommandBar : UserControl, // RegisterAll isn't AOT compatible WeakReferenceMessenger.Default.Register(this); + WeakReferenceMessenger.Default.Register(this); + + ViewModel.PropertyChanged += ViewModel_PropertyChanged; } public void Receive(OpenContextMenuMessage message) @@ -49,8 +56,41 @@ public sealed partial class CommandBar : UserControl, ShowMode = FlyoutShowMode.Standard, }; MoreCommandsButton.Flyout.ShowAt(MoreCommandsButton, options); - CommandsDropdown.SelectedIndex = 0; - CommandsDropdown.Focus(FocusState.Programmatic); + UpdateUiForStackChange(); + } + + public void Receive(TryCommandKeybindingMessage msg) + { + if (!ViewModel.ShouldShowContextMenu) + { + return; + } + + var result = ViewModel?.CheckKeybinding(msg.Ctrl, msg.Alt, msg.Shift, msg.Win, msg.Key); + + if (result == ContextKeybindingResult.Hide) + { + msg.Handled = true; + } + else if (result == ContextKeybindingResult.KeepOpen) + { + if (!MoreCommandsButton.Flyout.IsOpen) + { + var options = new FlyoutShowOptions + { + ShowMode = FlyoutShowMode.Standard, + }; + MoreCommandsButton.Flyout.ShowAt(MoreCommandsButton, options); + } + + UpdateUiForStackChange(); + + msg.Handled = true; + } + else if (result == ContextKeybindingResult.Unhandled) + { + msg.Handled = false; + } } [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS has a tendency to delete XAML bound methods over-aggressively")] @@ -85,8 +125,160 @@ public sealed partial class CommandBar : UserControl, { if (e.ClickedItem is CommandContextItemViewModel item) { - ViewModel?.InvokeItemCommand.Execute(item); - MoreCommandsButton.Flyout.Hide(); + if (ViewModel?.InvokeItem(item) == ContextKeybindingResult.Hide) + { + MoreCommandsButton.Flyout.Hide(); + } + else + { + UpdateUiForStackChange(); + } } } + + private void CommandsDropdown_KeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Handled) + { + return; + } + + var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); + var altPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down); + var shiftPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down); + var winPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.LeftWindows).HasFlag(CoreVirtualKeyStates.Down) || + InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.RightWindows).HasFlag(CoreVirtualKeyStates.Down); + + var result = ViewModel?.CheckKeybinding(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key); + + if (result == ContextKeybindingResult.Hide) + { + e.Handled = true; + MoreCommandsButton.Flyout.Hide(); + WeakReferenceMessenger.Default.Send(); + } + else if (result == ContextKeybindingResult.KeepOpen) + { + e.Handled = true; + } + else if (result == ContextKeybindingResult.Unhandled) + { + e.Handled = false; + } + } + + private void Flyout_Opened(object sender, object e) + { + UpdateUiForStackChange(); + } + + private void Flyout_Closing(FlyoutBase sender, FlyoutBaseClosingEventArgs args) + { + ViewModel?.ClearContextStack(); + WeakReferenceMessenger.Default.Send(); + } + + private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + var prop = e.PropertyName; + if (prop == nameof(ViewModel.ContextMenu)) + { + UpdateUiForStackChange(); + } + } + + private void ContextFilterBox_TextChanged(object sender, TextChangedEventArgs e) + { + ViewModel.ContextMenu?.SetSearchText(ContextFilterBox.Text); + + if (CommandsDropdown.SelectedIndex == -1) + { + CommandsDropdown.SelectedIndex = 0; + } + } + + private void ContextFilterBox_KeyDown(object sender, KeyRoutedEventArgs e) + { + var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); + var altPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down); + var shiftPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down); + var winPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.LeftWindows).HasFlag(CoreVirtualKeyStates.Down) || + InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.RightWindows).HasFlag(CoreVirtualKeyStates.Down); + + if (e.Key == VirtualKey.Enter) + { + if (CommandsDropdown.SelectedItem is CommandContextItemViewModel item) + { + if (ViewModel?.InvokeItem(item) == ContextKeybindingResult.Hide) + { + MoreCommandsButton.Flyout.Hide(); + WeakReferenceMessenger.Default.Send(); + } + else + { + UpdateUiForStackChange(); + } + + e.Handled = true; + } + } + else if (e.Key == VirtualKey.Escape || + (e.Key == VirtualKey.Left && altPressed)) + { + if (ViewModel.CanPopContextStack()) + { + ViewModel.PopContextStack(); + UpdateUiForStackChange(); + } + else + { + MoreCommandsButton.Flyout.Hide(); + WeakReferenceMessenger.Default.Send(); + } + + e.Handled = true; + } + + CommandsDropdown_KeyDown(sender, e); + } + + private void ContextFilterBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e) + { + if (e.Key == VirtualKey.Up) + { + // navigate previous + if (CommandsDropdown.SelectedIndex > 0) + { + CommandsDropdown.SelectedIndex--; + } + else + { + CommandsDropdown.SelectedIndex = CommandsDropdown.Items.Count - 1; + } + + e.Handled = true; + } + else if (e.Key == VirtualKey.Down) + { + // navigate next + if (CommandsDropdown.SelectedIndex < CommandsDropdown.Items.Count - 1) + { + CommandsDropdown.SelectedIndex++; + } + else + { + CommandsDropdown.SelectedIndex = 0; + } + + e.Handled = true; + } + } + + private void UpdateUiForStackChange() + { + ContextFilterBox.Text = string.Empty; + ViewModel.ContextMenu?.SetSearchText(string.Empty); + CommandsDropdown.SelectedIndex = 0; + ContextFilterBox.Focus(FocusState.Programmatic); + } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs index f41ac98ea3..c868e3dd5e 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/SearchBar.xaml.cs @@ -105,7 +105,9 @@ public sealed partial class SearchBar : UserControl, var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down); var altPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down); - + var shiftPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down); + var winPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.LeftWindows).HasFlag(CoreVirtualKeyStates.Down) || + InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.RightWindows).HasFlag(CoreVirtualKeyStates.Down); if (ctrlPressed && e.Key == VirtualKey.Enter) { // ctrl+enter @@ -164,6 +166,16 @@ public sealed partial class SearchBar : UserControl, { WeakReferenceMessenger.Default.Send(new()); } + + if (!e.Handled) + { + // The CommandBar is responsible for handling all the item keybindings, + // since the bound context item may need to then show another + // context menu + TryCommandKeybindingMessage msg = new(ctrlPressed, altPressed, shiftPressed, winPressed, e.Key); + WeakReferenceMessenger.Default.Send(msg); + e.Handled = msg.Handled; + } } private void FilterBox_PreviewKeyDown(object sender, KeyRoutedEventArgs e) @@ -250,23 +262,36 @@ public sealed partial class SearchBar : UserControl, private void Page_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) { var property = e.PropertyName; - if (CurrentPageViewModel is ListViewModel list && - property == nameof(ListViewModel.SearchText)) - { - // Only if the text actually changed... - // (sometimes this triggers on a round-trip of the SearchText) - if (FilterBox.Text != list.SearchText) - { - // ... Update our displayed text, and... - FilterBox.Text = list.SearchText; - // ... Move the cursor to the end of the input - FilterBox.Select(FilterBox.Text.Length, 0); + if (CurrentPageViewModel is ListViewModel list) + { + if (property == nameof(ListViewModel.SearchText)) + { + // Only if the text actually changed... + // (sometimes this triggers on a round-trip of the SearchText) + if (FilterBox.Text != list.SearchText) + { + // ... Update our displayed text, and... + FilterBox.Text = list.SearchText; + + // ... Move the cursor to the end of the input + FilterBox.Select(FilterBox.Text.Length, 0); + } + } + else if (property == nameof(ListViewModel.InitialSearchText)) + { + // GH #38712: + // The ListPage will notify us of the `InitialSearchText` when + // we first load the viewmodel. We can use that as an + // opportunity to immediately select the search text. That lets + // the user start typing a new search without manually + // selecting the old one. + SelectSearch(); } } } public void Receive(GoHomeMessage message) => ClearSearch(); - public void Receive(FocusSearchBoxMessage message) => this.Focus(Microsoft.UI.Xaml.FocusState.Programmatic); + public void Receive(FocusSearchBoxMessage message) => FilterBox.Focus(Microsoft.UI.Xaml.FocusState.Programmatic); } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/Tag.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/Tag.xaml index 54f3c69b23..1a522ebdc6 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/Tag.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Controls/Tag.xaml @@ -38,6 +38,7 @@ + @@ -56,6 +57,7 @@ Padding="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}" + AutomationProperties.Name="{TemplateBinding Text}" Background="{TemplateBinding Background}" BackgroundSizing="{TemplateBinding BackgroundSizing}" BorderBrush="{TemplateBinding BorderBrush}" diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml b/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml index 395bd0ebe6..060b382f28 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/ExtViews/ListPage.xaml @@ -28,21 +28,22 @@ NotEmptyValue="Visible" /> - - - + - - + @@ -55,6 +56,7 @@ Width="20" Height="20" Margin="4,0,4,0" + AutomationProperties.AccessibilityView="Raw" Foreground="{ThemeResource TextFillColorSecondaryBrush}" SourceKey="{x:Bind Icon, Mode=OneWay}" SourceRequested="{x:Bind help:IconCacheProvider.SourceRequested}" /> @@ -136,14 +138,15 @@ + Text="{x:Bind ViewModel.EmptyContent.Title, Mode=OneWay}" + TextAlignment="Center" + TextWrapping="Wrap" /> + Text="{x:Bind ViewModel.EmptyContent.Subtitle, Mode=OneWay}" + TextAlignment="Center" + TextWrapping="Wrap" /> diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/NativeMethods.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/NativeMethods.cs new file mode 100644 index 0000000000..a3227ca77c --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/NativeMethods.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; +using System.Security; + +namespace Microsoft.CmdPal.UI.Helpers; + +[SuppressUnmanagedCodeSecurity] +internal static class NativeMethods +{ + [DllImport("shell32.dll")] + public static extern int SHQueryUserNotificationState(out UserNotificationState state); +} + +internal enum UserNotificationState : int +{ + QUNS_NOT_PRESENT = 1, + QUNS_BUSY, + QUNS_RUNNING_D3D_FULL_SCREEN, + QUNS_PRESENTATION_MODE, + QUNS_ACCEPTS_NOTIFICATIONS, + QUNS_QUIET_TIME, + QUNS_APP, +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/WindowHelper.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/WindowHelper.cs new file mode 100644 index 0000000000..c0d257088e --- /dev/null +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Helpers/WindowHelper.cs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.InteropServices; + +namespace Microsoft.CmdPal.UI.Helpers; + +internal sealed partial class WindowHelper +{ + public static bool IsWindowFullscreen() + { + UserNotificationState state; + + // https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ne-shellapi-query_user_notification_state + if (Marshal.GetExceptionForHR(NativeMethods.SHQueryUserNotificationState(out state)) == null) + { + if (state == UserNotificationState.QUNS_RUNNING_D3D_FULL_SCREEN || + state == UserNotificationState.QUNS_BUSY || + state == UserNotificationState.QUNS_PRESENTATION_MODE) + { + return true; + } + } + + return false; + } +} diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs index ac1b0e0860..ce20c9be77 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/MainWindow.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; +using CmdPalKeyboardService; using CommunityToolkit.Mvvm.Messaging; using Microsoft.CmdPal.Common.Helpers; using Microsoft.CmdPal.Common.Messages; @@ -42,6 +43,7 @@ public sealed partial class MainWindow : Window, private readonly WNDPROC? _hotkeyWndProc; private readonly WNDPROC? _originalWndProc; private readonly List _hotkeys = []; + private bool _ignoreHotKeyWhenFullScreen = true; // Stylistically, window messages are WM_* #pragma warning disable SA1310 // Field names should not contain underscore @@ -52,9 +54,10 @@ public sealed partial class MainWindow : Window, #pragma warning restore SA1306 // Field names should begin with lower-case letter #pragma warning restore SA1310 // Field names should not contain underscore + private readonly KeyboardListener _keyboardListener; + // Notification Area ("Tray") icon data private NOTIFYICONDATAW? _trayIconData; - private bool _createdIcon; private DestroyIconSafeHandle? _largeIcon; private DesktopAcrylicController? _acrylicController; @@ -67,6 +70,11 @@ public sealed partial class MainWindow : Window, _hwnd = new HWND(WinRT.Interop.WindowNative.GetWindowHandle(this).ToInt32()); CommandPaletteHost.SetHostHwnd((ulong)_hwnd.Value); + _keyboardListener = new KeyboardListener(); + _keyboardListener.Start(); + + _keyboardListener.SetProcessCommand(new CmdPalKeyboardService.ProcessCommand(HandleSummon)); + // TaskbarCreated is the message that's broadcast when explorer.exe // restarts. We need to know when that happens to be able to bring our // notification area icon back @@ -99,7 +107,6 @@ public sealed partial class MainWindow : Window, _hotkeyWndProc = HotKeyPrc; var hotKeyPrcPointer = Marshal.GetFunctionPointerForDelegate(_hotkeyWndProc); _originalWndProc = Marshal.GetDelegateForFunctionPointer(PInvoke.SetWindowLongPtr(_hwnd, WINDOW_LONG_PTR_INDEX.GWL_WNDPROC, hotKeyPrcPointer)); - AddNotificationIcon(); // Load our settings, and then also wire up a settings changed handler HotReloadSettings(); @@ -149,6 +156,9 @@ public sealed partial class MainWindow : Window, var settings = App.Current.Services.GetService()!; SetupHotkey(settings); + SetupTrayIcon(settings.ShowSystemTrayIcon); + + _ignoreHotKeyWhenFullScreen = settings.IgnoreShortcutWhenFullscreen; // This will prevent our window from appearing in alt+tab or the taskbar. // You'll _need_ to use the hotkey to summon it. @@ -279,16 +289,12 @@ public sealed partial class MainWindow : Window, ShowHwnd(message.Hwnd, settings.SummonOn); } - public void Receive(HideWindowMessage message) - { - PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); - } + public void Receive(HideWindowMessage message) => PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); + + public void Receive(QuitMessage message) => - public void Receive(QuitMessage message) - { // This might come in on a background thread DispatcherQueue.TryEnqueue(() => Close()); - } public void Receive(DismissMessage message) => PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); @@ -299,12 +305,14 @@ public sealed partial class MainWindow : Window, var extensionService = serviceProvider.GetService()!; extensionService.SignalStopExtensionsAsync(); - RemoveNotificationIcon(); + RemoveTrayIcon(); // WinUI bug is causing a crash on shutdown when FailFastOnErrors is set to true (#51773592). // Workaround by turning it off before shutdown. App.Current.DebugSettings.FailFastOnErrors = false; DisposeAcrylic(); + + _keyboardListener.Stop(); } private void DisposeAcrylic() @@ -369,12 +377,17 @@ public sealed partial class MainWindow : Window, // ... then don't hide the window when it loses focus. return; } - else - { - PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); - PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus()); + // Are we disabled? If we are, then we don't want to dismiss on focus lost. + // This can happen if an extension wanted to show a modal dialog on top of our + // window i.e. in the case of an MSAL auth window. + if (PInvoke.IsWindowEnabled(_hwnd) == 0) + { + return; } + + PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); + PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnLostFocus()); } if (_configurationSource != null) @@ -383,14 +396,13 @@ public sealed partial class MainWindow : Window, } } - public void Summon(string commandId) - { + public void Summon(string commandId) => + // The actual showing and hiding of the window will be done by the // ShellPage. This is because we don't want to show the window if the // user bound a hotkey to just an invokable command, which we can't // know till the message is being handled. WeakReferenceMessenger.Default.Send(new(commandId, _hwnd)); - } #pragma warning disable SA1310 // Field names should not contain underscore private const uint DOT_KEY = 0xBE; @@ -399,6 +411,8 @@ public sealed partial class MainWindow : Window, private void UnregisterHotkeys() { + _keyboardListener.ClearHotkeys(); + while (_hotkeys.Count > 0) { PInvoke.UnregisterHotKey(_hwnd, _hotkeys.Count - 1); @@ -413,19 +427,28 @@ public sealed partial class MainWindow : Window, var globalHotkey = settings.Hotkey; if (globalHotkey != null) { - var vk = globalHotkey.Code; - var modifiers = - (globalHotkey.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | - (globalHotkey.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | - (globalHotkey.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | - (globalHotkey.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) - ; - - var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); - if (success) + if (settings.UseLowLevelGlobalHotkey) { + _keyboardListener.SetHotkeyAction(globalHotkey.Win, globalHotkey.Ctrl, globalHotkey.Shift, globalHotkey.Alt, (byte)globalHotkey.Code, string.Empty); + _hotkeys.Add(new(globalHotkey, string.Empty)); } + else + { + var vk = globalHotkey.Code; + var modifiers = + (globalHotkey.Alt ? HOT_KEY_MODIFIERS.MOD_ALT : 0) | + (globalHotkey.Ctrl ? HOT_KEY_MODIFIERS.MOD_CONTROL : 0) | + (globalHotkey.Shift ? HOT_KEY_MODIFIERS.MOD_SHIFT : 0) | + (globalHotkey.Win ? HOT_KEY_MODIFIERS.MOD_WIN : 0) + ; + + var success = PInvoke.RegisterHotKey(_hwnd, _hotkeys.Count, modifiers, (uint)vk); + if (success) + { + _hotkeys.Add(new(globalHotkey, string.Empty)); + } + } } foreach (var commandHotkey in settings.CommandHotkeys) @@ -451,6 +474,26 @@ public sealed partial class MainWindow : Window, } } + private void HandleSummon(string commandId) + { + var isRootHotkey = string.IsNullOrEmpty(commandId); + PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey)); + + // Note to future us: the wParam will have the index of the hotkey we registered. + // We can use that in the future to differentiate the hotkeys we've pressed + // so that we can bind hotkeys to individual commands + if (!this.Visible || !isRootHotkey) + { + Activate(); + + Summon(commandId); + } + else if (isRootHotkey) + { + PInvoke.ShowWindow(_hwnd, SHOW_WINDOW_CMD.SW_HIDE); + } + } + private LRESULT HotKeyPrc( HWND hwnd, uint uMsg, @@ -464,23 +507,33 @@ public sealed partial class MainWindow : Window, var hotkeyIndex = (int)wParam.Value; if (hotkeyIndex < _hotkeys.Count) { + if (_ignoreHotKeyWhenFullScreen) + { + // If we're in full screen mode, ignore the hotkey + if (WindowHelper.IsWindowFullscreen()) + { + return (LRESULT)IntPtr.Zero; + } + } + var hotkey = _hotkeys[hotkeyIndex]; - var isRootHotkey = string.IsNullOrEmpty(hotkey.CommandId); - PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey)); + HandleSummon(hotkey.CommandId); - // Note to future us: the wParam will have the index of the hotkey we registered. - // We can use that in the future to differentiate the hotkeys we've pressed - // so that we can bind hotkeys to individual commands - if (!this.Visible || !isRootHotkey) - { - Activate(); + // var isRootHotkey = string.IsNullOrEmpty(hotkey.CommandId); - Summon(hotkey.CommandId); - } - else if (isRootHotkey) - { - PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_HIDE); - } + // // Note to future us: the wParam will have the index of the hotkey we registered. + // // We can use that in the future to differentiate the hotkeys we've pressed + // // so that we can bind hotkeys to individual commands + // if (!this.Visible || !isRootHotkey) + // { + // Activate(); + + // Summon(hotkey.CommandId); + // } + // else if (isRootHotkey) + // { + // PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_HIDE); + // } } return (LRESULT)IntPtr.Zero; @@ -491,9 +544,9 @@ public sealed partial class MainWindow : Window, // WM_WINDOWPOSCHANGING which is always received on explorer startup sequence. case PInvoke.WM_WINDOWPOSCHANGING: { - if (!_createdIcon) + if (_trayIconData == null) { - AddNotificationIcon(); + SetupTrayIcon(); } } @@ -505,7 +558,7 @@ public sealed partial class MainWindow : Window, { // Handle the case where explorer.exe restarts. // Even if we created it before, do it again - AddNotificationIcon(); + SetupTrayIcon(); } else if (uMsg == WM_TRAY_ICON) { @@ -525,55 +578,60 @@ public sealed partial class MainWindow : Window, return PInvoke.CallWindowProc(_originalWndProc, hwnd, uMsg, wParam, lParam); } - private void AddNotificationIcon() + private void SetupTrayIcon(bool? showSystemTrayIcon = null) { - // We only need to build the tray data once. - if (_trayIconData == null) + if (showSystemTrayIcon ?? App.Current.Services.GetService()!.ShowSystemTrayIcon) { - // We need to stash this handle, so it doesn't clean itself up. If - // explorer restarts, we'll come back through here, and we don't - // really need to re-load the icon in that case. We can just use - // the handle from the first time. - _largeIcon = GetAppIconHandle(); - _trayIconData = new NOTIFYICONDATAW() + // We only need to build the tray data once. + if (_trayIconData == null) { - cbSize = (uint)Marshal.SizeOf(typeof(NOTIFYICONDATAW)), - hWnd = _hwnd, - uID = MY_NOTIFY_ID, - uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP, - uCallbackMessage = WM_TRAY_ICON, - hIcon = (HICON)_largeIcon.DangerousGetHandle(), - szTip = RS_.GetString("AppStoreName"), - }; + // We need to stash this handle, so it doesn't clean itself up. If + // explorer restarts, we'll come back through here, and we don't + // really need to re-load the icon in that case. We can just use + // the handle from the first time. + _largeIcon = GetAppIconHandle(); + _trayIconData = new NOTIFYICONDATAW() + { + cbSize = (uint)Marshal.SizeOf(typeof(NOTIFYICONDATAW)), + hWnd = _hwnd, + uID = MY_NOTIFY_ID, + uFlags = NOTIFY_ICON_DATA_FLAGS.NIF_MESSAGE | NOTIFY_ICON_DATA_FLAGS.NIF_ICON | NOTIFY_ICON_DATA_FLAGS.NIF_TIP, + uCallbackMessage = WM_TRAY_ICON, + hIcon = (HICON)_largeIcon.DangerousGetHandle(), + szTip = RS_.GetString("AppStoreName"), + }; + } + + var d = (NOTIFYICONDATAW)_trayIconData; + + // Add the notification icon + PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d); } - - var d = (NOTIFYICONDATAW)_trayIconData; - - // Add the notification icon - if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_ADD, in d)) + else { - _createdIcon = true; + RemoveTrayIcon(); } } - private void RemoveNotificationIcon() + private void RemoveTrayIcon() { - if (_trayIconData != null && _createdIcon) + if (_trayIconData != null) { var d = (NOTIFYICONDATAW)_trayIconData; if (PInvoke.Shell_NotifyIcon(NOTIFY_ICON_MESSAGE.NIM_DELETE, in d)) { - _createdIcon = false; + _trayIconData = null; } } + + _largeIcon?.Close(); } private DestroyIconSafeHandle GetAppIconHandle() { var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location; DestroyIconSafeHandle largeIcon; - DestroyIconSafeHandle smallIcon; - PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out smallIcon, 1); + PInvoke.ExtractIconEx(exePath, 0, out largeIcon, out _, 1); return largeIcon; } } diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj index e1f891558d..c79669e714 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj @@ -23,13 +23,13 @@ false - + true - Microsoft.Terminal.UI + Microsoft.Terminal.UI;CmdPalKeyboardService $(OutDir) @@ -73,12 +73,17 @@ + + + + + diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw index 03e8c40eaa..6c63eeff16 100644 --- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw +++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Strings/en-us/Resources.resw @@ -322,6 +322,18 @@ Right-click to remove the key combination, thereby deactivating the shortcut. This key will open the Command Palette. + + Use low-level keyboard hook + + + Try this if there are issues with the shortcut (Command Palette might not get focus when triggered from an elevated window) + + + Ignore shortcut in fullscreen mode + + + Preventing disruption of the program running in fullscreen by unintentional activation of shortcut + Go home when activated @@ -373,9 +385,6 @@ Right-click to remove the key combination, thereby deactivating the shortcut. Extensions - - More commands - Open Command Palette settings @@ -385,4 +394,22 @@ Right-click to remove the key combination, thereby deactivating the shortcut. Behavior + + Search commands... + + + Show system tray icon + + + Choose if Command Palette is visible in the system tray + + + Back + + + Back + + + More + \ No newline at end of file diff --git a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj index 039bf41817..69e3c64635 100644 --- a/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj +++ b/src/modules/cmdpal/Microsoft.Terminal.UI/Microsoft.Terminal.UI.vcxproj @@ -3,7 +3,7 @@ ..\..\..\..\ - $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.6.250205002 + $(PathToRoot)packages\Microsoft.WindowsAppSDK.1.7.250401001 diff --git a/src/modules/cmdpal/README.md b/src/modules/cmdpal/README.md index 83f37f0c16..2ca4f2f0cf 100644 --- a/src/modules/cmdpal/README.md +++ b/src/modules/cmdpal/README.md @@ -40,7 +40,7 @@ Projects of interest are: [Initial SDK Spec]: ./doc/initial-sdk-spec/initial-sdk-spec.md [generic samples]: ./Exts/SamplePagesExtension -[real samples]: .Exts/ProcessMonitorExtension +[real samples]: ./Exts/ProcessMonitorExtension [real extensions that we've "shipped" already]: https://github.com/zadjii/CmdPalExtensions/blob/main/src/extensions diff --git a/src/modules/cmdpal/doc/command-pal-anatomy/command-palette-anatomy.md b/src/modules/cmdpal/doc/command-pal-anatomy/command-palette-anatomy.md index 2af4771e16..878daaa69a 100644 --- a/src/modules/cmdpal/doc/command-pal-anatomy/command-palette-anatomy.md +++ b/src/modules/cmdpal/doc/command-pal-anatomy/command-palette-anatomy.md @@ -152,7 +152,7 @@ We've made it easy to build a new extension. Just follow these steps: 2. Run the following PowerShell script, replacing "MastodonExtension" with the `Name` of your extension and "Mastodon extension for cmdpal" with the `DisplayName` of the [command that will show up in the root view](#root-view) of the Command Palette: ```powershell -.\Exts\NewExtension.ps1 -name MastodonExtension -DisplayName "Mastodon extension for cmdpal" +.\ext\NewExtension.ps1 -name MastodonExtension -DisplayName "Mastodon extension for cmdpal" ``` 3. Open the solution in Visual Studio. diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsCommandProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs similarity index 80% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs index ed724794fd..f271d4d556 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsPage.cs @@ -57,7 +57,7 @@ public sealed partial class AllAppsPage : ListPage Stopwatch stopwatch = new(); stopwatch.Start(); - List apps = GetPrograms(); + var apps = GetPrograms(); this.allAppsSection = apps .Select((app) => new AppListItem(app, true)) @@ -73,26 +73,15 @@ public sealed partial class AllAppsPage : ListPage internal List GetPrograms() { - IEnumerable uwpResults = AppCache.Instance.Value.UWPs + var uwpResults = AppCache.Instance.Value.UWPs .Where((application) => application.Enabled) - .Select(app => - new AppItem() - { - Name = app.Name, - Subtitle = app.Description, - Type = UWPApplication.Type(), - IcoPath = app.LogoType != LogoType.Error ? app.LogoPath : string.Empty, - DirPath = app.Location, - UserModelId = app.UserModelId, - IsPackaged = true, - Commands = app.GetCommands(), - }); + .Select(UwpToAppItem); - IEnumerable win32Results = AppCache.Instance.Value.Win32s + var win32Results = AppCache.Instance.Value.Win32s .Where((application) => application.Enabled && application.Valid) .Select(app => { - string icoPath = string.IsNullOrEmpty(app.IcoPath) ? + var icoPath = string.IsNullOrEmpty(app.IcoPath) ? (app.AppType == Win32Program.ApplicationType.InternetShortcutApplication ? app.IcoPath : app.FullPath) : @@ -116,4 +105,21 @@ public sealed partial class AllAppsPage : ListPage return uwpResults.Concat(win32Results).OrderBy(app => app.Name).ToList(); } + + private AppItem UwpToAppItem(UWPApplication app) + { + var iconPath = app.LogoType != LogoType.Error ? app.LogoPath : string.Empty; + var item = new AppItem() + { + Name = app.Name, + Subtitle = app.Description, + Type = UWPApplication.Type(), + IcoPath = iconPath, + DirPath = app.Location, + UserModelId = app.UserModelId, + IsPackaged = true, + Commands = app.GetCommands(), + }; + return item; + } } diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AllAppsSettings.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppCache.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCache.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppCache.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCache.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/AppCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/AppCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppListItem.cs similarity index 74% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppListItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppListItem.cs index 1ab8df76ef..57c9175d4d 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/AppListItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/AppListItem.cs @@ -32,7 +32,13 @@ internal sealed partial class AppListItem : ListItem Tags = [_appTag]; MoreCommands = _app.Commands!.ToArray(); - _details = new Lazy
(() => BuildDetails()); + _details = new Lazy
(() => + { + var t = BuildDetails(); + t.Wait(); + return t.Result; + }); + _icon = new Lazy(() => { var t = FetchIcon(useThumbnails); @@ -41,8 +47,9 @@ internal sealed partial class AppListItem : ListItem }); } - private Details BuildDetails() + private async Task
BuildDetails() { + // Build metadata, with app type, path, etc. var metadata = new List(); metadata.Add(new DetailsElement() { Key = "Type", Data = new DetailsTags() { Tags = [new Tag(_app.Type)] } }); if (!_app.IsPackaged) @@ -50,10 +57,33 @@ internal sealed partial class AppListItem : ListItem metadata.Add(new DetailsElement() { Key = "Path", Data = new DetailsLink() { Text = _app.ExePath } }); } + // Icon + IconInfo? heroImage = null; + if (_app.IsPackaged) + { + heroImage = new IconInfo(_app.IcoPath); + } + else + { + try + { + var stream = await ThumbnailHelper.GetThumbnail(_app.ExePath, true); + if (stream != null) + { + heroImage = IconInfo.FromStream(stream); + } + } + catch (Exception) + { + // do nothing if we fail to load an icon. + // Logging it would be too NOISY, there's really no need. + } + } + return new Details() { Title = this.Title, - HeroImage = this.Icon ?? new IconInfo(string.Empty), + HeroImage = heroImage ?? this.Icon ?? new IconInfo(string.Empty), Metadata = metadata.ToArray(), }; } @@ -64,11 +94,6 @@ internal sealed partial class AppListItem : ListItem if (_app.IsPackaged) { icon = new IconInfo(_app.IcoPath); - if (_details.IsValueCreated) - { - _details.Value.HeroImage = icon; - } - return icon; } @@ -94,11 +119,6 @@ internal sealed partial class AppListItem : ListItem icon = new IconInfo(_app.IcoPath); } - if (_details.IsValueCreated) - { - _details.Value.HeroImage = icon; - } - return icon; } } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Assets/AllApps.svg diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/OpenInConsoleCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/OpenInConsoleCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/OpenInConsoleCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/OpenInConsoleCommand.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/OpenPathCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/OpenPathCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/OpenPathCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/OpenPathCommand.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/RunAsAdminCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/RunAsAdminCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/RunAsAdminCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/RunAsAdminCommand.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/RunAsUserCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/RunAsUserCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Commands/RunAsUserCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Commands/RunAsUserCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Microsoft.CmdPal.Ext.Apps.csproj diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/NativeMethods.txt diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxFactory.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/AppxPackageHelper.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/DisabledProgramSource.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/DisabledProgramSource.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/DisabledProgramSource.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/DisabledProgramSource.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IApplicationActivationManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxFactory.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplication.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestApplicationsEnumerator.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestProperties.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IAppxManifestReader.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/IFileVersionInfoWrapper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IFileVersionInfoWrapper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/IFileVersionInfoWrapper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IFileVersionInfoWrapper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IPackage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IPackage.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackageCatalog.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IPackageCatalog.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackageCatalog.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IPackageCatalog.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackageManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IPackageManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IPackageManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IPackageManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IProgram.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IProgram.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/IProgram.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/IProgram.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/LogoType.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/LogoType.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/LogoType.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/LogoType.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageCatalogWrapper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageCatalogWrapper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageCatalogWrapper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageCatalogWrapper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageManagerWrapper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageWrapper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageWrapper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/PackageWrapper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/PackageWrapper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/ProgramSource.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ProgramSource.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/ProgramSource.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ProgramSource.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/ReparsePoint.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWP.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs similarity index 98% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs index 6a4cf94480..7a35400a7a 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/UWPApplication.cs @@ -6,13 +6,11 @@ using System; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; -using System.Runtime.InteropServices; using System.Text; using System.Xml; using Microsoft.CmdPal.Ext.Apps.Commands; using Microsoft.CmdPal.Ext.Apps.Properties; using Microsoft.CmdPal.Ext.Apps.Utils; -using Microsoft.CommandPalette.Extensions; using Microsoft.CommandPalette.Extensions.Toolkit; using static Microsoft.CmdPal.Ext.Apps.Utils.Native; using PackageVersion = Microsoft.CmdPal.Ext.Apps.Programs.UWP.PackageVersion; @@ -314,7 +312,12 @@ public class UWPApplication : IProgram } } - var selectedIconPath = paths.FirstOrDefault(File.Exists); + // By working from the highest resolution to the lowest, we make + // sure that we use the highest quality possible icon for the app. + // + // FirstOrDefault would result in us using the 1x scaled icon + // always, which is usually too small for our needs. + var selectedIconPath = paths.LastOrDefault(File.Exists); if (!string.IsNullOrEmpty(selectedIconPath)) { LogoPath = selectedIconPath; diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Programs/Win32Program.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Properties/Resources.resx diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/EventHandler.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/EventHandler.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/EventHandler.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/EventHandler.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/FileSystemWatcherWrapper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/FileSystemWatcherWrapper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/FileSystemWatcherWrapper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/FileSystemWatcherWrapper.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/IFileSystemWatcherWrapper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/IFileSystemWatcherWrapper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/IFileSystemWatcherWrapper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/IFileSystemWatcherWrapper.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/IProgramRepository.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/IProgramRepository.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/IProgramRepository.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/IProgramRepository.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/IRepository`1.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/IRepository`1.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/IRepository`1.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/IRepository`1.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/ListRepository`1.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/PackageRepository.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramFileSystemWatchers.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Storage/Win32ProgramRepository.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/IShellLinkHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/IShellLinkHelper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/IShellLinkHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/IShellLinkHelper.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Native.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellCommand.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLinkHelper.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ShellLocalization.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/Theme.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Apps/Utils/ThemeHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs similarity index 94% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs index 9b3f54a21f..5da419cd40 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkForm.cs @@ -34,7 +34,7 @@ internal sealed partial class AddBookmarkForm : FormContent "style": "text", "id": "name", "label": "{{Resources.bookmarks_form_name_label}}", - "value": {{JsonSerializer.Serialize(name)}}, + "value": {{JsonSerializer.Serialize(name, BookmarkSerializationContext.Default.String)}}, "isRequired": true, "errorMessage": "{{Resources.bookmarks_form_name_required}}" }, @@ -42,7 +42,7 @@ internal sealed partial class AddBookmarkForm : FormContent "type": "Input.Text", "style": "text", "id": "bookmark", - "value": {{JsonSerializer.Serialize(url)}}, + "value": {{JsonSerializer.Serialize(url, BookmarkSerializationContext.Default.String)}}, "label": "{{Resources.bookmarks_form_bookmark_label}}", "isRequired": true, "errorMessage": "{{Resources.bookmarks_form_bookmark_required}}" diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/AddBookmarkPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Assets/Bookmark.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarkData.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkData.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarkData.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkData.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderForm.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderForm.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderForm.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderForm.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkPlaceholderPage.cs diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkSerializationContext.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkSerializationContext.cs new file mode 100644 index 0000000000..9730bf214d --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarkSerializationContext.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Microsoft.CmdPal.Ext.Bookmarks; + +[JsonSerializable(typeof(float))] +[JsonSerializable(typeof(int))] +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(bool))] +[JsonSerializable(typeof(BookmarkData))] +[JsonSerializable(typeof(Bookmarks))] +[JsonSerializable(typeof(List), TypeInfoPropertyName = "BookmarkList")] +[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true, IncludeFields = true, PropertyNameCaseInsensitive = true, AllowTrailingCommas = true)] +internal sealed partial class BookmarkSerializationContext : JsonSerializerContext +{ +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Bookmarks.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Bookmarks.cs similarity index 85% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Bookmarks.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Bookmarks.cs index 7c3a1dd1e0..8f2e257782 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Bookmarks.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Bookmarks.cs @@ -28,7 +28,7 @@ public sealed class Bookmarks if (!string.IsNullOrEmpty(jsonStringReading)) { - data = JsonSerializer.Deserialize(jsonStringReading, _jsonOptions) ?? new Bookmarks(); + data = JsonSerializer.Deserialize(jsonStringReading, BookmarkSerializationContext.Default.Bookmarks) ?? new Bookmarks(); } } @@ -37,7 +37,7 @@ public sealed class Bookmarks public static void WriteToFile(string path, Bookmarks data) { - var jsonString = JsonSerializer.Serialize(data, _jsonOptions); + var jsonString = JsonSerializer.Serialize(data, BookmarkSerializationContext.Default.Bookmarks); File.WriteAllText(BookmarksCommandProvider.StateJsonPath(), jsonString); } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarksCommandProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarksCommandProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/BookmarksCommandProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/BookmarksCommandProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj similarity index 90% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj index eaa4ac6b42..40c3cca9f2 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Microsoft.CmdPal.Ext.Bookmarks.csproj @@ -1,5 +1,6 @@  + Microsoft.CmdPal.Ext.Bookmarks enable @@ -14,9 +15,9 @@ - + - + Resources.resx @@ -24,7 +25,7 @@ True - + PreserveNewest @@ -39,5 +40,5 @@ PublicResXFileCodeGenerator - + diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/OpenInTerminalCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/OpenInTerminalCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/OpenInTerminalCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/OpenInTerminalCommand.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/Properties/Resources.resx diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/UrlCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/UrlCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Bookmark/UrlCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Bookmark/UrlCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Assets/Calculator.svg diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/CalculatorCommandProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/CalculatorCommandProvider.cs new file mode 100644 index 0000000000..1478dbbd45 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/CalculatorCommandProvider.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.Ext.Calc.Helper; +using Microsoft.CmdPal.Ext.Calc.Pages; +using Microsoft.CmdPal.Ext.Calc.Properties; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Calc; + +public partial class CalculatorCommandProvider : CommandProvider +{ + private readonly ListItem _listItem = new(new CalculatorListPage(settings)) + { + Subtitle = Resources.calculator_top_level_subtitle, + MoreCommands = [new CommandContextItem(settings.Settings.SettingsPage)], + }; + + private readonly FallbackCalculatorItem _fallback = new(settings); + private static SettingsManager settings = new(); + + public CalculatorCommandProvider() + { + Id = "Calculator"; + DisplayName = Resources.calculator_display_name; + Icon = CalculatorIcons.ProviderIcon; + Settings = settings.Settings; + } + + public override ICommandItem[] TopLevelCommands() => [_listItem]; + + public override IFallbackCommandItem[] FallbackCommands() => [_fallback]; +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/BracketHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/BracketHelper.cs new file mode 100644 index 0000000000..67d8940ed4 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/BracketHelper.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public static class BracketHelper +{ + public static bool IsBracketComplete(string query) + { + if (string.IsNullOrWhiteSpace(query)) + { + return true; + } + + var valueTuples = query + .Select(BracketTrail) + .Where(r => r != default); + + var trailTest = new Stack(); + + foreach (var (direction, type) in valueTuples) + { + switch (direction) + { + case TrailDirection.Open: + trailTest.Push(type); + break; + case TrailDirection.Close: + // Try to get item out of stack + if (!trailTest.TryPop(out var popped)) + { + return false; + } + + if (type != popped) + { + return false; + } + + continue; + default: + { + throw new ArgumentOutOfRangeException($"Can't process value (Parameter direction: {direction})"); + } + } + } + + return trailTest.Count == 0; + } + + private static (TrailDirection Direction, TrailType Type) BracketTrail(char @char) + { + switch (@char) + { + case '(': + return (TrailDirection.Open, TrailType.Round); + case ')': + return (TrailDirection.Close, TrailType.Round); + case '[': + return (TrailDirection.Open, TrailType.Bracket); + case ']': + return (TrailDirection.Close, TrailType.Bracket); + default: + return default; + } + } + + private enum TrailDirection + { + None, + Open, + Close, + } + + private enum TrailType + { + None, + Bracket, + Round, + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs new file mode 100644 index 0000000000..0d6f9536db --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateEngine.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text.RegularExpressions; + +using Mages.Core; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public static class CalculateEngine +{ + private static readonly Engine _magesEngine = new Engine(new Configuration + { + Scope = new Dictionary + { + { "e", Math.E }, // e is not contained in the default mages engine + }, + }); + + public const int RoundingDigits = 10; + + public enum TrigMode + { + Radians, + Degrees, + Gradians, + } + + /// + /// Interpret + /// + /// Use CultureInfo.CurrentCulture if something is user facing + public static CalculateResult Interpret(SettingsManager settings, string input, CultureInfo cultureInfo, out string error) + { + error = default; + + if (!CalculateHelper.InputValid(input)) + { + return default; + } + + // check for division by zero + // We check if the string contains a slash followed by space (optional) and zero. Whereas the zero must not be followed by a dot, comma, 'b', 'o' or 'x' as these indicate a number with decimal digits or a binary/octal/hexadecimal value respectively. The zero must also not be followed by other digits. + if (new Regex("\\/\\s*0(?!(?:[,\\.0-9]|[box]0*[1-9a-f]))", RegexOptions.IgnoreCase).Match(input).Success) + { + error = Properties.Resources.calculator_division_by_zero; + return default; + } + + // mages has quirky log representation + // mage has log == ln vs log10 + input = input. + Replace("log(", "log10(", true, CultureInfo.CurrentCulture). + Replace("ln(", "log(", true, CultureInfo.CurrentCulture); + + input = CalculateHelper.FixHumanMultiplicationExpressions(input); + + // Get the user selected trigonometry unit + TrigMode trigMode = settings.TrigUnit; + + // Modify trig functions depending on angle unit setting + input = CalculateHelper.UpdateTrigFunctions(input, trigMode); + + // Expand conversions between trig units + input = CalculateHelper.ExpandTrigConversions(input, trigMode); + + var result = _magesEngine.Interpret(input); + + // This could happen for some incorrect queries, like pi(2) + if (result == null) + { + error = Properties.Resources.calculator_expression_not_complete; + return default; + } + + result = TransformResult(result); + if (result is string) + { + error = result as string; + return default; + } + + if (string.IsNullOrEmpty(result?.ToString())) + { + return default; + } + + var decimalResult = Convert.ToDecimal(result, cultureInfo); + var roundedResult = Round(decimalResult); + + return new CalculateResult() + { + Result = decimalResult, + RoundedResult = roundedResult, + }; + } + + public static decimal Round(decimal value) + { + return Math.Round(value, RoundingDigits, MidpointRounding.AwayFromZero); + } + + private static dynamic TransformResult(object result) + { + if (result.ToString() == "NaN") + { + return Properties.Resources.calculator_not_a_number; + } + + if (result is Function) + { + return Properties.Resources.calculator_expression_not_complete; + } + + if (result is double[,]) + { + // '[10,10]' is interpreted as array by mages engine + return Properties.Resources.calculator_double_array_returned; + } + + return result; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs new file mode 100644 index 0000000000..acab3d7b96 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculateHelper.cs @@ -0,0 +1,328 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Text.RegularExpressions; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public static class CalculateHelper +{ + private static readonly Regex RegValidExpressChar = new Regex( + @"^(" + + @"%|" + + @"ceil\s*\(|floor\s*\(|exp\s*\(|max\s*\(|min\s*\(|abs\s*\(|log(?:2|10)?\s*\(|ln\s*\(|sqrt\s*\(|pow\s*\(|" + + @"factorial\s*\(|sign\s*\(|round\s*\(|rand\s*\(\)|randi\s*\([^\)]|" + + @"sin\s*\(|cos\s*\(|tan\s*\(|arcsin\s*\(|arccos\s*\(|arctan\s*\(|" + + @"sinh\s*\(|cosh\s*\(|tanh\s*\(|arsinh\s*\(|arcosh\s*\(|artanh\s*\(|" + + @"rad\s*\(|deg\s*\(|grad\s*\(|" + /* trigonometry unit conversion macros */ + @"pi|" + + @"==|~=|&&|\|\||" + + @"((-?(\d+(\.\d*)?)|-?(\.\d+))[Ee](-?\d+))|" + /* expression from CheckScientificNotation between parenthesis */ + @"e|[0-9]|0[xX][0-9a-fA-F]+|0[bB][01]+|0[oO][0-7]+|[\+\-\*\/\^\., ""]|[\(\)\|\!\[\]]" + + @")+$", + RegexOptions.Compiled); + + private const string DegToRad = "(pi / 180) * "; + private const string DegToGrad = "(10 / 9) * "; + private const string GradToRad = "(pi / 200) * "; + private const string GradToDeg = "(9 / 10) * "; + private const string RadToDeg = "(180 / pi) * "; + private const string RadToGrad = "(200 / pi) * "; + + public static bool InputValid(string input) + { + if (string.IsNullOrWhiteSpace(input)) + { + throw new ArgumentNullException(paramName: nameof(input)); + } + + if (!RegValidExpressChar.IsMatch(input)) + { + return false; + } + + if (!BracketHelper.IsBracketComplete(input)) + { + return false; + } + + // If the input ends with a binary operator then it is not a valid input to mages and the Interpret function would throw an exception. Because we expect here that the user has not finished typing we block those inputs. + var trimmedInput = input.TrimEnd(); + if (trimmedInput.EndsWith('+') || trimmedInput.EndsWith('-') || trimmedInput.EndsWith('*') || trimmedInput.EndsWith('|') || trimmedInput.EndsWith('\\') || trimmedInput.EndsWith('^') || trimmedInput.EndsWith('=') || trimmedInput.EndsWith('&') || trimmedInput.EndsWith('/') || trimmedInput.EndsWith('%')) + { + return false; + } + + return true; + } + + public static string FixHumanMultiplicationExpressions(string input) + { + var output = CheckScientificNotation(input); + output = CheckNumberOrConstantThenParenthesisExpr(output); + output = CheckNumberOrConstantThenFunc(output); + output = CheckParenthesisExprThenFunc(output); + output = CheckParenthesisExprThenParenthesisExpr(output); + output = CheckNumberThenConstant(output); + output = CheckConstantThenConstant(output); + return output; + } + + private static string CheckScientificNotation(string input) + { + /** + * NOTE: By the time the expression gets to us, it's already in English format. + * + * Regex explanation: + * (-?(\d+({0}\d*)?)|-?({0}\d+)): Used to capture one of two types: + * -?(\d+({0}\d*)?): Captures a decimal number starting with a number (e.g. "-1.23") + * -?({0}\d+): Captures a decimal number without leading number (e.g. ".23") + * e: Captures 'e' or 'E' + * (-?\d+): Captures an integer number (e.g. "-1" or "23") + */ + var p = @"(-?(\d+(\.\d*)?)|-?(\.\d+))e(-?\d+)"; + return Regex.Replace(input, p, "($1 * 10^($5))", RegexOptions.IgnoreCase); + } + + /* + * num (exp) + * const (exp) + */ + private static string CheckNumberOrConstantThenParenthesisExpr(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+|pi|e)\s*(\()", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + /* + * num func + * const func + */ + private static string CheckNumberOrConstantThenFunc(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+|pi|e)\s*([a-zA-Z]+[0-9]*\s*\()", m => + { + if (input[m.Index] == 'e' && input[m.Index + 1] == 'x' && input[m.Index + 2] == 'p') + { + return m.Value; + } + + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + /* + * (exp) func + * func func + */ + private static string CheckParenthesisExprThenFunc(string input) + { + var p = @"(\))\s*([a-zA-Z]+[0-9]*\s*\()"; + var r = "$1 * $2"; + return Regex.Replace(input, p, r); + } + + /* + * (exp) (exp) + * func (exp) + */ + private static string CheckParenthesisExprThenParenthesisExpr(string input) + { + var p = @"(\))\s*(\()"; + var r = "$1 * $2"; + return Regex.Replace(input, p, r); + } + + /* + * num const + */ + private static string CheckNumberThenConstant(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+)\s*(pi|e)", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + /* + * const const + */ + private static string CheckConstantThenConstant(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(pi|e)\s*(pi|e)", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + // Gets the index of the closing bracket of a function + private static int FindClosingBracketIndex(string input, int start) + { + var bracketCount = 0; // Set count to zero + for (var i = start; i < input.Length; i++) + { + if (input[i] == '(') + { + bracketCount++; + } + else if (input[i] == ')') + { + bracketCount--; + if (bracketCount == 0) + { + return i; + } + } + } + + return -1; // Unmatched brackets + } + + private static string ModifyTrigFunction(string input, string function, string modification) + { + // Get the RegEx pattern to match, depending on whether the function is inverse or normal + var pattern = function.StartsWith("arc", StringComparison.Ordinal) ? string.Empty : @"(? +{ + public decimal? Result { get; set; } + + public decimal? RoundedResult { get; set; } + + public bool Equals(CalculateResult other) + { + return Result == other.Result && RoundedResult == other.RoundedResult; + } + + public override bool Equals(object obj) + { + return obj is CalculateResult other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Result, RoundedResult); + } + + public static bool operator ==(CalculateResult left, CalculateResult right) + { + return left.Equals(right); + } + + public static bool operator !=(CalculateResult left, CalculateResult right) + { + return !(left == right); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculatorIcons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculatorIcons.cs new file mode 100644 index 0000000000..e3be5f2149 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/CalculatorIcons.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public static class CalculatorIcons +{ + public static IconInfo ResultIcon => new("\uE94E"); + + public static IconInfo SaveIcon => new("\uE74E"); + + public static IconInfo ErrorIcon => new("\uE783"); + + public static IconInfo ProviderIcon => IconHelpers.FromRelativePath("Assets\\Calculator.svg"); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/ErrorHandler.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/ErrorHandler.cs new file mode 100644 index 0000000000..b3948dc854 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/ErrorHandler.cs @@ -0,0 +1,53 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using ManagedCommon; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +internal static class ErrorHandler +{ + /// + /// Method to handles errors while calculating + /// + /// Bool to indicate if it is a fallback query. + /// User input as string including the action keyword. + /// Error message if applicable. + /// Exception if applicable. + /// List of results to show. Either an error message or an empty list. + /// Thrown if and are both filled with their default values. + internal static ListItem OnError(bool isFallbackSearch, string queryInput, string errorMessage, Exception exception = default) + { + string userMessage; + + if (errorMessage != default) + { + Logger.LogError($"Failed to calculate <{queryInput}>: {errorMessage}"); + userMessage = errorMessage; + } + else if (exception != default) + { + Logger.LogError($"Exception when query for <{queryInput}>", exception); + userMessage = exception.Message; + } + else + { + throw new ArgumentException("The arguments error and exception have default values. One of them has to be filled with valid error data (error message/exception)!"); + } + + return isFallbackSearch ? null : CreateErrorResult(userMessage); + } + + private static ListItem CreateErrorResult(string errorMessage) + { + return new ListItem(new NoOpCommand()) + { + Title = Properties.Resources.calculator_calculation_failed_title, + Subtitle = errorMessage, + Icon = CalculatorIcons.ErrorIcon, + }; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs new file mode 100644 index 0000000000..8de77ebdae --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/NumberTranslator.cs @@ -0,0 +1,144 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +/// +/// Tries to convert all numbers in a text from one culture format to another. +/// +public class NumberTranslator +{ + private readonly CultureInfo sourceCulture; + private readonly CultureInfo targetCulture; + private readonly Regex splitRegexForSource; + private readonly Regex splitRegexForTarget; + + private NumberTranslator(CultureInfo sourceCulture, CultureInfo targetCulture) + { + this.sourceCulture = sourceCulture; + this.targetCulture = targetCulture; + + splitRegexForSource = GetSplitRegex(this.sourceCulture); + splitRegexForTarget = GetSplitRegex(this.targetCulture); + } + + /// + /// Create a new . + /// + /// source culture + /// target culture + /// Number translator for target culture + public static NumberTranslator Create(CultureInfo sourceCulture, CultureInfo targetCulture) + { + ArgumentNullException.ThrowIfNull(sourceCulture); + + ArgumentNullException.ThrowIfNull(targetCulture); + + return new NumberTranslator(sourceCulture, targetCulture); + } + + /// + /// Translate from source to target culture. + /// + /// input string to translate + /// translated string + public string Translate(string input) + { + return Translate(input, sourceCulture, targetCulture, splitRegexForSource); + } + + /// + /// Translate from target to source culture. + /// + /// input string to translate back to source culture + /// source culture string + public string TranslateBack(string input) + { + return Translate(input, targetCulture, sourceCulture, splitRegexForTarget); + } + + private static string Translate(string input, CultureInfo cultureFrom, CultureInfo cultureTo, Regex splitRegex) + { + var outputBuilder = new StringBuilder(); + var hexRegex = new Regex(@"(?:(0x[\da-fA-F]+))"); + + var hexTokens = hexRegex.Split(input); + + foreach (var hexToken in hexTokens) + { + if (hexToken.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) + { + // Mages engine has issues processing large hex number (larger than 7 hex digits + 0x prefix = 9 characters). So we convert it to decimal and pass it to the engine. + if (hexToken.Length > 9) + { + try + { + var num = Convert.ToInt64(hexToken, 16); + var numStr = num.ToString(cultureFrom); + outputBuilder.Append(numStr); + } + catch (Exception) + { + outputBuilder.Append(hexToken); + } + } + else + { + outputBuilder.Append(hexToken); + } + + continue; + } + + var tokens = splitRegex.Split(hexToken); + foreach (var token in tokens) + { + var leadingZeroCount = 0; + + // Count leading zero characters. + foreach (var c in token) + { + if (c != '0') + { + break; + } + + leadingZeroCount++; + } + + // number is all zero characters. no need to add zero characters at the end. + if (token.Length == leadingZeroCount) + { + leadingZeroCount = 0; + } + + decimal number; + + outputBuilder.Append( + decimal.TryParse(token, NumberStyles.Number, cultureFrom, out number) + ? (new string('0', leadingZeroCount) + number.ToString(cultureTo)) + : token.Replace(cultureFrom.TextInfo.ListSeparator, cultureTo.TextInfo.ListSeparator)); + } + } + + return outputBuilder.ToString(); + } + + private static Regex GetSplitRegex(CultureInfo culture) + { + var splitPattern = $"((?:\\d|{Regex.Escape(culture.NumberFormat.NumberDecimalSeparator)}"; + if (!string.IsNullOrEmpty(culture.NumberFormat.NumberGroupSeparator)) + { + splitPattern += $"|{Regex.Escape(culture.NumberFormat.NumberGroupSeparator)}"; + } + + splitPattern += ")+)"; + return new Regex(splitPattern); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs new file mode 100644 index 0000000000..6b1e62ae03 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/QueryHelper.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.Text; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.Foundation; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public static partial class QueryHelper +{ + public static ListItem Query(string query, SettingsManager settings, bool isFallbackSearch, TypedEventHandler handleSave = null) + { + ArgumentNullException.ThrowIfNull(query); + if (!isFallbackSearch) + { + ArgumentNullException.ThrowIfNull(handleSave); + } + + CultureInfo inputCulture = settings.InputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; + CultureInfo outputCulture = settings.OutputUseEnglishFormat ? new CultureInfo("en-us") : CultureInfo.CurrentCulture; + + // Happens if the user has only typed the action key so far + if (string.IsNullOrEmpty(query)) + { + return null; + } + + NumberTranslator translator = NumberTranslator.Create(inputCulture, new CultureInfo("en-US")); + var input = translator.Translate(query.Normalize(NormalizationForm.FormKC)); + + if (!CalculateHelper.InputValid(input)) + { + return null; + } + + try + { + // Using CurrentUICulture since this is user facing + var result = CalculateEngine.Interpret(settings, input, outputCulture, out var errorMessage); + + // This could happen for some incorrect queries, like pi(2) + if (result.Equals(default(CalculateResult))) + { + // If errorMessage is not default then do error handling + return errorMessage == default ? null : ErrorHandler.OnError(isFallbackSearch, query, errorMessage); + } + + if (isFallbackSearch) + { + // Fallback search + return ResultHelper.CreateResult(result.RoundedResult, inputCulture, outputCulture, query); + } + + return ResultHelper.CreateResult(result.RoundedResult, inputCulture, outputCulture, query, handleSave); + } + catch (Mages.Core.ParseException) + { + // Invalid input + return ErrorHandler.OnError(isFallbackSearch, query, Properties.Resources.calculator_expression_not_complete); + } + catch (OverflowException) + { + // Result to big to convert to decimal + return ErrorHandler.OnError(isFallbackSearch, query, Properties.Resources.calculator_not_covert_to_decimal); + } + catch (Exception e) + { + // Any other crash occurred + // We want to keep the process alive if any the mages library throws any exceptions. + return ErrorHandler.OnError(isFallbackSearch, query, default, e); + } + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/ResultHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/ResultHelper.cs new file mode 100644 index 0000000000..0fab0a8245 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/ResultHelper.cs @@ -0,0 +1,103 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Globalization; +using ManagedCommon; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.Foundation; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public static class ResultHelper +{ + public static ListItem CreateResult(decimal? roundedResult, CultureInfo inputCulture, CultureInfo outputCulture, string query, TypedEventHandler handleSave) + { + // Return null when the expression is not a valid calculator query. + if (roundedResult == null) + { + return null; + } + + var result = roundedResult?.ToString(outputCulture); + + // Create a SaveCommand and subscribe to the SaveRequested event + // This can append the result to the history list. + var saveCommand = new SaveCommand(result); + saveCommand.SaveRequested += handleSave; + + var copyCommandItem = CreateResult(roundedResult, inputCulture, outputCulture, query); + + return new ListItem(saveCommand) + { + // Using CurrentCulture since this is user facing + Icon = CalculatorIcons.ResultIcon, + Title = result, + Subtitle = query, + TextToSuggest = result, + MoreCommands = [ + new CommandContextItem(copyCommandItem.Command) + { + Icon = copyCommandItem.Icon, + Title = copyCommandItem.Title, + Subtitle = copyCommandItem.Subtitle, + }, + ..copyCommandItem.MoreCommands, + ], + }; + } + + public static ListItem CreateResult(decimal? roundedResult, CultureInfo inputCulture, CultureInfo outputCulture, string query) + { + // Return null when the expression is not a valid calculator query. + if (roundedResult == null) + { + return null; + } + + var decimalResult = roundedResult?.ToString(outputCulture); + + List context = []; + + if (decimal.IsInteger((decimal)roundedResult)) + { + var i = decimal.ToInt64((decimal)roundedResult); + try + { + var hexResult = "0x" + i.ToString("X", outputCulture); + context.Add(new CommandContextItem(new CopyTextCommand(hexResult) { Name = Properties.Resources.calculator_copy_hex }) + { + Title = hexResult, + }); + } + catch (Exception ex) + { + Logger.LogError("Error parsing hex format", ex); + } + + try + { + var binaryResult = "0b" + i.ToString("B", outputCulture); + context.Add(new CommandContextItem(new CopyTextCommand(binaryResult) { Name = Properties.Resources.calculator_copy_binary }) + { + Title = binaryResult, + }); + } + catch (Exception ex) + { + Logger.LogError("Error parsing binary format", ex); + } + } + + return new ListItem(new CopyTextCommand(decimalResult)) + { + // Using CurrentCulture since this is user facing + Title = decimalResult, + Subtitle = query, + TextToSuggest = decimalResult, + MoreCommands = context.ToArray(), + }; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/SaveCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/SaveCommand.cs new file mode 100644 index 0000000000..850f8511e3 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/SaveCommand.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.Ext.Calc.Properties; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.Foundation; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public sealed partial class SaveCommand : InvokableCommand +{ + private readonly string _result; + + public event TypedEventHandler SaveRequested; + + public SaveCommand(string result) + { + Name = Resources.calculator_save_command_name; + Icon = CalculatorIcons.SaveIcon; + _result = result; + } + + public override ICommandResult Invoke() + { + SaveRequested?.Invoke(this, this); + ClipboardHelper.SetText(_result); + return CommandResult.KeepOpen(); + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/SettingsManager.cs new file mode 100644 index 0000000000..e106123f5e --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Helper/SettingsManager.cs @@ -0,0 +1,98 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.IO; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Calc.Helper; + +public class SettingsManager : JsonSettingsManager +{ + private static readonly string _namespace = "calculator"; + + private static string Namespaced(string propertyName) => $"{_namespace}.{propertyName}"; + + private static readonly List _trigUnitChoices = new() + { + new ChoiceSetSetting.Choice(Properties.Resources.calculator_settings_trig_unit_radians, "0"), + new ChoiceSetSetting.Choice(Properties.Resources.calculator_settings_trig_unit_degrees, "1"), + new ChoiceSetSetting.Choice(Properties.Resources.calculator_settings_trig_unit_gradians, "2"), + }; + + private readonly ChoiceSetSetting _trigUnit = new( + Namespaced(nameof(TrigUnit)), + Properties.Resources.calculator_settings_trig_unit_mode, + Properties.Resources.calculator_settings_trig_unit_mode_description, + _trigUnitChoices); + + private readonly ToggleSetting _inputUseEnNumberFormat = new( + Namespaced(nameof(InputUseEnglishFormat)), + Properties.Resources.calculator_settings_in_en_format, + Properties.Resources.calculator_settings_in_en_format_description, + false); + + private readonly ToggleSetting _outputUseEnNumberFormat = new( + Namespaced(nameof(OutputUseEnglishFormat)), + Properties.Resources.calculator_settings_out_en_format, + Properties.Resources.calculator_settings_out_en_format_description, + false); + + public CalculateEngine.TrigMode TrigUnit + { + get + { + if (_trigUnit.Value == null || string.IsNullOrEmpty(_trigUnit.Value)) + { + return CalculateEngine.TrigMode.Radians; + } + + var success = int.TryParse(_trigUnit.Value, out var result); + + if (!success) + { + return CalculateEngine.TrigMode.Radians; + } + + switch (result) + { + case 0: + return CalculateEngine.TrigMode.Radians; + case 1: + return CalculateEngine.TrigMode.Degrees; + case 2: + return CalculateEngine.TrigMode.Gradians; + default: + return CalculateEngine.TrigMode.Radians; + } + } + } + + public bool InputUseEnglishFormat => _inputUseEnNumberFormat.Value; + + public bool OutputUseEnglishFormat => _outputUseEnNumberFormat.Value; + + internal static string SettingsJsonPath() + { + var directory = Utilities.BaseSettingsPath("Microsoft.CmdPal"); + Directory.CreateDirectory(directory); + + // now, the state is just next to the exe + return Path.Combine(directory, "settings.json"); + } + + public SettingsManager() + { + FilePath = SettingsJsonPath(); + + Settings.Add(_trigUnit); + Settings.Add(_inputUseEnNumberFormat); + Settings.Add(_outputUseEnNumberFormat); + + // Load settings from file upon initialization + LoadSettings(); + + Settings.SettingsChanged += (s, a) => this.SaveSettings(); + } +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj similarity index 87% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj index 68a38c1ca2..59f5ccbc70 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Microsoft.CmdPal.Ext.Calc.csproj @@ -1,5 +1,6 @@  + Microsoft.CmdPal.Ext.Calc $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal @@ -9,9 +10,14 @@ Microsoft.CmdPal.Ext.Calc.pri + + + + + Resources.resx diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Pages/CalculatorListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Pages/CalculatorListPage.cs new file mode 100644 index 0000000000..24f26646c5 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Pages/CalculatorListPage.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Threading; +using Microsoft.CmdPal.Ext.Calc.Helper; +using Microsoft.CmdPal.Ext.Calc.Properties; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Calc.Pages; + +// The calculator page is a dynamic list page +// * The first command is where we display the results. Title=result, Subtitle=query +// - The default command is `SaveCommand`. +// - When you save, insert into list at spot 1 +// - change SearchText to the result +// - MoreCommands: a single `CopyCommand` to copy the result to the clipboard +// * The rest of the items are previously saved results +// - Command is a CopyCommand +// - Each item also sets the TextToSuggest to the result +public sealed partial class CalculatorListPage : DynamicListPage +{ + private readonly Lock _resultsLock = new(); + private readonly SettingsManager _settingsManager; + private readonly List _items = []; + private readonly List history = []; + private readonly ListItem _emptyItem; + + // This is the text that saved when the user click the result. + // We need to avoid the double calculation. This may cause some wierd behaviors. + private string skipQuerySearchText = string.Empty; + + public CalculatorListPage(SettingsManager settings) + { + _settingsManager = settings; + Icon = CalculatorIcons.ProviderIcon; + Name = Resources.calculator_title; + PlaceholderText = Resources.calculator_placeholder_text; + Id = "com.microsoft.cmdpal.calculator"; + + _emptyItem = new ListItem(new NoOpCommand()) + { + Title = Resources.calculator_placeholder_text, + Icon = CalculatorIcons.ResultIcon, + }; + EmptyContent = new CommandItem(new NoOpCommand()) + { + Icon = CalculatorIcons.ProviderIcon, + Title = Resources.calculator_placeholder_text, + }; + + UpdateSearchText(string.Empty, string.Empty); + } + + public override void UpdateSearchText(string oldSearch, string newSearch) + { + if (oldSearch == newSearch) + { + return; + } + + if (!string.IsNullOrEmpty(skipQuerySearchText) && newSearch == skipQuerySearchText) + { + // only skip once. + skipQuerySearchText = string.Empty; + return; + } + + skipQuerySearchText = string.Empty; + + _emptyItem.Subtitle = newSearch; + + var result = QueryHelper.Query(newSearch, _settingsManager, false, HandleSave); + UpdateResult(result); + } + + private void UpdateResult(ListItem result) + { + lock (_resultsLock) + { + this._items.Clear(); + + if (result != null) + { + this._items.Add(result); + } + else + { + _items.Add(_emptyItem); + } + + this._items.AddRange(history); + } + + RaiseItemsChanged(this._items.Count); + } + + private void HandleSave(object sender, object args) + { + var lastResult = _items[0].Title; + if (!string.IsNullOrEmpty(lastResult)) + { + var li = new ListItem(new CopyTextCommand(lastResult)) + { + Title = _items[0].Title, + Subtitle = _items[0].Subtitle, + TextToSuggest = lastResult, + }; + + history.Insert(0, li); + _items.Insert(1, li); + + // Why we need to clean the query record? Removed, but if necessary, please move it back. + // _items[0].Subtitle = string.Empty; + + // this change will call the UpdateSearchText again. + // We need to avoid it. + skipQuerySearchText = lastResult; + SearchText = lastResult; + this.RaiseItemsChanged(this._items.Count); + } + } + + public override IListItem[] GetItems() => _items.ToArray(); +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Pages/FallbackCalculatorItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Pages/FallbackCalculatorItem.cs new file mode 100644 index 0000000000..b309f4ed3a --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Pages/FallbackCalculatorItem.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.Ext.Calc.Helper; +using Microsoft.CmdPal.Ext.Calc.Properties; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.Calc.Pages; + +public sealed partial class FallbackCalculatorItem : FallbackCommandItem +{ + private readonly CopyTextCommand _copyCommand = new(string.Empty); + private readonly SettingsManager _settings; + + public FallbackCalculatorItem(SettingsManager settings) + : base(new NoOpCommand(), Resources.calculator_title) + { + Command = _copyCommand; + _copyCommand.Name = string.Empty; + Title = string.Empty; + Subtitle = Resources.calculator_placeholder_text; + Icon = CalculatorIcons.ProviderIcon; + _settings = settings; + } + + public override void UpdateQuery(string query) + { + var result = QueryHelper.Query(query, _settings, true, null); + + if (result == null) + { + _copyCommand.Text = string.Empty; + _copyCommand.Name = string.Empty; + Title = string.Empty; + Subtitle = string.Empty; + MoreCommands = []; + return; + } + + _copyCommand.Text = result.Title; + _copyCommand.Name = string.IsNullOrWhiteSpace(query) ? string.Empty : Resources.calculator_copy_command_name; + Title = result.Title; + + // we have to make the subtitle the equation, + // so that we will still string match the original query + // Otherwise, something like 1+2 will have a title of "3" and not match + Subtitle = query; + + MoreCommands = result.MoreCommands; + } +} diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..8cd385be32 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.Designer.cs @@ -0,0 +1,297 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.CmdPal.Ext.Calc.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.CmdPal.Ext.Calc.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Failed to calculate the input. + /// + public static string calculator_calculation_failed_title { + get { + return ResourceManager.GetString("calculator_calculation_failed_title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy binary. + /// + public static string calculator_copy_binary { + get { + return ResourceManager.GetString("calculator_copy_binary", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy. + /// + public static string calculator_copy_command_name { + get { + return ResourceManager.GetString("calculator_copy_command_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Copy hexadecimal. + /// + public static string calculator_copy_hex { + get { + return ResourceManager.GetString("calculator_copy_hex", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calculator. + /// + public static string calculator_display_name { + get { + return ResourceManager.GetString("calculator_display_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expression contains division by zero. + /// + public static string calculator_division_by_zero { + get { + return ResourceManager.GetString("calculator_division_by_zero", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unsupported use of square brackets. + /// + public static string calculator_double_array_returned { + get { + return ResourceManager.GetString("calculator_double_array_returned", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error: {0}. + /// + public static string calculator_error { + get { + return ResourceManager.GetString("calculator_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expression wrong or incomplete. + /// + public static string calculator_expression_not_complete { + get { + return ResourceManager.GetString("calculator_expression_not_complete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calculation result is not a valid number (NaN). + /// + public static string calculator_not_a_number { + get { + return ResourceManager.GetString("calculator_not_a_number", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Result value was either too large or too small for a decimal number. + /// + public static string calculator_not_covert_to_decimal { + get { + return ResourceManager.GetString("calculator_not_covert_to_decimal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type an equation.... + /// + public static string calculator_placeholder_text { + get { + return ResourceManager.GetString("calculator_placeholder_text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Save. + /// + public static string calculator_save_command_name { + get { + return ResourceManager.GetString("calculator_save_command_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use English (United States) number format for input. + /// + public static string calculator_settings_in_en_format { + get { + return ResourceManager.GetString("calculator_settings_in_en_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignores your system setting and expects numbers in the format '{0}'.. + /// + public static string calculator_settings_in_en_format_description { + get { + return ResourceManager.GetString("calculator_settings_in_en_format_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use English (United States) number format for output. + /// + public static string calculator_settings_out_en_format { + get { + return ResourceManager.GetString("calculator_settings_out_en_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Ignores your system setting and returns numbers in the format '{0}'.. + /// + public static string calculator_settings_out_en_format_description { + get { + return ResourceManager.GetString("calculator_settings_out_en_format_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Replace input if query ends with '='. + /// + public static string calculator_settings_replace_input { + get { + return ResourceManager.GetString("calculator_settings_replace_input", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to When using direct activation, appending '=' to the expression will replace the input with the calculated result (e.g. '=5*3-2=' will change the query to '=13').. + /// + public static string calculator_settings_replace_input_description { + get { + return ResourceManager.GetString("calculator_settings_replace_input_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Degrees. + /// + public static string calculator_settings_trig_unit_degrees { + get { + return ResourceManager.GetString("calculator_settings_trig_unit_degrees", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Gradians. + /// + public static string calculator_settings_trig_unit_gradians { + get { + return ResourceManager.GetString("calculator_settings_trig_unit_gradians", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trigonometry Unit. + /// + public static string calculator_settings_trig_unit_mode { + get { + return ResourceManager.GetString("calculator_settings_trig_unit_mode", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specifies the angle unit to use for trigonometry operations. + /// + public static string calculator_settings_trig_unit_mode_description { + get { + return ResourceManager.GetString("calculator_settings_trig_unit_mode_description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Radians. + /// + public static string calculator_settings_trig_unit_radians { + get { + return ResourceManager.GetString("calculator_settings_trig_unit_radians", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Calculator. + /// + public static string calculator_title { + get { + return ResourceManager.GetString("calculator_title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Press = to type an equation. + /// + public static string calculator_top_level_subtitle { + get { + return ResourceManager.GetString("calculator_top_level_subtitle", resourceCulture); + } + } + } +} diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx similarity index 69% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx index 580f704433..3c50d3a1c5 100644 --- a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Calc/Properties/Resources.resx @@ -140,4 +140,63 @@ Copy + + Failed to calculate the input + + + Expression contains division by zero + + + Expression wrong or incomplete + + + Calculation result is not a valid number (NaN) + + + Unsupported use of square brackets + + + Gradians + + + Degrees + + + Radians + + + Trigonometry Unit + + + Specifies the angle unit to use for trigonometry operations + + + Use English (United States) number format for output + + + Ignores your system setting and returns numbers in the format '{0}'. + {0} is a placeholder and will be replaced in code. + + + Use English (United States) number format for input + + + Ignores your system setting and expects numbers in the format '{0}'. + {0} is a placeholder and will be replaced in code. + + + Replace input if query ends with '=' + + + When using direct activation, appending '=' to the expression will replace the input with the calculated result (e.g. '=5*3-2=' will change the query to '=13'). + + + Result value was either too large or too small for a decimal number + + + Copy hexadecimal + + + Copy binary + \ No newline at end of file diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/ClipboardHistoryCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/ClipboardHistoryCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/ClipboardHistoryCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/ClipboardHistoryCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/CopyCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/CopyCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/CopyCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/CopyCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/PasteCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/PasteCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/PasteCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Commands/PasteCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/ClipboardHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs similarity index 98% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs index 50ff346103..f4b6089229 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Helpers/NativeMethods.cs @@ -17,7 +17,7 @@ internal static class NativeMethods internal INPUTTYPE type; internal InputUnion data; - internal static int Size => Marshal.SizeOf(typeof(INPUT)); + internal static int Size => Marshal.SizeOf(); } [StructLayout(LayoutKind.Explicit)] diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj similarity index 92% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj index 1d583e279b..774753f31d 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Microsoft.CmdPal.Ext.ClipboardHistory.csproj @@ -1,5 +1,6 @@  + Microsoft.CmdPal.Ext.ClipboardHistory $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardFormat.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardFormat.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardFormat.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardFormat.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Models/ClipboardItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Pages/ClipboardHistoryListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Pages/ClipboardHistoryListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.ClipboardHistory/Pages/ClipboardHistoryListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.ClipboardHistory/Pages/ClipboardHistoryListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Assets/FileExplorer.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/CopyPathCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/CopyPathCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/CopyPathCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/CopyPathCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenFileCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenFileCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenFileCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenFileCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenInConsoleCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenInConsoleCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenInConsoleCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenInConsoleCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenPropertiesCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Commands/OpenWithCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Data/IndexerListItem.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/FallbackOpenFileItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/DataSourceManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROP.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPIDSET.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPIDSET.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPIDSET.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPIDSET.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPSET.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPSET.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPSET.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/DBPROPSET.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowset.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/OleDB/IRowsetInfo.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchQuery.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SearchResult.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManager.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchCatalogManagerClass.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManager.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchManagerClass.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelper.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/CSearchQueryHelperClass.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchCatalogManager.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchManager.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/SystemSearch/ISearchQueryHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/QueryStringBuilder.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/UrlToFilePathConverter.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/UrlToFilePathConverter.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/UrlToFilePathConverter.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Indexer/Utils/UrlToFilePathConverter.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/IndexerCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/IndexerCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/IndexerCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/IndexerCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Microsoft.CmdPal.Ext.Indexer.csproj diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Native/NativeHelpers.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/NativeMethods.txt diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryExplorePage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryExplorePage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryExplorePage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryExplorePage.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryPage.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/DirectoryPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/ExploreListItem.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/Icons.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Pages/IndexerPage.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Indexer/Properties/Resources.resx diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Assets/Registry.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Assets/Registry.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Assets/Registry.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Assets/Registry.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Assets/Registry.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Assets/Registry.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Assets/Registry.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Assets/Registry.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Classes/RegistryEntry.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Classes/RegistryEntry.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Classes/RegistryEntry.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Classes/RegistryEntry.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Commands/CopyRegistryInfoCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Commands/CopyRegistryInfoCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Commands/CopyRegistryInfoCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Commands/CopyRegistryInfoCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Commands/OpenKeyInEditorCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Commands/OpenKeyInEditorCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Commands/OpenKeyInEditorCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Commands/OpenKeyInEditorCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Constants/KeyName.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Constants/KeyName.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Constants/KeyName.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Constants/KeyName.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Constants/MaxTextLength.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Constants/MaxTextLength.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Constants/MaxTextLength.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Constants/MaxTextLength.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/CopyType.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/CopyType.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/CopyType.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/CopyType.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Enumerations/TruncateSide.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Enumerations/TruncateSide.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Enumerations/TruncateSide.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Enumerations/TruncateSide.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/ContextMenuHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/ContextMenuHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/ContextMenuHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/ContextMenuHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/QueryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/QueryHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/QueryHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/QueryHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/RegistryHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/RegistryHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/RegistryHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/RegistryHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/ResultHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/ResultHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/ResultHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/ResultHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/ValueHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/ValueHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Helpers/ValueHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Helpers/ValueHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj similarity index 96% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj index 2ff67f859f..c7633d7356 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Microsoft.CmdPal.Ext.Registry.csproj @@ -1,5 +1,7 @@  + + Microsoft.CmdPal.Ext.Registry $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Pages/RegistryListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Pages/RegistryListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Pages/RegistryListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Pages/RegistryListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/Properties/Resources.resx diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/RegistryCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/RegistryCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Registry/RegistryCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Registry/RegistryCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Assets/Run.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Assets/Run.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Assets/Run.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Assets/Run.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Assets/Run.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Assets/Run.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Assets/Run.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Assets/Run.svg diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/Assets/Run_V2_2x.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Assets/Run_V2_2x.svg similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/Assets/Run_V2_2x.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Assets/Run_V2_2x.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Commands/ExecuteItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Commands/ExecuteItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Commands/ExecuteItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Commands/ExecuteItem.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/FallbackExecuteItem.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/Helpers/ExecutionShell.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/ExecutionShell.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/Helpers/ExecutionShell.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/ExecutionShell.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Helpers/RunAsType.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/RunAsType.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Helpers/RunAsType.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/RunAsType.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Helpers/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/SettingsManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Helpers/SettingsManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/SettingsManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Helpers/ShellListPageHelpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/ShellListPageHelpers.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Helpers/ShellListPageHelpers.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Helpers/ShellListPageHelpers.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Icons.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.Shell/Icons.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Icons.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj similarity index 95% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj index c95f2a93b2..934e6d264a 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Microsoft.CmdPal.Ext.Shell.csproj @@ -1,5 +1,7 @@  + + enable Microsoft.CmdPal.Ext.Shell diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Pages/ShellListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/Properties/Resources.resx diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/ShellCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/ShellCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.Shell/ShellCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.Shell/ShellCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Assets/SystemCommand.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/EmptyRecycleBinCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/EmptyRecycleBinCommand.cs similarity index 95% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/EmptyRecycleBinCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/EmptyRecycleBinCommand.cs index 932e421e42..a62dd720b9 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/EmptyRecycleBinCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/EmptyRecycleBinCommand.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Microsoft.CmdPal.Ext.System.Helpers; using Microsoft.CommandPalette.Extensions.Toolkit; -namespace Microsoft.CmdPal.Ext.Shell; +namespace Microsoft.CmdPal.Ext.System; public sealed partial class EmptyRecycleBinCommand : InvokableCommand { diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/EmptyRecycleBinConfirmation.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/EmptyRecycleBinConfirmation.cs similarity index 97% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/EmptyRecycleBinConfirmation.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/EmptyRecycleBinConfirmation.cs index c15247d8cb..558cd6b893 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/EmptyRecycleBinConfirmation.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/EmptyRecycleBinConfirmation.cs @@ -2,7 +2,6 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.CmdPal.Ext.Shell; using Microsoft.CmdPal.Ext.System.Helpers; using Microsoft.CommandPalette.Extensions.Toolkit; diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/ExecuteCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/ExecuteCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/ExecuteCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/ExecuteCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/ExecuteCommandConfirmation.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/ExecuteCommandConfirmation.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/ExecuteCommandConfirmation.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/ExecuteCommandConfirmation.cs diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/FallbackSystemCommandItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/FallbackSystemCommandItem.cs new file mode 100644 index 0000000000..94b23e3e0d --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/FallbackSystemCommandItem.cs @@ -0,0 +1,72 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Microsoft.CmdPal.Ext.System.Helpers; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.System; + +internal sealed partial class FallbackSystemCommandItem : FallbackCommandItem +{ + public FallbackSystemCommandItem(SettingsManager settings) + : base(new NoOpCommand(), Resources.Microsoft_plugin_ext_fallback_display_title) + { + Title = string.Empty; + Subtitle = string.Empty; + + var isBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi; + var hideEmptyRB = settings.HideEmptyRecycleBin; + var confirmSystemCommands = settings.ShowDialogToConfirmCommand; + var showSuccessOnEmptyRB = settings.ShowSuccessMessageAfterEmptyingRecycleBin; + + systemCommands = Commands.GetSystemCommands(isBootedInUefiMode, hideEmptyRB, confirmSystemCommands, showSuccessOnEmptyRB); + } + + private readonly List systemCommands; + + public override void UpdateQuery(string query) + { + if (string.IsNullOrWhiteSpace(query)) + { + Title = string.Empty; + Subtitle = string.Empty; + return; + } + + IListItem? result = null; + var resultScore = 0; + + // find the max score for the query + foreach (var command in systemCommands) + { + var title = command.Title; + var subTitle = command.Subtitle; + var titleScore = StringMatcher.FuzzySearch(query, title).Score; + var subTitleScore = StringMatcher.FuzzySearch(query, subTitle).Score; + + var maxScore = Math.Max(titleScore, subTitleScore); + if (maxScore > resultScore) + { + resultScore = maxScore; + result = command; + } + } + + if (result == null) + { + Title = string.Empty; + Subtitle = string.Empty; + + return; + } + + Title = result.Title; + Subtitle = result.Subtitle; + Icon = result.Icon; + Command = result.Command; + } +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs similarity index 98% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs index 1459b83bc6..a981b2ed00 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Commands.cs @@ -85,7 +85,7 @@ internal static class Commands { results.AddRange(new[] { - new ListItem(new OpenInShellCommand(Resources.Microsoft_plugin_command_name_empty, "explorer.exe", "shell:RecycleBinFolder")) + new ListItem(new OpenInShellCommand(Resources.Microsoft_plugin_command_name_open, "explorer.exe", "shell:RecycleBinFolder")) { Title = Resources.Microsoft_plugin_sys_RecycleBinOpen, Subtitle = Resources.Microsoft_plugin_sys_RecycleBin_description, @@ -102,7 +102,7 @@ internal static class Commands else { results.Add( - new ListItem(new OpenInShellCommand(Resources.Microsoft_plugin_command_name_empty, "explorer.exe", "shell:RecycleBinFolder")) + new ListItem(new OpenInShellCommand(Resources.Microsoft_plugin_command_name_open, "explorer.exe", "shell:RecycleBinFolder")) { Title = Resources.Microsoft_plugin_sys_RecycleBin, Subtitle = Resources.Microsoft_plugin_sys_RecycleBin_description, diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Icons.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/MessageBoxHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/MessageBoxHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/MessageBoxHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/MessageBoxHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Native.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Native.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Native.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/NativeMethods.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/NativeMethods.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/NativeMethods.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/NetworkConnectionProperties.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/NetworkConnectionProperties.cs similarity index 98% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/NetworkConnectionProperties.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/NetworkConnectionProperties.cs index 9afb39b6f1..486eeaa8b5 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/NetworkConnectionProperties.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/NetworkConnectionProperties.cs @@ -311,14 +311,14 @@ internal sealed class NetworkConnectionProperties { switch (property) { - case string: - return string.IsNullOrWhiteSpace(property) ? string.Empty : $"\n\n{title}{property}"; + case string str: + return string.IsNullOrWhiteSpace(str) ? string.Empty : $"\n\n{title}{str}"; case List listString: - return listString.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}"; + return listString.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", listString)}"; case List listIP: - return listIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}"; + return listIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", listIP)}"; case IPAddressCollection collectionIP: - return collectionIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}"; + return collectionIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", collectionIP)}"; case null: return string.Empty; default: diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/OpenInShellHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/OpenInShellHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/OpenInShellHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/OpenInShellHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/ResultHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/ResultHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/ResultHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/ResultHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/SettingsManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/SettingsManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/SettingsManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/SystemPluginContext.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/SystemPluginContext.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/SystemPluginContext.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/SystemPluginContext.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Win32Helpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Win32Helpers.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Helpers/Win32Helpers.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Helpers/Win32Helpers.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj similarity index 95% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj index 4c619bc5e5..48e9d6ba82 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Microsoft.CmdPal.Ext.System.csproj @@ -1,5 +1,6 @@  + enable Microsoft.CmdPal.Ext.System diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/OpenInShellCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/OpenInShellCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/OpenInShellCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/OpenInShellCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Pages/SystemCommandPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Pages/SystemCommandPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Pages/SystemCommandPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Pages/SystemCommandPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs similarity index 98% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs index 6aa26a7b0d..1574a2aa0d 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.Designer.cs @@ -186,6 +186,15 @@ namespace Microsoft.CmdPal.Ext.System { } } + /// + /// Looks up a localized string similar to Open System Command. + /// + public static string Microsoft_plugin_ext_fallback_display_title { + get { + return ResourceManager.GetString("Microsoft_plugin_ext_fallback_display_title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hide disconnected network info. /// diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.resx similarity index 99% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.resx index e71e8a8d01..0aaa3d0611 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/Properties/Resources.resx @@ -411,4 +411,7 @@ Sleep + + Open System Command + \ No newline at end of file diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/SystemCommandExtensionProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/SystemCommandExtensionProvider.cs similarity index 87% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/SystemCommandExtensionProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/SystemCommandExtensionProvider.cs index be89cf75fd..b1dc3ffc26 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.System/SystemCommandExtensionProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.System/SystemCommandExtensionProvider.cs @@ -14,6 +14,7 @@ public partial class SystemCommandExtensionProvider : CommandProvider private readonly ICommandItem[] _commands; private static readonly SettingsManager _settingsManager = new(); public static readonly SystemCommandPage Page = new(_settingsManager); + private readonly FallbackSystemCommandItem _fallbackFileItem = new(_settingsManager); public SystemCommandExtensionProvider() { @@ -36,4 +37,6 @@ public partial class SystemCommandExtensionProvider : CommandProvider { return _commands; } + + public override IFallbackCommandItem[] FallbackCommands() => [_fallbackFileItem]; } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/TimeDate.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.dark.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.dark.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.dark.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.dark.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.light.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.light.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.light.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Assets/Warning.light.png diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/FallbackTimeDateItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/FallbackTimeDateItem.cs new file mode 100644 index 0000000000..267ab43989 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/FallbackTimeDateItem.cs @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Globalization; +using System.Linq; +using Microsoft.CmdPal.Ext.TimeDate.Helpers; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace Microsoft.CmdPal.Ext.TimeDate; + +internal sealed partial class FallbackTimeDateItem : FallbackCommandItem +{ + private readonly HashSet _validOptions; + private SettingsManager _settingsManager; + + public FallbackTimeDateItem(SettingsManager settings) + : base(new NoOpCommand(), Resources.Microsoft_plugin_timedate_fallback_display_title) + { + Title = string.Empty; + Subtitle = string.Empty; + _settingsManager = settings; + _validOptions = new(StringComparer.OrdinalIgnoreCase) + { + Resources.ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagDate", CultureInfo.CurrentCulture), + Resources.ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagDateNow", CultureInfo.CurrentCulture), + + Resources.ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagTime", CultureInfo.CurrentCulture), + Resources.ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagTimeNow", CultureInfo.CurrentCulture), + + Resources.ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagFormat", CultureInfo.CurrentCulture), + Resources.ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagFormatNow", CultureInfo.CurrentCulture), + + Resources.ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagWeek", CultureInfo.CurrentCulture), + }; + } + + public override void UpdateQuery(string query) + { + if (!_settingsManager.EnableFallbackItems || string.IsNullOrWhiteSpace(query) || !IsValidQuery(query)) + { + Title = string.Empty; + Subtitle = string.Empty; + Command = new NoOpCommand(); + return; + } + + var availableResults = AvailableResultsList.GetList(false, _settingsManager); + ListItem result = null; + var maxScore = 0; + + foreach (var f in availableResults) + { + var score = f.Score(query, f.Label, f.AlternativeSearchTag); + if (score > maxScore) + { + maxScore = score; + result = f.ToListItem(); + } + } + + if (result != null) + { + Title = result.Title; + Subtitle = result.Subtitle; + Icon = result.Icon; + } + else + { + Title = string.Empty; + Subtitle = string.Empty; + Command = new NoOpCommand(); + } + } + + private bool IsValidQuery(string query) + { + if (_validOptions.Contains(query)) + { + return true; + } + + foreach (var option in _validOptions) + { + if (option == null) + { + continue; + } + + var parts = option.Split(';', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + + if (parts.Any(part => string.Equals(part, query, StringComparison.OrdinalIgnoreCase))) + { + return true; + } + } + + return false; + } +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResult.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResult.cs similarity index 85% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResult.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResult.cs index 9393d82b6f..7665de85df 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResult.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResult.cs @@ -4,11 +4,9 @@ using System.Runtime.CompilerServices; using Microsoft.CommandPalette.Extensions.Toolkit; -[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")] - namespace Microsoft.CmdPal.Ext.TimeDate.Helpers; -internal class AvailableResult +internal sealed class AvailableResult { /// /// Gets or sets the time/date value @@ -30,6 +28,11 @@ internal class AvailableResult /// internal ResultIconType IconType { get; set; } + /// + /// Gets or sets a value to show additional error details + /// + internal string ErrorDetails { get; set; } = string.Empty; + /// /// Returns the path to the icon /// @@ -42,6 +45,7 @@ internal class AvailableResult ResultIconType.Time => ResultHelper.TimeIcon, ResultIconType.Date => ResultHelper.CalendarIcon, ResultIconType.DateTime => ResultHelper.TimeDateIcon, + ResultIconType.Error => ResultHelper.ErrorIcon, _ => null, }; } @@ -53,6 +57,7 @@ internal class AvailableResult Title = this.Value, Subtitle = this.Label, Icon = this.GetIconInfo(), + Details = string.IsNullOrEmpty(this.ErrorDetails) ? null : new Details() { Body = this.ErrorDetails }, }; } @@ -81,4 +86,5 @@ public enum ResultIconType Time, Date, DateTime, + Error, } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResultsList.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResultsList.cs similarity index 74% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResultsList.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResultsList.cs index f01e6b8b07..60ccaf38b5 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResultsList.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/AvailableResultsList.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Text.RegularExpressions; namespace Microsoft.CmdPal.Ext.TimeDate.Helpers; @@ -32,6 +33,7 @@ internal static class AvailableResultsList var dateTimeNowUtc = dateTimeNow.ToUniversalTime(); var firstWeekRule = firstWeekOfYear ?? TimeAndDateHelper.GetCalendarWeekRule(settings.FirstWeekOfYear); var firstDayOfTheWeek = firstDayOfWeek ?? TimeAndDateHelper.GetFirstDayOfWeek(settings.FirstDayOfWeek); + var weekOfYear = calendar.GetWeekOfYear(dateTimeNow, firstWeekRule, firstDayOfTheWeek); results.AddRange(new[] { @@ -58,17 +60,103 @@ internal static class AvailableResultsList AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"), IconType = ResultIconType.DateTime, }, + new AvailableResult() + { + Value = weekOfYear.ToString(CultureInfo.CurrentCulture), + Label = Resources.Microsoft_plugin_timedate_WeekOfYear, + AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"), + IconType = ResultIconType.Date, + }, }); - if (isKeywordSearch || !settings.OnlyDateTimeNowGlobal) + if (isKeywordSearch) { // We use long instead of int for unix time stamp because int is too small after 03:14:07 UTC 2038-01-19 var unixTimestamp = ((DateTimeOffset)dateTimeNowUtc).ToUnixTimeSeconds(); var unixTimestampMilliseconds = ((DateTimeOffset)dateTimeNowUtc).ToUnixTimeMilliseconds(); - var weekOfYear = calendar.GetWeekOfYear(dateTimeNow, firstWeekRule, firstDayOfTheWeek); var era = DateTimeFormatInfo.CurrentInfo.GetEraName(calendar.GetEra(dateTimeNow)); var eraShort = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(calendar.GetEra(dateTimeNow)); + // Custom formats + foreach (var f in settings.CustomFormats) + { + var formatParts = f.Split("=", 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + var formatSyntax = formatParts.Length == 2 ? formatParts[1] : string.Empty; + var searchTags = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagCustom"); + var dtObject = dateTimeNow; + + // If Length = 0 then empty string. + if (formatParts.Length >= 1) + { + try + { + // Verify and check input and update search tags + if (formatParts.Length == 1) + { + throw new FormatException("Format syntax part after equal sign is missing."); + } + + var containsCustomSyntax = TimeAndDateHelper.StringContainsCustomFormatSyntax(formatSyntax); + if (formatSyntax.StartsWith("UTC:", StringComparison.InvariantCulture)) + { + searchTags = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagCustomUtc"); + dtObject = dateTimeNowUtc; + } + + // Get formated date + var value = TimeAndDateHelper.ConvertToCustomFormat(dtObject, unixTimestamp, unixTimestampMilliseconds, weekOfYear, eraShort, Regex.Replace(formatSyntax, "^UTC:", string.Empty), firstWeekRule, firstDayOfTheWeek); + try + { + value = dtObject.ToString(value, CultureInfo.CurrentCulture); + } + catch + { + if (!containsCustomSyntax) + { + throw; + } + else + { + // Do not fail as we have custom format syntax. Instead fix backslashes. + value = Regex.Replace(value, @"(? - /// Gets a result with an error message that only numbers can't be parsed + /// Gets a result with an error message that input can't be parsed ///
/// Element of type . - internal static ListItem CreateNumberErrorResult() => new ListItem(new NoOpCommand()) - { - Title = Resources.Microsoft_plugin_timedate_ErrorResultTitle, - Subtitle = Resources.Microsoft_plugin_timedate_ErrorResultSubTitle, - Icon = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.TimeDate\\Assets\\Warning.light.png", "Microsoft.CmdPal.Ext.TimeDate\\Assets\\Warning.dark.png"), - }; - +#pragma warning disable CA1863 // Use 'CompositeFormat' internal static ListItem CreateInvalidInputErrorResult() => new ListItem(new NoOpCommand()) { Title = Resources.Microsoft_plugin_timedate_InvalidInput_ErrorMessageTitle, - Subtitle = Resources.Microsoft_plugin_timedate_InvalidInput_ErrorMessageSubTitle, - Icon = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.TimeDate\\Assets\\Warning.light.png", "Microsoft.CmdPal.Ext.TimeDate\\Assets\\Warning.dark.png"), + Icon = ErrorIcon, + Details = new Details() + { + Title = Resources.Microsoft_plugin_timedate_InvalidInput_DetailsHeader, + + // Because of translation we can't use 'CompositeFormat'. + Body = string.Format(CultureInfo.CurrentCulture, Resources.Microsoft_plugin_timedate_InvalidInput_SupportedInput, "**", "\n\n", "\n\n* "), + }, }; +#pragma warning restore CA1863 // Use 'CompositeFormat' } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/SettingsManager.cs similarity index 81% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/SettingsManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/SettingsManager.cs index d0de0017b8..7b351fe3b8 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/SettingsManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/SettingsManager.cs @@ -13,6 +13,11 @@ namespace Microsoft.CmdPal.Ext.TimeDate.Helpers; public class SettingsManager : JsonSettingsManager { + // Line break character used in WinUI3 TextBox and TextBlock. + private const char TEXTBOXNEWLINE = '\r'; + + private const string CUSTOMFORMATPLACEHOLDER = "MyFormat=dd-MMM-yyyy\rMySecondFormat=dddd (Da\\y nu\\mber: DOW)\rMyUtcFormat=UTC:hh:mm:ss"; + private static readonly string _namespace = "timeDate"; private static string Namespaced(string propertyName) => $"{_namespace}.{propertyName}"; @@ -70,11 +75,11 @@ public class SettingsManager : JsonSettingsManager Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek, _firstDayOfWeekChoices); - private readonly ToggleSetting _onlyDateTimeNowGlobal = new( - Namespaced(nameof(OnlyDateTimeNowGlobal)), - Resources.Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal, - Resources.Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal_Description, - true); // TODO -- double check default value + private readonly ToggleSetting _enableFallbackItems = new( + Namespaced(nameof(EnableFallbackItems)), + Resources.Microsoft_plugin_timedate_SettingEnableFallbackItems, + Resources.Microsoft_plugin_timedate_SettingEnableFallbackItems_Description, + true); private readonly ToggleSetting _timeWithSeconds = new( Namespaced(nameof(TimeWithSecond)), @@ -88,11 +93,11 @@ public class SettingsManager : JsonSettingsManager Resources.Microsoft_plugin_timedate_SettingDateWithWeekday_Description, false); // TODO -- double check default value - private readonly ToggleSetting _hideNumberMessageOnGlobalQuery = new( - Namespaced(nameof(HideNumberMessageOnGlobalQuery)), - Resources.Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery, - Resources.Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery, - true); // TODO -- double check default value + private readonly TextSetting _customFormats = new( + Namespaced(nameof(CustomFormats)), + Resources.Microsoft_plugin_timedate_Setting_CustomFormats, + Resources.Microsoft_plugin_timedate_Setting_CustomFormats + TEXTBOXNEWLINE + string.Format(CultureInfo.CurrentCulture, Resources.Microsoft_plugin_timedate_Setting_CustomFormatsDescription.ToString(), "DOW", "DIM", "WOM", "WOY", "EAB", "WFT", "UXT", "UMS", "OAD", "EXC", "EXF", "UTC:"), + string.Empty); public int FirstWeekOfYear { @@ -134,13 +139,13 @@ public class SettingsManager : JsonSettingsManager } } - public bool OnlyDateTimeNowGlobal => _onlyDateTimeNowGlobal.Value; + public bool EnableFallbackItems => _enableFallbackItems.Value; public bool TimeWithSecond => _timeWithSeconds.Value; public bool DateWithWeekday => _dateWithWeekday.Value; - public bool HideNumberMessageOnGlobalQuery => _hideNumberMessageOnGlobalQuery.Value; + public List CustomFormats => _customFormats.Value.Split(TEXTBOXNEWLINE).ToList(); internal static string SettingsJsonPath() { @@ -155,12 +160,15 @@ public class SettingsManager : JsonSettingsManager { FilePath = SettingsJsonPath(); - Settings.Add(_firstWeekOfYear); - Settings.Add(_firstDayOfWeek); - Settings.Add(_onlyDateTimeNowGlobal); + Settings.Add(_enableFallbackItems); Settings.Add(_timeWithSeconds); Settings.Add(_dateWithWeekday); - Settings.Add(_hideNumberMessageOnGlobalQuery); + Settings.Add(_firstWeekOfYear); + Settings.Add(_firstDayOfWeek); + + _customFormats.Multiline = true; + _customFormats.Placeholder = CUSTOMFORMATPLACEHOLDER; + Settings.Add(_customFormats); // Load settings from file upon initialization LoadSettings(); diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeAndDateHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeAndDateHelper.cs new file mode 100644 index 0000000000..368e20a48e --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeAndDateHelper.cs @@ -0,0 +1,439 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +namespace Microsoft.CmdPal.Ext.TimeDate.Helpers; + +internal static class TimeAndDateHelper +{ + /* htcfreek:Currently not used. + * private static readonly Regex _regexSpecialInputFormats = new Regex(@"^.*(u|ums|ft|oa|exc|exf)\d"); */ + + private static readonly Regex _regexCustomDateTimeFormats = new Regex(@"(? + /// Get the format for the time string + /// + /// Type of format + /// Show date with weekday and name of month (long format) + /// Show time with seconds (long format) + /// String that identifies the time/date format () + internal static string GetStringFormat(FormatStringType targetFormat, bool timeLong, bool dateLong) + { + switch (targetFormat) + { + case FormatStringType.Time: + return timeLong ? "T" : "t"; + case FormatStringType.Date: + return dateLong ? "D" : "d"; + case FormatStringType.DateTime: + if (timeLong & dateLong) + { + return "F"; // Friday, October 31, 2008 5:04:32 PM + } + else if (timeLong & !dateLong) + { + return "G"; // 10/31/2008 5:04:32 PM + } + else if (!timeLong & dateLong) + { + return "f"; // Friday, October 31, 2008 5:04 PM + } + else + { + // (!timeLong & !dateLong) + return "g"; // 10/31/2008 5:04 PM + } + + default: + return string.Empty; // Windows default based on current culture settings + } + } + + /// + /// Returns the number week in the month (Used code from 'David Morton' from ) + /// + /// date + /// Setting for the first day in the week. + /// Number of week in the month + internal static int GetWeekOfMonth(DateTime date, DayOfWeek formatSettingFirstDayOfWeek) + { + var weekCount = 1; + + for (var i = 1; i <= date.Day; i++) + { + DateTime d = new(date.Year, date.Month, i); + + // Count week number +1 if day is the first day of a week and not day 1 of the month. + // (If we count on day one of a month we would start the month with week number 2.) + if (i > 1 && d.DayOfWeek == formatSettingFirstDayOfWeek) + { + weekCount += 1; + } + } + + return weekCount; + } + + /// + /// Returns the number of the day in the week + /// + /// Date + /// Number of the day in the week + internal static int GetNumberOfDayInWeek(DateTime date, DayOfWeek formatSettingFirstDayOfWeek) + { + var daysInWeek = 7; + var adjustment = 1; // We count from 1 to 7 and not from 0 to 6 + + return ((date.DayOfWeek + daysInWeek - formatSettingFirstDayOfWeek) % daysInWeek) + adjustment; + } + + internal static double ConvertToOleAutomationFormat(DateTime date, OADateFormats type) + { + var v = date.ToOADate(); + + switch (type) + { + case OADateFormats.Excel1904: + // Excel with base 1904: Adjust by -1462 + v -= 1462; + + // Date starts at 1/1/1904 = 0 + if (Math.Truncate(v) < 0) + { + throw new ArgumentOutOfRangeException("Not a valid Excel date.", innerException: null); + } + + return v; + case OADateFormats.Excel1900: + // Excel with base 1900: Adjust by -1 if v < 61 + v = v < 61 ? v - 1 : v; + + // Date starts at 1/1/1900 = 1 + if (Math.Truncate(v) < 1) + { + throw new ArgumentOutOfRangeException("Not a valid Excel date.", innerException: null); + } + + return v; + default: + // OLE Automation date: Return as is. + return v; + } + } + + /// + /// Convert input string to a object in local time + /// + /// String with date/time + /// The new object + /// Error message shown to the user + /// True on success, otherwise false + internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp, out string inputParsingErrorMsg) + { + inputParsingErrorMsg = string.Empty; + CompositeFormat errorMessage = CompositeFormat.Parse(Resources.Microsoft_plugin_timedate_InvalidInput_SupportedRange); + + if (DateTime.TryParse(input, out timestamp)) + { + // Known date/time format + return true; + } + else if (Regex.IsMatch(input, @"^u[\+-]?\d+$")) + { + // Unix time stamp + // We use long instead of int, because int is too small after 03:14:07 UTC 2038-01-19 + var canParse = long.TryParse(input.TrimStart('u'), out var secondsU); + + // Value has to be in the range from -62135596800 to 253402300799 + if (!canParse || secondsU < UnixTimeSecondsMin || secondsU > UnixTimeSecondsMax) + { + inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Unix, UnixTimeSecondsMin, UnixTimeSecondsMax); + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + + timestamp = DateTimeOffset.FromUnixTimeSeconds(secondsU).LocalDateTime; + return true; + } + else if (Regex.IsMatch(input, @"^ums[\+-]?\d+$")) + { + // Unix time stamp in milliseconds + // We use long instead of int because int is too small after 03:14:07 UTC 2038-01-19 + var canParse = long.TryParse(input.TrimStart("ums".ToCharArray()), out var millisecondsUms); + + // Value has to be in the range from -62135596800000 to 253402300799999 + if (!canParse || millisecondsUms < UnixTimeMillisecondsMin || millisecondsUms > UnixTimeMillisecondsMax) + { + inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Unix_Milliseconds, UnixTimeMillisecondsMin, UnixTimeMillisecondsMax); + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + + timestamp = DateTimeOffset.FromUnixTimeMilliseconds(millisecondsUms).LocalDateTime; + return true; + } + else if (Regex.IsMatch(input, @"^ft\d+$")) + { + var canParse = long.TryParse(input.TrimStart("ft".ToCharArray()), out var secondsFt); + + // Windows file time + // Value has to be in the range from 0 to 2650467707991000000 + if (!canParse || secondsFt < WindowsFileTimeMin || secondsFt > WindowsFileTimeMax) + { + inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_WindowsFileTime, WindowsFileTimeMin, WindowsFileTimeMax); + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + + // DateTime.FromFileTime returns as local time. + timestamp = DateTime.FromFileTime(secondsFt); + return true; + } + else if (Regex.IsMatch(input, @"^oa[+-]?\d+[,.0-9]*$")) + { + var canParse = double.TryParse(input.TrimStart("oa".ToCharArray()), out var oADate); + + // OLE Automation date + // Input has to be in the range from -657434.99999999 to 2958465.99999999 + // DateTime.FromOADate returns as local time. + if (!canParse || oADate < OADateMin || oADate > OADateMax) + { + inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_OADate, OADateMin, OADateMax); + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + + timestamp = DateTime.FromOADate(oADate); + return true; + } + else if (Regex.IsMatch(input, @"^exc[+-]?\d+[,.0-9]*$")) + { + var canParse = double.TryParse(input.TrimStart("exc".ToCharArray()), out var excDate); + + // Excel's 1900 date value + // Input has to be in the range from 1 (0 = Fake date) to 2958465.99998843 and not 60 whole number + // Because of a bug in Excel and the way it behaves before 3/1/1900 we have to adjust all inputs lower than 61 for +1 + // DateTime.FromOADate returns as local time. + if (!canParse || excDate < 0 || excDate > Excel1900DateMax) + { + // For the if itself we use 0 as min value that we can show a special message if input is 0. + inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Excel1900, Excel1900DateMin, Excel1900DateMax); + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + + if (Math.Truncate(excDate) == 0 || Math.Truncate(excDate) == 60) + { + inputParsingErrorMsg = Resources.Microsoft_plugin_timedate_InvalidInput_FakeExcel1900; + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + + excDate = excDate <= 60 ? excDate + 1 : excDate; + timestamp = DateTime.FromOADate(excDate); + return true; + } + else if (Regex.IsMatch(input, @"^exf[+-]?\d+[,.0-9]*$")) + { + var canParse = double.TryParse(input.TrimStart("exf".ToCharArray()), out var exfDate); + + // Excel's 1904 date value + // Input has to be in the range from 0 to 2957003.99998843 + // Because Excel uses 01/01/1904 as base we need to adjust for +1462 + // DateTime.FromOADate returns as local time. + if (!canParse || exfDate < Excel1904DateMin || exfDate > Excel1904DateMax) + { + inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Excel1904, Excel1904DateMin, Excel1904DateMax); + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + + timestamp = DateTime.FromOADate(exfDate + 1462); + return true; + } + else + { + timestamp = new DateTime(1, 1, 1, 1, 1, 1); + return false; + } + } + + /* htcfreek:Currently not required + /// + /// Test if input is special parsing for Unix time, Unix time in milliseconds, file time, ... + /// + /// String with date/time + /// True if yes, otherwise false + internal static bool IsSpecialInputParsing(string input) + { + return _regexSpecialInputFormats.IsMatch(input); + }*/ + + /// + /// Converts a DateTime object based on the format string + /// + /// Date/time object. + /// Value for replacing "Unix Time Stamp". + /// Value for replacing "Unix Time Stamp in milliseconds". + /// Value for relacing calendar week. + /// Era abbreviation. + /// Format definition. + /// Formated date/time string. + internal static string ConvertToCustomFormat(DateTime date, long unix, long unixMilliseconds, int calWeek, string eraShortFormat, string format, CalendarWeekRule firstWeekRule, DayOfWeek firstDayOfTheWeek) + { + var result = format; + + // DOW: Number of day in week + result = _regexCustomDateTimeDow.Replace(result, GetNumberOfDayInWeek(date, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture)); + + // DIM: Days in Month + result = _regexCustomDateTimeDim.Replace(result, DateTime.DaysInMonth(date.Year, date.Month).ToString(CultureInfo.CurrentCulture)); + + // WOM: Week of Month + result = _regexCustomDateTimeWom.Replace(result, GetWeekOfMonth(date, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture)); + + // WOY: Week of Year + result = _regexCustomDateTimeWoy.Replace(result, calWeek.ToString(CultureInfo.CurrentCulture)); + + // EAB: Era abbreviation + result = _regexCustomDateTimeEab.Replace(result, eraShortFormat); + + // WFT: Week of Month + if (_regexCustomDateTimeWft.IsMatch(result)) + { + // Special handling as very early dates can't convert. + result = _regexCustomDateTimeWft.Replace(result, date.ToFileTime().ToString(CultureInfo.CurrentCulture)); + } + + // UXT: Unix time stamp + result = _regexCustomDateTimeUxt.Replace(result, unix.ToString(CultureInfo.CurrentCulture)); + + // UMS: Unix time stamp milli seconds + result = _regexCustomDateTimeUms.Replace(result, unixMilliseconds.ToString(CultureInfo.CurrentCulture)); + + // OAD: OLE Automation date + result = _regexCustomDateTimeOad.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.OLEAutomation).ToString(CultureInfo.CurrentCulture)); + + // EXC: Excel date value with base 1900 + if (_regexCustomDateTimeExc.IsMatch(result)) + { + // Special handling as very early dates can't convert. + result = _regexCustomDateTimeExc.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.Excel1900).ToString(CultureInfo.CurrentCulture)); + } + + // EXF: Excel date value with base 1904 + if (_regexCustomDateTimeExf.IsMatch(result)) + { + // Special handling as very early dates can't convert. + result = _regexCustomDateTimeExf.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.Excel1904).ToString(CultureInfo.CurrentCulture)); + } + + return result; + } + + /// + /// Test a string for our custom date and time format syntax + /// + /// String to test. + /// True if yes and otherwise false + internal static bool StringContainsCustomFormatSyntax(string str) + { + return _regexCustomDateTimeFormats.IsMatch(str); + } + + /// + /// Returns a CalendarWeekRule enum value based on the plugin setting. + /// + internal static CalendarWeekRule GetCalendarWeekRule(int pluginSetting) + { + switch (pluginSetting) + { + case 0: + return CalendarWeekRule.FirstDay; + case 1: + return CalendarWeekRule.FirstFullWeek; + case 2: + return CalendarWeekRule.FirstFourDayWeek; + default: + // Wrong json value and system setting (-1). + return DateTimeFormatInfo.CurrentInfo.CalendarWeekRule; + } + } + + /// + /// Returns a DayOfWeek enum value based on the FirstDayOfWeek plugin setting. + /// + internal static DayOfWeek GetFirstDayOfWeek(int pluginSetting) + { + switch (pluginSetting) + { + case 0: + return DayOfWeek.Sunday; + case 1: + return DayOfWeek.Monday; + case 2: + return DayOfWeek.Tuesday; + case 3: + return DayOfWeek.Wednesday; + case 4: + return DayOfWeek.Thursday; + case 5: + return DayOfWeek.Friday; + case 6: + return DayOfWeek.Saturday; + default: + // Wrong json value and system setting (-1). + return DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek; + } + } +} + +/// +/// Type of time/date format +/// +internal enum FormatStringType +{ + Time, + Date, + DateTime, +} + +/// +/// Different versions of Date formats based on OLE Automation date +/// +internal enum OADateFormats +{ + OLEAutomation, + Excel1900, + Excel1904, +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeDateCalculator.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeDateCalculator.cs similarity index 80% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeDateCalculator.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeDateCalculator.cs index 5022236678..38f417ad5b 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeDateCalculator.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Helpers/TimeDateCalculator.cs @@ -29,15 +29,18 @@ public sealed partial class TimeDateCalculator /// List of Wox s. public static List ExecuteSearch(SettingsManager settings, string query) { - var isEmptySearchInput = string.IsNullOrEmpty(query); + var isEmptySearchInput = string.IsNullOrWhiteSpace(query); List availableFormats = new List(); List results = new List(); // currently, all of the search in V2 is keyword search. var isKeywordSearch = true; + // Last input parsing error + var lastInputParsingErrorMsg = string.Empty; + // Switch search type - if (isEmptySearchInput || (!isKeywordSearch && settings.OnlyDateTimeNowGlobal)) + if (isEmptySearchInput || (!isKeywordSearch)) { // Return all results for system time/date on empty keyword search // or only time, date and now results for system time on global queries if the corresponding setting is enabled @@ -47,13 +50,13 @@ public sealed partial class TimeDateCalculator { // Search for specified format with specified time/date value var userInput = query.Split(InputDelimiter); - if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp)) + if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp, out lastInputParsingErrorMsg)) { availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, settings, null, null, timestamp)); query = userInput[0]; } } - else if (TimeAndDateHelper.ParseStringAsDateTime(query, out DateTime timestamp)) + else if (TimeAndDateHelper.ParseStringAsDateTime(query, out DateTime timestamp, out lastInputParsingErrorMsg)) { // Return all formats for specified time/date value availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, settings, null, null, timestamp)); @@ -88,19 +91,15 @@ public sealed partial class TimeDateCalculator } } - // If search term is only a number that can't be parsed return an error message - if (!isEmptySearchInput && results.Count == 0 && Regex.IsMatch(query, @"\w+\d+.*$") && !query.Any(char.IsWhiteSpace) && (TimeAndDateHelper.IsSpecialInputParsing(query) || !Regex.IsMatch(query, @"\d+[\.:/]\d+"))) - { - // Without plugin key word show only if message is not hidden by setting - if (!settings.HideNumberMessageOnGlobalQuery) - { - results.Add(ResultHelper.CreateNumberErrorResult()); - } - } - if (results.Count == 0) { - results.Add(ResultHelper.CreateInvalidInputErrorResult()); + var er = ResultHelper.CreateInvalidInputErrorResult(); + if (!string.IsNullOrEmpty(lastInputParsingErrorMsg)) + { + er.Details = new Details() { Body = lastInputParsingErrorMsg }; + } + + results.Add(er); } return results; diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj similarity index 95% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj index 733ed8634e..34301712cf 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Microsoft.CmdPal.Ext.TimeDate.csproj @@ -1,6 +1,7 @@  - + + Microsoft.CmdPal.Ext.TimeDate false diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs similarity index 98% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs index edcdb02e4b..024a559087 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Pages/TimeDateExtensionPage.cs @@ -30,6 +30,7 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage PlaceholderText = Resources.Microsoft_plugin_timedate_placeholder_text; Id = "com.microsoft.cmdpal.timedate"; _settingsManager = settingsManager; + ShowDetails = true; } public override IListItem[] GetItems() diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.Designer.cs similarity index 76% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.Designer.cs index 8891e81c2b..a21759d10a 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.Designer.cs @@ -150,6 +150,15 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Days in month. + /// + public static string Microsoft_plugin_timedate_DaysInMonth { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_DaysInMonth", resourceCulture); + } + } + /// /// Looks up a localized string similar to Era. /// @@ -169,20 +178,47 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } /// - /// Looks up a localized string similar to Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time. + /// Looks up a localized string similar to Failed to convert into custom format. /// - public static string Microsoft_plugin_timedate_ErrorResultSubTitle { + public static string Microsoft_plugin_timedate_ErrorConvertCustomFormat { get { - return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorResultSubTitle", resourceCulture); + return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorConvertCustomFormat", resourceCulture); } } /// - /// Looks up a localized string similar to Error: Invalid number input. + /// Looks up a localized string similar to Not a valid Windows file time. /// - public static string Microsoft_plugin_timedate_ErrorResultTitle { + public static string Microsoft_plugin_timedate_ErrorConvertWft { get { - return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorResultTitle", resourceCulture); + return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorConvertWft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Excel's 1900 date value. + /// + public static string Microsoft_plugin_timedate_Excel1900 { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_Excel1900", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Excel's 1904 date value. + /// + public static string Microsoft_plugin_timedate_Excel1904 { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_Excel1904", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open Time Data Command. + /// + public static string Microsoft_plugin_timedate_fallback_display_title { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_fallback_display_title", resourceCulture); } } @@ -205,11 +241,20 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } /// - /// Looks up a localized string similar to Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time. + /// Looks up a localized string similar to Invalid custom format:. /// - public static string Microsoft_plugin_timedate_InvalidInput_ErrorMessageSubTitle { + public static string Microsoft_plugin_timedate_InvalidCustomFormat { get { - return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_ErrorMessageSubTitle", resourceCulture); + return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidCustomFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Supported input. + /// + public static string Microsoft_plugin_timedate_InvalidInput_DetailsHeader { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_DetailsHeader", resourceCulture); } } @@ -222,6 +267,33 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Cannot parse the input as Excel's 1900 date value because it is a fake date. (In Excel 0 stands for 0/1/1900 and this date doesn't exist. And 60 stands for 2/29/1900 and this date only exists in Excel for compatibility with Lotus 123.). + /// + public static string Microsoft_plugin_timedate_InvalidInput_FakeExcel1900 { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_FakeExcel1900", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A {0}format name{0}, a {0}valid date or time value{0}, or a {0}prefixed number{0}. To search for a format in a specific date/time please use the syntax {0}format::date/time/number{0}.{1}Supported prefixes:{2}'{0}u{0}' for Unix Timestamp{2}'{0}ums{0}' for Unix Timestamp in milliseconds{2}'{0}ft{0}' for Windows file time{2}'{0}oa{0}' for OLE Automation Date{2}'{0}exc{0}' for Excel's 1900 date value{2}'{0}exf{0}' for Excel's 1904 date value. + /// + public static string Microsoft_plugin_timedate_InvalidInput_SupportedInput { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_SupportedInput", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your input for {0} is outside the range **from {1} to {2}**.. + /// + public static string Microsoft_plugin_timedate_InvalidInput_SupportedRange { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_SupportedRange", resourceCulture); + } + } + /// /// Looks up a localized string similar to ISO 8601. /// @@ -258,6 +330,15 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Leap year. + /// + public static string Microsoft_plugin_timedate_LeapYear { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_LeapYear", resourceCulture); + } + } + /// /// Looks up a localized string similar to Open. /// @@ -321,6 +402,15 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Not a leap year. + /// + public static string Microsoft_plugin_timedate_NoLeapYear { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_NoLeapYear", resourceCulture); + } + } + /// /// Looks up a localized string similar to Now. /// @@ -339,6 +429,15 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to OLE Automation Date. + /// + public static string Microsoft_plugin_timedate_OADate { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_OADate", resourceCulture); + } + } + /// /// Looks up a localized string similar to Search values or type a custom time stamp.... /// @@ -411,6 +510,42 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Date and time; Time and Date; Custom format. + /// + public static string Microsoft_plugin_timedate_SearchTagCustom { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustom", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current date and time; Current time and date; Now; Custom format. + /// + public static string Microsoft_plugin_timedate_SearchTagCustomNow { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomNow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date and time UTC; Time UTC and Date; Custom UTC format. + /// + public static string Microsoft_plugin_timedate_SearchTagCustomUtc { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomUtc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current date and time UTC; Current time UTC and date; Now UTC; Custom UTC format. + /// + public static string Microsoft_plugin_timedate_SearchTagCustomUtcNow { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomUtcNow", resourceCulture); + } + } + /// /// Looks up a localized string similar to Date. /// @@ -483,6 +618,15 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Current Week; Calendar week; Week of the year; Week. + /// + public static string Microsoft_plugin_timedate_SearchTagWeek { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagWeek", resourceCulture); + } + } + /// /// Looks up a localized string similar to Second. /// @@ -492,6 +636,24 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Custom formats. + /// + public static string Microsoft_plugin_timedate_Setting_CustomFormats { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_CustomFormats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use date and time string format syntax and {0} (Day of Week), {1} (Days in Month), {2} (Week of Month), {3} (Week of the year), {4} (Era abbreviation), {5} (Windows File Time), {6} (Unix Time), {7} (Unix Time in milliseconds), {8} (OLE Automation date), {9} (Excel's 1900 based date value), {10} (Excel's 1904 based date value). If the format starts with {11}, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.). + /// + public static string Microsoft_plugin_timedate_Setting_CustomFormatsDescription { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_CustomFormatsDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use system setting. /// @@ -519,6 +681,24 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Enable fallback items for TimeDate (week, year, now, time, date). + /// + public static string Microsoft_plugin_timedate_SettingEnableFallbackItems { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingEnableFallbackItems", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Show time and date results when typing keywords like "week", "year", "now", "time", or "date". + /// + public static string Microsoft_plugin_timedate_SettingEnableFallbackItems_Description { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_SettingEnableFallbackItems_Description", resourceCulture); + } + } + /// /// Looks up a localized string similar to First day of the week. /// @@ -636,33 +816,6 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } - /// - /// Looks up a localized string similar to Hide 'Invalid number input' error message on global queries. - /// - public static string Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery { - get { - return ResourceManager.GetString("Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show only 'Time', 'Date' and 'Now' result for system time on global queries. - /// - public static string Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal { - get { - return ResourceManager.GetString("Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Regardless of this setting, for global queries the first word of the query has to be a complete match.. - /// - public static string Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal_Description { - get { - return ResourceManager.GetString("Microsoft_plugin_timedate_SettingOnlyDateTimeNowGlobal_Description", resourceCulture); - } - } - /// /// Looks up a localized string similar to Show time with seconds. /// @@ -681,6 +834,15 @@ namespace Microsoft.CmdPal.Ext.TimeDate { } } + /// + /// Looks up a localized string similar to Select for more details.. + /// + public static string Microsoft_plugin_timedate_show_details { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_show_details", resourceCulture); + } + } + /// /// Looks up a localized string similar to Select or press Ctrl+C to copy. /// diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.resx similarity index 76% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.resx index a3974911c4..35862592ca 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/Properties/Resources.resx @@ -155,11 +155,8 @@ Era abbreviation - - Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time - - - Error: Invalid number input + + Supported input Hour @@ -268,15 +265,6 @@ This setting applies to the 'Date' and 'Now' result. - - Hide 'Invalid number input' error message on global queries - - - Show only 'Time', 'Date' and 'Now' result for system time on global queries - - - Regardless of this setting, for global queries the first word of the query has to be a complete match. - Show time with seconds @@ -372,7 +360,80 @@ Time and Date - - Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time + + A {0}format name{0}, a {0}valid date or time value{0}, or a {0}prefixed number{0}. To search for a format in a specific date/time please use the syntax {0}format::date/time/number{0}.{1}Supported prefixes:{2}'{0}u{0}' for Unix Timestamp{2}'{0}ums{0}' for Unix Timestamp in milliseconds{2}'{0}ft{0}' for Windows file time{2}'{0}oa{0}' for OLE Automation Date{2}'{0}exc{0}' for Excel's 1900 date value{2}'{0}exf{0}' for Excel's 1904 date value + The placed holders are replaced with formatting syntax in code. + + + Date and time; Time and Date; Custom format + Don't change order + + + Date and time UTC; Time UTC and Date; Custom UTC format + Don't change order + + + Current date and time; Current time and date; Now; Custom format + Don't change order + + + Current date and time UTC; Current time UTC and date; Now UTC; Custom UTC format + Don't change order + + + Invalid custom format: + + + Custom formats + + + Use date and time string format syntax and {0} (Day of Week), {1} (Days in Month), {2} (Week of Month), {3} (Week of the year), {4} (Era abbreviation), {5} (Windows File Time), {6} (Unix Time), {7} (Unix Time in milliseconds), {8} (OLE Automation date), {9} (Excel's 1900 based date value), {10} (Excel's 1904 based date value). If the format starts with {11}, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.) + The {n} parts are place holders and get replaced in the code. + + + Select for more details. + + + Failed to convert into custom format + + + Not a valid Windows file time + + + Your input for {0} is outside the range **from {1} to {2}**. + The placeholder will be replace in code. + + + Cannot parse the input as Excel's 1900 date value because it is a fake date. (In Excel 0 stands for 0/1/1900 and this date doesn't exist. And 60 stands for 2/29/1900 and this date only exists in Excel for compatibility with Lotus 123.) + + + OLE Automation Date + + + Excel's 1900 date value + + + Excel's 1904 date value + + + Leap year + + + Not a leap year + + + Days in month + + + Open Time Data Command + + + Current Week; Calendar week; Week of the year; Week + + + Enable fallback items for TimeDate (week, year, now, time, date) + + + Show time and date results when typing keywords like "week", "year", "now", "time", or "date" \ No newline at end of file diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs similarity index 93% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs index 05597c4553..d29356fa77 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.TimeDate/TimeDateCommandsProvider.cs @@ -18,6 +18,7 @@ public partial class TimeDateCommandsProvider : CommandProvider private static readonly SettingsManager _settingsManager = new(); private static readonly CompositeFormat MicrosoftPluginTimedatePluginDescription = System.Text.CompositeFormat.Parse(Resources.Microsoft_plugin_timedate_plugin_description); private static readonly TimeDateExtensionPage _timeDateExtensionPage = new(_settingsManager); + private readonly FallbackTimeDateItem _fallbackTimeDateItem = new(_settingsManager); public TimeDateCommandsProvider() { @@ -45,4 +46,6 @@ public partial class TimeDateCommandsProvider : CommandProvider } public override ICommandItem[] TopLevelCommands() => [_command]; + + public override IFallbackCommandItem[] FallbackCommands() => [_fallbackTimeDateItem]; } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Assets/WebSearch.svg diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Commands/OpenURLCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Commands/OpenURLCommand.cs new file mode 100644 index 0000000000..fd3f3a8f18 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Commands/OpenURLCommand.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CmdPal.Ext.WebSearch.Helpers; +using Microsoft.CommandPalette.Extensions.Toolkit; + +using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo; + +namespace Microsoft.CmdPal.Ext.WebSearch.Commands; + +internal sealed partial class OpenURLCommand : InvokableCommand +{ + private readonly SettingsManager _settingsManager; + + public string Url { get; internal set; } = string.Empty; + + internal OpenURLCommand(string url, SettingsManager settingsManager) + { + Url = url; + BrowserInfo.UpdateIfTimePassed(); + Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png"); + Name = string.Empty; + _settingsManager = settingsManager; + } + + public override CommandResult Invoke() + { + if (!ShellHelpers.OpenCommandInShell(BrowserInfo.Path, BrowserInfo.ArgumentsPattern, $"{Url}")) + { + // TODO GH# 138 --> actually display feedback from the extension somewhere. + return CommandResult.KeepOpen(); + } + + return CommandResult.Dismiss(); + } +} diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/Commands/SearchWebCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Commands/SearchWebCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/Commands/SearchWebCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Commands/SearchWebCommand.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/FallbackExecuteSearchItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/FallbackExecuteSearchItem.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/FallbackExecuteSearchItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/FallbackExecuteSearchItem.cs diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/FallbackOpenURLItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/FallbackOpenURLItem.cs new file mode 100644 index 0000000000..721af2ec8e --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/FallbackOpenURLItem.cs @@ -0,0 +1,88 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.Text; +using Microsoft.CmdPal.Ext.WebSearch.Commands; +using Microsoft.CmdPal.Ext.WebSearch.Helpers; +using Microsoft.CmdPal.Ext.WebSearch.Properties; +using Microsoft.CommandPalette.Extensions.Toolkit; +using BrowserInfo = Microsoft.CmdPal.Ext.WebSearch.Helpers.DefaultBrowserInfo; + +namespace Microsoft.CmdPal.Ext.WebSearch; + +internal sealed partial class FallbackOpenURLItem : FallbackCommandItem +{ + private readonly OpenURLCommand _executeItem; + private static readonly CompositeFormat PluginOpenURL = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open_url); + private static readonly CompositeFormat PluginOpenUrlInBrowser = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open_url_in_browser); + + public FallbackOpenURLItem(SettingsManager settings) + : base(new OpenURLCommand(string.Empty, settings), string.Empty) + { + _executeItem = (OpenURLCommand)this.Command!; + Title = string.Empty; + _executeItem.Name = string.Empty; + Subtitle = string.Empty; + Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png"); + } + + public override void UpdateQuery(string query) + { + if (!IsValidUrl(query)) + { + Title = string.Empty; + Subtitle = string.Empty; + return; + } + + var success = Uri.TryCreate(query, UriKind.Absolute, out var uri); + + // if url not contain schema, add http:// by default. + if (!success) + { + query = "https://" + query; + } + + _executeItem.Url = query; + _executeItem.Name = string.IsNullOrEmpty(query) ? string.Empty : Properties.Resources.open_in_default_browser; + + Title = string.Format(CultureInfo.CurrentCulture, PluginOpenURL, query); + Subtitle = string.Format(CultureInfo.CurrentCulture, PluginOpenUrlInBrowser, BrowserInfo.Name ?? BrowserInfo.MSEdgeName); + } + + public static bool IsValidUrl(string url) + { + if (string.IsNullOrWhiteSpace(url)) + { + return false; + } + + if (!url.Contains('.', StringComparison.OrdinalIgnoreCase)) + { + // eg: 'com', 'org'. We don't think it's a valid url. + // This can simplify the logic of checking if the url is valid. + return false; + } + + if (Uri.IsWellFormedUriString(url, UriKind.Absolute)) + { + return true; + } + + if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && + !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase) && + !url.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase) && + !url.StartsWith("file://", StringComparison.OrdinalIgnoreCase)) + { + if (Uri.IsWellFormedUriString("https://" + url, UriKind.Absolute)) + { + return true; + } + } + + return false; + } +} diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/DefaultBrowserInfo.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/HistoryItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/HistoryItem.cs similarity index 80% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/HistoryItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/HistoryItem.cs index 84a1c249ba..d381c1e4cc 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/HistoryItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/HistoryItem.cs @@ -13,5 +13,5 @@ public class HistoryItem(string searchString, DateTime timestamp) public DateTime Timestamp { get; private set; } = timestamp; - public string ToJson() => JsonSerializer.Serialize(this); + public string ToJson() => JsonSerializer.Serialize(this, WebSearchJsonSerializationContext.Default.HistoryItem); } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/SettingsManager.cs similarity index 94% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/SettingsManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/SettingsManager.cs index b83ba47a73..8a39bca35b 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Helpers/SettingsManager.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/SettingsManager.cs @@ -80,7 +80,7 @@ public class SettingsManager : JsonSettingsManager if (File.Exists(_historyPath)) { var existingContent = File.ReadAllText(_historyPath); - historyItems = JsonSerializer.Deserialize>(existingContent) ?? []; + historyItems = JsonSerializer.Deserialize>(existingContent, WebSearchJsonSerializationContext.Default.ListHistoryItem) ?? []; } else { @@ -101,7 +101,7 @@ public class SettingsManager : JsonSettingsManager } // Serialize the updated list back to JSON and save it - var historyJson = JsonSerializer.Serialize(historyItems); + var historyJson = JsonSerializer.Serialize(historyItems, WebSearchJsonSerializationContext.Default.ListHistoryItem); File.WriteAllText(_historyPath, historyJson); } catch (Exception ex) @@ -121,7 +121,7 @@ public class SettingsManager : JsonSettingsManager // Read and deserialize JSON into a list of HistoryItem objects var fileContent = File.ReadAllText(_historyPath); - var historyItems = JsonSerializer.Deserialize>(fileContent) ?? []; + var historyItems = JsonSerializer.Deserialize>(fileContent, WebSearchJsonSerializationContext.Default.ListHistoryItem) ?? []; // Convert each HistoryItem to a ListItem var listItems = new List(); @@ -198,7 +198,7 @@ public class SettingsManager : JsonSettingsManager if (File.Exists(_historyPath)) { var existingContent = File.ReadAllText(_historyPath); - var historyItems = JsonSerializer.Deserialize>(existingContent) ?? []; + var historyItems = JsonSerializer.Deserialize>(existingContent, WebSearchJsonSerializationContext.Default.ListHistoryItem) ?? []; // Check if trimming is needed if (historyItems.Count > maxHistoryItems) @@ -207,7 +207,7 @@ public class SettingsManager : JsonSettingsManager historyItems = historyItems.Skip(historyItems.Count - maxHistoryItems).ToList(); // Save the trimmed history back to the file - var trimmedHistoryJson = JsonSerializer.Serialize(historyItems); + var trimmedHistoryJson = JsonSerializer.Serialize(historyItems, WebSearchJsonSerializationContext.Default.ListHistoryItem); File.WriteAllText(_historyPath, trimmedHistoryJson); } } diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/WebSearchJsonSerializationContext.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/WebSearchJsonSerializationContext.cs new file mode 100644 index 0000000000..443c9cdf40 --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Helpers/WebSearchJsonSerializationContext.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Microsoft.CmdPal.Ext.WebSearch.Helpers; + +namespace Microsoft.CmdPal.Ext.WebSearch; + +[JsonSerializable(typeof(float))] +[JsonSerializable(typeof(int))] +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(bool))] +[JsonSerializable(typeof(HistoryItem))] +[JsonSerializable(typeof(List))] +[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true, IncludeFields = true, PropertyNameCaseInsensitive = true, AllowTrailingCommas = true)] +internal sealed partial class WebSearchJsonSerializationContext : JsonSerializerContext +{ +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj similarity index 96% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj index 3ddedfcd71..3fbaeb30a7 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Microsoft.CmdPal.Ext.WebSearch.csproj @@ -1,5 +1,6 @@  + Microsoft.CmdPal.Ext.WebSearch enable diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/NativeMethods.txt similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WebSearch/NativeMethods.txt rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/NativeMethods.txt diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Pages/WebSearchListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Pages/WebSearchListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Pages/WebSearchListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Pages/WebSearchListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs similarity index 92% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs index e138b74ecc..15a8c4631f 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.Designer.cs @@ -195,6 +195,24 @@ namespace Microsoft.CmdPal.Ext.WebSearch.Properties { } } + /// + /// Looks up a localized string similar to Open "{0}". + /// + public static string plugin_open_url { + get { + return ResourceManager.GetString("plugin_open_url", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open url in {0}. + /// + public static string plugin_open_url_in_browser { + get { + return ResourceManager.GetString("plugin_open_url_in_browser", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to open {0}.. /// diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx similarity index 97% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx index 9caaca6c2f..aec5b771c9 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/Properties/Resources.resx @@ -163,6 +163,12 @@ Search the web in {0} + + Open "{0}" + + + Open url in {0} + Failed to open {0}. diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs similarity index 88% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs index 7772d9b8b3..6768ff8baf 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/WebSearchCommandsProvider.cs @@ -14,6 +14,7 @@ public partial class WebSearchCommandsProvider : CommandProvider { private readonly SettingsManager _settingsManager = new(); private readonly FallbackExecuteSearchItem _fallbackItem; + private readonly FallbackOpenURLItem _openUrlFallbackItem; public WebSearchCommandsProvider() { @@ -23,6 +24,7 @@ public partial class WebSearchCommandsProvider : CommandProvider Settings = _settingsManager.Settings; _fallbackItem = new FallbackExecuteSearchItem(_settingsManager); + _openUrlFallbackItem = new FallbackOpenURLItem(_settingsManager); } public override ICommandItem[] TopLevelCommands() @@ -36,5 +38,5 @@ public partial class WebSearchCommandsProvider : CommandProvider ]; } - public override IFallbackCommandItem[]? FallbackCommands() => [_fallbackItem]; + public override IFallbackCommandItem[]? FallbackCommands() => [_openUrlFallbackItem, _fallbackItem]; } diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchTopLevelCommandItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/WebSearchTopLevelCommandItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WebSearch/WebSearchTopLevelCommandItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WebSearch/WebSearchTopLevelCommandItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Extension.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.dark.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/Store.light.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Assets/WinGet.svg diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Microsoft.CmdPal.Ext.WinGet.csproj diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/NativeMethods.txt b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.txt similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/NativeMethods.txt rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/NativeMethods.txt diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs similarity index 83% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs index 1aa22bfa26..cf85359bdf 100644 --- a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageCommand.cs @@ -22,10 +22,12 @@ public partial class InstallPackageCommand : InvokableCommand private IAsyncOperationWithProgress? _unInstallAction; private Task? _installTask; - public bool IsInstalled { get; private set; } + public PackageInstallCommandState InstallCommandState { get; private set; } public static IconInfo CompletedIcon { get; } = new("\uE930"); // Completed + public static IconInfo UpdateIcon { get; } = new("\uE74A"); // Up + public static IconInfo DownloadIcon { get; } = new("\uE896"); // Download public static IconInfo DeleteIcon { get; } = new("\uE74D"); // Delete @@ -44,23 +46,41 @@ public partial class InstallPackageCommand : InvokableCommand internal bool SkipDependencies { get; set; } - public InstallPackageCommand(CatalogPackage package, bool isInstalled) + public InstallPackageCommand(CatalogPackage package, PackageInstallCommandState isInstalled) { _package = package; - IsInstalled = isInstalled; + InstallCommandState = isInstalled; UpdateAppearance(); } internal void FakeChangeStatus() { - IsInstalled = !IsInstalled; + InstallCommandState = InstallCommandState switch + { + PackageInstallCommandState.Install => PackageInstallCommandState.Uninstall, + PackageInstallCommandState.Update => PackageInstallCommandState.Uninstall, + PackageInstallCommandState.Uninstall => PackageInstallCommandState.Install, + _ => throw new NotImplementedException(), + }; UpdateAppearance(); } private void UpdateAppearance() { - Icon = IsInstalled ? CompletedIcon : DownloadIcon; - Name = IsInstalled ? Properties.Resources.winget_uninstall_name : Properties.Resources.winget_install_name; + Icon = InstallCommandState switch + { + PackageInstallCommandState.Install => DownloadIcon, + PackageInstallCommandState.Update => UpdateIcon, + PackageInstallCommandState.Uninstall => CompletedIcon, + _ => throw new NotImplementedException(), + }; + Name = InstallCommandState switch + { + PackageInstallCommandState.Install => Properties.Resources.winget_install_name, + PackageInstallCommandState.Update => Properties.Resources.winget_update_name, + PackageInstallCommandState.Uninstall => Properties.Resources.winget_uninstall_name, + _ => throw new NotImplementedException(), + }; } public override ICommandResult Invoke() @@ -72,7 +92,7 @@ public partial class InstallPackageCommand : InvokableCommand return CommandResult.KeepOpen(); } - if (IsInstalled) + if (InstallCommandState == PackageInstallCommandState.Uninstall) { // Uninstall _installBanner.State = MessageState.Info; @@ -88,7 +108,8 @@ public partial class InstallPackageCommand : InvokableCommand _installTask = Task.Run(() => TryDoInstallOperation(_unInstallAction)); } - else + else if (InstallCommandState is PackageInstallCommandState.Install or + PackageInstallCommandState.Update) { // Install _installBanner.State = MessageState.Info; @@ -117,7 +138,8 @@ public partial class InstallPackageCommand : InvokableCommand try { await action.AsTask(); - _installBanner.Message = IsInstalled ? + + _installBanner.Message = InstallCommandState == PackageInstallCommandState.Uninstall ? string.Format(CultureInfo.CurrentCulture, UninstallPackageFinished, _package.Name) : string.Format(CultureInfo.CurrentCulture, InstallPackageFinished, _package.Name); @@ -125,9 +147,10 @@ public partial class InstallPackageCommand : InvokableCommand _installBanner.State = MessageState.Success; _installTask = null; - _ = Task.Run(() => + _ = Task.Run(async () => { - Thread.Sleep(2500); + await Task.Delay(2500).ConfigureAwait(false); + if (_installTask == null) { WinGetExtensionHost.Instance.HideStatus(_installBanner); @@ -228,3 +251,10 @@ public partial class InstallPackageCommand : InvokableCommand } } } + +public enum PackageInstallCommandState +{ + Uninstall = 0, + Update = 1, + Install = 2, +} diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs similarity index 90% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs index 2ce192f262..5c556ec3cb 100644 --- a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/InstallPackageListItem.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using System.Threading.Tasks; using ManagedCommon; using Microsoft.CommandPalette.Extensions; @@ -31,7 +32,7 @@ public partial class InstallPackageListItem : ListItem { _package = package; - var version = _package.DefaultInstallVersion; + var version = _package.DefaultInstallVersion ?? _package.InstalledVersion; var versionTagText = "Unknown"; if (version != null) { @@ -49,7 +50,16 @@ public partial class InstallPackageListItem : ListItem private Details? BuildDetails(PackageVersionInfo? version) { - var metadata = version?.GetCatalogPackageMetadata(); + CatalogPackageMetadata? metadata = null; + try + { + metadata = version?.GetCatalogPackageMetadata(); + } + catch (COMException ex) + { + Logger.LogWarning($"{ex.ErrorCode}"); + } + if (metadata != null) { if (metadata.Tags.Where(t => t.Equals(WinGetExtensionPage.ExtensionsTag, StringComparison.OrdinalIgnoreCase)).Any()) @@ -149,12 +159,17 @@ public partial class InstallPackageListItem : ListItem var status = await _package.CheckInstalledStatusAsync(); var isInstalled = _package.InstalledVersion != null; + var installedState = isInstalled ? + (_package.IsUpdateAvailable ? + PackageInstallCommandState.Update : PackageInstallCommandState.Uninstall) : + PackageInstallCommandState.Install; + // might be an uninstall command - InstallPackageCommand installCommand = new(_package, isInstalled); + InstallPackageCommand installCommand = new(_package, installedState); if (isInstalled) { - this.Icon = InstallPackageCommand.CompletedIcon; + this.Icon = installCommand.Icon; this.Command = new NoOpCommand(); List contextMenu = []; CommandContextItem uninstallContextItem = new(installCommand) @@ -180,7 +195,7 @@ public partial class InstallPackageListItem : ListItem } // didn't find the app - _installCommand = new InstallPackageCommand(_package, isInstalled); + _installCommand = new InstallPackageCommand(_package, installedState); this.Command = _installCommand; Icon = _installCommand.Icon; diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Pages/WinGetExtensionPage.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs similarity index 97% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs index f62a291d36..418cccf58c 100644 --- a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.Designer.cs @@ -330,6 +330,15 @@ namespace Microsoft.CmdPal.Ext.WinGet.Properties { } } + /// + /// Looks up a localized string similar to Update. + /// + public static string winget_update_name { + get { + return ResourceManager.GetString("winget_update_name", resourceCulture); + } + } + /// /// Looks up a localized string similar to View online. /// diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx similarity index 98% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx index 2be07cec68..ac2128430d 100644 --- a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/Properties/Resources.resx @@ -154,6 +154,10 @@ Install + + Update + + Uninstalling {0}... {0} will be replaced by the name of an app package diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionCommandsProvider.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionHost.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionHost.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionHost.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WinGetExtensionHost.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WinGetStatics.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WinGetStatics.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WinGetStatics.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WinGetStatics.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassModel.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassModel.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassModel.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassModel.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassesDefinition.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassesDefinition.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassesDefinition.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClassesDefinition.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClsidContext.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClsidContext.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClsidContext.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/ClsidContext.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerFactory.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerFactory.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerFactory.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/WindowsPackageManager.Interop/WindowsPackageManagerStandardFactory.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/app.manifest b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/app.manifest similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WinGet/app.manifest rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WinGet/app.manifest diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Assets/WindowWalker.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/CloseWindowCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/CloseWindowCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/CloseWindowCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/CloseWindowCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/ExplorerInfoResultCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/ExplorerInfoResultCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/ExplorerInfoResultCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/ExplorerInfoResultCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/KillProcessCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/KillProcessCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/KillProcessCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/KillProcessCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/SwitchToWindowCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/SwitchToWindowCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Commands/SwitchToWindowCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Commands/SwitchToWindowCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ContextMenuHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/FuzzyMatching.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/FuzzyMatching.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/FuzzyMatching.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/FuzzyMatching.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/LivePreview.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/LivePreview.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/LivePreview.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/LivePreview.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/OpenWindows.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/OpenWindows.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/OpenWindows.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/OpenWindows.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/ResultHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ResultHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/ResultHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/ResultHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchController.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchController.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchController.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchController.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchResult.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchResult.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchResult.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchResult.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchString.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchString.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchString.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/SearchString.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/Window.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/Window.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/Window.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/Window.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/WindowProcess.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/WindowProcess.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Components/WindowProcess.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Components/WindowProcess.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/CVirtualDesktopManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/CVirtualDesktopManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/CVirtualDesktopManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/CVirtualDesktopManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/IVirtualDesktopManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/NativeMethods.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/OSVersionHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/OSVersionHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/OSVersionHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/OSVersionHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/SettingsManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/SettingsManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/SettingsManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/ShellCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/ShellCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/ShellCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/ShellCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VDesktop.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VDesktop.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VDesktop.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VDesktop.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/VirtualDesktopHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/Win32Helpers.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/Win32Helpers.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Helpers/Win32Helpers.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Helpers/Win32Helpers.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj similarity index 96% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj index 2ed59ad6a3..e346e824c8 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Microsoft.CmdPal.Ext.WindowWalker.csproj @@ -1,5 +1,7 @@  + + enable Microsoft.CmdPal.Ext.WindowWalker diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Pages/WindowWalkerListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Pages/WindowWalkerListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Pages/WindowWalkerListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Pages/WindowWalkerListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/Properties/Resources.resx diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerListItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerListItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerListItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowWalker/WindowWalkerListItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Action.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Action.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Action.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Action.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Assets/Services.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Commands/OpenServicesCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Commands/OpenServicesCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Commands/OpenServicesCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Commands/OpenServicesCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Commands/RestartServiceCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Commands/RestartServiceCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Commands/RestartServiceCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Commands/RestartServiceCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Commands/ServiceCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Commands/ServiceCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Commands/ServiceCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Commands/ServiceCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Helpers/ServiceHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Helpers/ServiceHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Helpers/ServiceHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Helpers/ServiceHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj similarity index 96% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj index 2b7b9345ec..9038a2d671 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Microsoft.CmdPal.Ext.WindowsServices.csproj @@ -1,5 +1,7 @@  + + Microsoft.CmdPal.Ext.WindowsServices $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Pages/ServicesListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Pages/ServicesListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Pages/ServicesListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Pages/ServicesListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.Designer.cs similarity index 99% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.Designer.cs index c20800d1c1..ce71199763 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.Designer.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.Designer.cs @@ -88,7 +88,7 @@ namespace Microsoft.CmdPal.Ext.WindowsServices.Properties { } /// - /// Looks up a localized string similar to Open services (Ctrl+O). + /// Looks up a localized string similar to Open services. /// internal static string wox_plugin_service_open_services { get { @@ -133,7 +133,7 @@ namespace Microsoft.CmdPal.Ext.WindowsServices.Properties { } /// - /// Looks up a localized string similar to Restart (Ctrl+R). + /// Looks up a localized string similar to Restart. /// internal static string wox_plugin_service_restart { get { diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/Properties/Resources.resx diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/ServiceResult.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/ServiceResult.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/ServiceResult.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/ServiceResult.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/WindowsServicesCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/WindowsServicesCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsServices/WindowsServicesCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsServices/WindowsServicesCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Assets/WindowsSettings.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSetting.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSetting.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSetting.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSetting.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSettings.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSettings.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSettings.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Classes/WindowsSettings.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Commands/CopySettingCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Commands/CopySettingCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Commands/CopySettingCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Commands/CopySettingCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Commands/OpenSettingsCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Commands/OpenSettingsCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Commands/OpenSettingsCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Commands/OpenSettingsCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ContextMenuHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ContextMenuHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ContextMenuHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ContextMenuHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/JsonSettingsListHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/JsonSettingsListHelper.cs similarity index 83% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/JsonSettingsListHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/JsonSettingsListHelper.cs index f99f0ce3b3..18b2548ce2 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/JsonSettingsListHelper.cs +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/JsonSettingsListHelper.cs @@ -4,10 +4,8 @@ using System; using System.IO; -using System.Linq; using System.Reflection; using System.Text.Json; -using System.Text.Json.Serialization; namespace Microsoft.CmdPal.Ext.WindowsSettings.Helpers; @@ -21,6 +19,8 @@ internal static class JsonSettingsListHelper /// private const string _settingsFile = "WindowsSettings.json"; + private const string _extTypeNamespace = "Microsoft.CmdPal.Ext.WindowsSettings"; + private static readonly JsonSerializerOptions _serializerOptions = new() { }; @@ -32,7 +32,6 @@ internal static class JsonSettingsListHelper internal static Classes.WindowsSettings ReadAllPossibleSettings() { var assembly = Assembly.GetExecutingAssembly(); - var type = assembly.GetTypes().FirstOrDefault(x => x.Name == nameof(WindowsSettingsCommandsProvider)); #pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Classes.WindowsSettings? settings = null; @@ -40,7 +39,7 @@ internal static class JsonSettingsListHelper try { - var resourceName = $"{type?.Namespace}.{_settingsFile}"; + var resourceName = $"{_extTypeNamespace}.{_settingsFile}"; using var stream = assembly.GetManifestResourceStream(resourceName); if (stream is null) { @@ -48,12 +47,13 @@ internal static class JsonSettingsListHelper } var options = _serializerOptions; - options.Converters.Add(new JsonStringEnumConverter()); + // Why we need it? I don't see any enum usage in WindowsSettings + // options.Converters.Add(new JsonStringEnumConverter()); using var reader = new StreamReader(stream); var text = reader.ReadToEnd(); - settings = JsonSerializer.Deserialize(text, options); + settings = JsonSerializer.Deserialize(text, WindowsSettingsJsonSerializationContext.Default.WindowsSettings); } #pragma warning disable CS0168 catch (Exception exception) diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ResultHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ResultHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ResultHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/ResultHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/TranslationHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/TranslationHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/TranslationHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/TranslationHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/UnsupportedSettingsHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/UnsupportedSettingsHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/UnsupportedSettingsHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/UnsupportedSettingsHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/WindowsSettingsPathHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/WindowsSettingsPathHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/WindowsSettingsPathHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Helpers/WindowsSettingsPathHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.dark.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.dark.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.dark.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.dark.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.light.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.light.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.light.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Images/WindowsSettings.light.png diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/JsonSerializationContext.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/JsonSerializationContext.cs new file mode 100644 index 0000000000..e267e8f52e --- /dev/null +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/JsonSerializationContext.cs @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Microsoft.CmdPal.Ext.WindowsSettings; + +[JsonSerializable(typeof(float))] +[JsonSerializable(typeof(int))] +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(bool))] +[JsonSerializable(typeof(Classes.WindowsSettings))] +[JsonSourceGenerationOptions(UseStringEnumConverter = true, WriteIndented = true, IncludeFields = true, PropertyNameCaseInsensitive = true, AllowTrailingCommas = true)] +internal sealed partial class WindowsSettingsJsonSerializationContext : JsonSerializerContext +{ +} diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj similarity index 96% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj index d44e907606..9f2d72bc8a 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Microsoft.CmdPal.Ext.WindowsSettings.csproj @@ -1,5 +1,6 @@  + Microsoft.CmdPal.Ext.WindowsSettings $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Pages/WindowsSettingsListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/Properties/Resources.resx diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.json similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.json rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.json diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.schema.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.schema.json similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.schema.json rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettings.schema.json diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettingsCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettingsCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettingsCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsSettings/WindowsSettingsCommandsProvider.cs diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.dark.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.dark.png similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.dark.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.dark.png diff --git a/src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.light.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.light.png similarity index 100% rename from src/modules/cmdpal/exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.light.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.light.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.png b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.png similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.png rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.png diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.svg b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.svg similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.svg rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Assets/WindowsTerminal.svg diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileAsAdminCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Commands/LaunchProfileCommand.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ApplicationActivationManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/IApplicationActivationManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ITerminalQuery.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ITerminalQuery.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ITerminalQuery.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/ITerminalQuery.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/SettingsManager.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/SettingsManager.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/SettingsManager.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalHelper.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalHelper.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalHelper.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalQuery.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalQuery.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalQuery.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Helpers/TerminalQuery.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj similarity index 96% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj index 7ea6f17148..1c09c35c5d 100644 --- a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj +++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Microsoft.CmdPal.Ext.WindowsTerminal.csproj @@ -1,6 +1,8 @@  + + Microsoft.CmdPal.Ext.WindowsTerminal diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Pages/ProfilesListPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Pages/ProfilesListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Pages/ProfilesListPage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Pages/ProfilesListPage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.Designer.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.Designer.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.Designer.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.Designer.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.resx b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.resx similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.resx rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/Properties/Resources.resx diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalPackage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalPackage.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalPackage.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalPackage.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalProfile.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalProfile.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalProfile.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalProfile.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalTopLevelCommandItem.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalTopLevelCommandItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalTopLevelCommandItem.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/TerminalTopLevelCommandItem.cs diff --git a/src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/WindowsTerminalCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/WindowsTerminalCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/Microsoft.CmdPal.Ext.WindowsTerminal/WindowsTerminalCommandsProvider.cs rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.WindowsTerminal/WindowsTerminalCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/LockScreenLogo.scale-200.png b/src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/LockScreenLogo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/LockScreenLogo.scale-200.png rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/LockScreenLogo.scale-200.png diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/SplashScreen.scale-200.png b/src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/SplashScreen.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/SplashScreen.scale-200.png rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/SplashScreen.scale-200.png diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Square150x150Logo.scale-200.png b/src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Square150x150Logo.scale-200.png rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Square150x150Logo.scale-200.png diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Square44x44Logo.scale-200.png b/src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Square44x44Logo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Square44x44Logo.scale-200.png rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Square44x44Logo.scale-200.png diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/StoreLogo.png b/src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/StoreLogo.png similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/StoreLogo.png rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/StoreLogo.png diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Wide310x150Logo.scale-200.png b/src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Wide310x150Logo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Assets/Wide310x150Logo.scale-200.png rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Assets/Wide310x150Logo.scale-200.png diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Package.appxmanifest b/src/modules/cmdpal/ext/ProcessMonitorExtension/Package.appxmanifest similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Package.appxmanifest rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Package.appxmanifest diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessItem.cs b/src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessItem.cs similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessItem.cs rename to src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessItem.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessListPage.cs b/src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessListPage.cs rename to src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessListPage.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessMonitorCommandProvider.cs b/src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessMonitorCommandProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessMonitorCommandProvider.cs rename to src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessMonitorCommandProvider.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessMonitorExtension.csproj b/src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessMonitorExtension.csproj similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/ProcessMonitorExtension.csproj rename to src/modules/cmdpal/ext/ProcessMonitorExtension/ProcessMonitorExtension.csproj diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Program.cs b/src/modules/cmdpal/ext/ProcessMonitorExtension/Program.cs similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Program.cs rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Program.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Properties/PublishProfiles/win-arm64.pubxml b/src/modules/cmdpal/ext/ProcessMonitorExtension/Properties/PublishProfiles/win-arm64.pubxml similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Properties/PublishProfiles/win-arm64.pubxml rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Properties/PublishProfiles/win-arm64.pubxml diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Properties/PublishProfiles/win-x64.pubxml b/src/modules/cmdpal/ext/ProcessMonitorExtension/Properties/PublishProfiles/win-x64.pubxml similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Properties/PublishProfiles/win-x64.pubxml rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Properties/PublishProfiles/win-x64.pubxml diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/Properties/launchSettings.json b/src/modules/cmdpal/ext/ProcessMonitorExtension/Properties/launchSettings.json similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/Properties/launchSettings.json rename to src/modules/cmdpal/ext/ProcessMonitorExtension/Properties/launchSettings.json diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/SampleExtension.cs b/src/modules/cmdpal/ext/ProcessMonitorExtension/SampleExtension.cs similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/SampleExtension.cs rename to src/modules/cmdpal/ext/ProcessMonitorExtension/SampleExtension.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/SwitchToProcess.cs b/src/modules/cmdpal/ext/ProcessMonitorExtension/SwitchToProcess.cs similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/SwitchToProcess.cs rename to src/modules/cmdpal/ext/ProcessMonitorExtension/SwitchToProcess.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/TerminateProcess.cs b/src/modules/cmdpal/ext/ProcessMonitorExtension/TerminateProcess.cs similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/TerminateProcess.cs rename to src/modules/cmdpal/ext/ProcessMonitorExtension/TerminateProcess.cs diff --git a/src/modules/cmdpal/Exts/ProcessMonitorExtension/app.manifest b/src/modules/cmdpal/ext/ProcessMonitorExtension/app.manifest similarity index 100% rename from src/modules/cmdpal/Exts/ProcessMonitorExtension/app.manifest rename to src/modules/cmdpal/ext/ProcessMonitorExtension/app.manifest diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Assets/LockScreenLogo.scale-200.png b/src/modules/cmdpal/ext/SamplePagesExtension/Assets/LockScreenLogo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Assets/LockScreenLogo.scale-200.png rename to src/modules/cmdpal/ext/SamplePagesExtension/Assets/LockScreenLogo.scale-200.png diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Assets/SplashScreen.scale-200.png b/src/modules/cmdpal/ext/SamplePagesExtension/Assets/SplashScreen.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Assets/SplashScreen.scale-200.png rename to src/modules/cmdpal/ext/SamplePagesExtension/Assets/SplashScreen.scale-200.png diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Square150x150Logo.scale-200.png b/src/modules/cmdpal/ext/SamplePagesExtension/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Square150x150Logo.scale-200.png rename to src/modules/cmdpal/ext/SamplePagesExtension/Assets/Square150x150Logo.scale-200.png diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Square44x44Logo.scale-200.png b/src/modules/cmdpal/ext/SamplePagesExtension/Assets/Square44x44Logo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Square44x44Logo.scale-200.png rename to src/modules/cmdpal/ext/SamplePagesExtension/Assets/Square44x44Logo.scale-200.png diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/src/modules/cmdpal/ext/SamplePagesExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png rename to src/modules/cmdpal/ext/SamplePagesExtension/Assets/Square44x44Logo.targetsize-24_altform-unplated.png diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Assets/StoreLogo.png b/src/modules/cmdpal/ext/SamplePagesExtension/Assets/StoreLogo.png similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Assets/StoreLogo.png rename to src/modules/cmdpal/ext/SamplePagesExtension/Assets/StoreLogo.png diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Wide310x150Logo.scale-200.png b/src/modules/cmdpal/ext/SamplePagesExtension/Assets/Wide310x150Logo.scale-200.png similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Assets/Wide310x150Logo.scale-200.png rename to src/modules/cmdpal/ext/SamplePagesExtension/Assets/Wide310x150Logo.scale-200.png diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/EvilSampleListPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/EvilSampleListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/EvilSampleListPage.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/EvilSampleListPage.cs diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/EvilSamplesPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/EvilSamplesPage.cs new file mode 100644 index 0000000000..2fc1218bd7 --- /dev/null +++ b/src/modules/cmdpal/ext/SamplePagesExtension/EvilSamplesPage.cs @@ -0,0 +1,262 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.System; + +namespace SamplePagesExtension; + +public partial class EvilSamplesPage : ListPage +{ + private readonly IListItem[] _commands = [ + new ListItem(new EvilSampleListPage()) + { + Title = "List Page without items", + Subtitle = "Throws exception on GetItems", + }, + new ListItem(new ExplodeInFiveSeconds(false)) + { + Title = "Page that will throw an exception after loading it", + Subtitle = "Throws exception on GetItems _after_ a ItemsChanged", + }, + new ListItem(new ExplodeInFiveSeconds(true)) + { + Title = "Page that keeps throwing exceptions", + Subtitle = "Will throw every 5 seconds once you open it", + }, + new ListItem(new ExplodeOnPropChange()) + { + Title = "Throw in the middle of a PropChanged", + Subtitle = "Will throw every 5 seconds once you open it", + }, + new ListItem(new SelfImmolateCommand()) + { + Title = "Terminate this extension", + Subtitle = "Will exit this extension (while it's loaded!)", + }, + new ListItem(new NoOpCommand()) + { + Title = "I have lots of nulls", + Subtitle = null, + MoreCommands = null, + Tags = null, + Details = new Details() + { + Title = null, + HeroImage = null, + Metadata = null, + }, + }, + new ListItem(new NoOpCommand()) + { + Title = "I also have nulls", + Subtitle = null, + MoreCommands = null, + Details = new Details() + { + Title = null, + HeroImage = null, + Metadata = [new DetailsElement() { Key = "Oops all nulls", Data = new DetailsTags() { Tags = null } }], + }, + }, + new ListItem(new AnonymousCommand(action: () => + { + ToastStatusMessage toast = new("I should appear immediately"); + toast.Show(); + Thread.Sleep(5000); + }) { Result = CommandResult.KeepOpen() }) + { + Title = "I take just forever to return something", + Subtitle = "The toast should appear immediately.", + MoreCommands = null, + Details = new Details() + { + Body = "This is a test for GH#512. If it doesn't appear immediately, it's likely InvokeCommand is happening on the UI thread.", + }, + }, + + // More edge cases than truly evil + new ListItem( + new ToastCommand("Primary command invoked", MessageState.Info) { Name = "Primary command", Icon = new IconInfo("\uF146") }) // dial 1 + { + Title = "anonymous command test", + Subtitle = "Try pressing Ctrl+1 with me selected", + Icon = new IconInfo("\uE712"), // "More" dots + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Secondary command invoked", MessageState.Warning) { Name = "Secondary command", Icon = new IconInfo("\uF147") }) // dial 2 + { + Title = "I'm a second command", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number1), + }, + new CommandContextItem("nested...") + { + Title = "We can go deeper...", + Icon = new IconInfo("\uF148"), + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number2), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested A invoked") { Name = "Do it", Icon = new IconInfo("A") }) + { + Title = "Nested A", + RequestedShortcut = KeyChordHelpers.FromModifiers(alt: true, vkey: VirtualKey.A), + }, + + new CommandContextItem( + new ToastCommand("Nested B invoked") { Name = "Do it", Icon = new IconInfo("B") }) + { + Title = "Nested B...", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested C invoked") { Name = "Do it" }) + { + Title = "You get it", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + } + ], + }, + ], + } + ], + }, + new ListItem( + new ToastCommand("Primary command invoked", MessageState.Info) { Name = "Primary command", Icon = new IconInfo("\uF146") }) // dial 1 + { + Title = "noop command test", + Subtitle = "Try pressing Ctrl+1 with me selected", + Icon = new IconInfo("\uE712"), // "More" dots + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Secondary command invoked", MessageState.Warning) { Name = "Secondary command", Icon = new IconInfo("\uF147") }) // dial 2 + { + Title = "I'm a second command", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number1), + }, + new CommandContextItem(new NoOpCommand()) + { + Title = "We can go deeper...", + Icon = new IconInfo("\uF148"), + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number2), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested A invoked") { Name = "Do it", Icon = new IconInfo("A") }) + { + Title = "Nested A", + RequestedShortcut = KeyChordHelpers.FromModifiers(alt: true, vkey: VirtualKey.A), + }, + + new CommandContextItem( + new ToastCommand("Nested B invoked") { Name = "Do it", Icon = new IconInfo("B") }) + { + Title = "Nested B...", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested C invoked") { Name = "Do it" }) + { + Title = "You get it", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + } + ], + }, + ], + } + ], + }, + new ListItem( + new ToastCommand("Primary command invoked", MessageState.Info) { Name = "Primary command", Icon = new IconInfo("\uF146") }) // dial 1 + { + Title = "noop secondary command test", + Subtitle = "Try pressing Ctrl+1 with me selected", + Icon = new IconInfo("\uE712"), // "More" dots + MoreCommands = [ + new CommandContextItem(new NoOpCommand()) + { + Title = "We can go deeper...", + Icon = new IconInfo("\uF148"), + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number2), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested A invoked") { Name = "Do it", Icon = new IconInfo("A") }) + { + Title = "Nested A", + RequestedShortcut = KeyChordHelpers.FromModifiers(alt: true, vkey: VirtualKey.A), + }, + + new CommandContextItem( + new ToastCommand("Nested B invoked") { Name = "Do it", Icon = new IconInfo("B") }) + { + Title = "Nested B...", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested C invoked") { Name = "Do it" }) + { + Title = "You get it", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + } + ], + }, + ], + } + ], + }, + + ]; + + public EvilSamplesPage() + { + Name = "Evil Samples"; + Icon = new IconInfo("👿"); // Info + } + + public override IListItem[] GetItems() => _commands; +} + +[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Sample code")] +internal sealed partial class ExplodeOnPropChange : ListPage +{ + private bool _explode; + + public override string Title + { + get => _explode ? Commands[9001].Title : base.Title; + set => base.Title = value; + } + + private IListItem[] Commands => [ + new ListItem(new NoOpCommand()) + { + Title = "This page will explode in five seconds!", + Subtitle = "I'll change my Name, then explode", + }, + ]; + + public ExplodeOnPropChange() + { + Icon = new IconInfo(string.Empty); + Name = "Open"; + } + + public override IListItem[] GetItems() + { + _ = Task.Run(() => + { + Thread.Sleep(1000); + Title = "Ready? 3..."; + Thread.Sleep(1000); + Title = "Ready? 2..."; + Thread.Sleep(1000); + Title = "Ready? 1..."; + Thread.Sleep(1000); + _explode = true; + Title = "boom"; + }); + return Commands; + } +} diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/ExplodeInFiveSeconds.cs b/src/modules/cmdpal/ext/SamplePagesExtension/ExplodeInFiveSeconds.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/ExplodeInFiveSeconds.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/ExplodeInFiveSeconds.cs diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/NativeMethods.txt b/src/modules/cmdpal/ext/SamplePagesExtension/NativeMethods.txt new file mode 100644 index 0000000000..50eba02d20 --- /dev/null +++ b/src/modules/cmdpal/ext/SamplePagesExtension/NativeMethods.txt @@ -0,0 +1,3 @@ +GetForegroundWindow +GetWindowTextLength +GetWindowText \ No newline at end of file diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Package.appxmanifest b/src/modules/cmdpal/ext/SamplePagesExtension/Package.appxmanifest similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Package.appxmanifest rename to src/modules/cmdpal/ext/SamplePagesExtension/Package.appxmanifest diff --git a/src/modules/cmdpal/exts/SamplePagesExtension/Pages/SampleCommentsPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleCommentsPage.cs similarity index 100% rename from src/modules/cmdpal/exts/SamplePagesExtension/Pages/SampleCommentsPage.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleCommentsPage.cs diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleContentPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleContentPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleContentPage.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleContentPage.cs diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleDynamicListPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleDynamicListPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleDynamicListPage.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleDynamicListPage.cs diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs new file mode 100644 index 0000000000..3cf987e417 --- /dev/null +++ b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPage.cs @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; +using Windows.System; +using Windows.Win32; + +namespace SamplePagesExtension; + +internal sealed partial class SampleListPage : ListPage +{ + public SampleListPage() + { + Icon = new IconInfo("\uEA37"); + Name = "Sample List Page"; + } + + public override IListItem[] GetItems() + { + var confirmOnceArgs = new ConfirmationArgs + { + PrimaryCommand = new AnonymousCommand( + () => + { + var t = new ToastStatusMessage("The dialog was confirmed"); + t.Show(); + }) + { + Name = "Confirm", + Result = CommandResult.KeepOpen(), + }, + Title = "You can set a title for the dialog", + Description = "Are you really sure you want to do the thing?", + }; + var confirmTwiceArgs = new ConfirmationArgs + { + PrimaryCommand = new AnonymousCommand(() => { }) + { + Name = "How sure are you?", + Result = CommandResult.Confirm(confirmOnceArgs), + }, + Title = "You can ask twice too", + Description = "You probably don't want to though, that'd be annoying.", + }; + + return [ + new ListItem(new NoOpCommand()) + { + Title = "This is a basic item in the list", + Subtitle = "I don't do anything though", + }, + new ListItem(new SampleListPageWithDetails()) + { + Title = "This item will take you to another page", + Subtitle = "This allows for nested lists of items", + }, + new ListItem(new OpenUrlCommand("https://github.com/microsoft/powertoys")) + { + Title = "Or you can go to links", + Subtitle = "This takes you to the PowerToys repo on GitHub", + }, + new ListItem(new SampleMarkdownPage()) + { + Title = "Items can have tags", + Subtitle = "and I'll take you to a page with markdown content", + Tags = [new Tag("Sample Tag")], + }, + + new ListItem( + new ToastCommand("Primary command invoked", MessageState.Info) { Name = "Primary command", Icon = new IconInfo("\uF146") }) // dial 1 + { + Title = "You can add context menu items too. Press Ctrl+k", + Subtitle = "Try pressing Ctrl+1 with me selected", + Icon = new IconInfo("\uE712"), // "More" dots + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Secondary command invoked", MessageState.Warning) { Name = "Secondary command", Icon = new IconInfo("\uF147") }) // dial 2 + { + Title = "I'm a second command", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number1), + }, + new CommandContextItem( + new ToastCommand("Third command invoked", MessageState.Error) { Name = "Do 3", Icon = new IconInfo("\uF148") }) // dial 3 + { + Title = "We can go deeper...", + Icon = new IconInfo("\uF148"), + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.Number2), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested A invoked") { Name = "Do it", Icon = new IconInfo("A") }) + { + Title = "Nested A", + RequestedShortcut = KeyChordHelpers.FromModifiers(alt: true, vkey: VirtualKey.A), + }, + + new CommandContextItem( + new ToastCommand("Nested B invoked") { Name = "Do it", Icon = new IconInfo("B") }) + { + Title = "Nested B...", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + MoreCommands = [ + new CommandContextItem( + new ToastCommand("Nested C invoked") { Name = "Do it" }) + { + Title = "You get it", + RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, vkey: VirtualKey.B), + } + ], + }, + ], + } + ], + }, + + new ListItem(new SendMessageCommand()) + { + Title = "I send lots of messages", + Subtitle = "Status messages can be used to provide feedback to the user in-app", + }, + new SendSingleMessageItem(), + new ListItem(new IndeterminateProgressMessageCommand()) + { + Title = "Do a thing with a spinner", + Subtitle = "Messages can have progress spinners, to indicate something is happening in the background", + }, + new ListItem( + new AnonymousCommand(() => { }) + { + Result = CommandResult.Confirm(confirmOnceArgs), + }) + { + Title = "Confirm before doing something", + }, + new ListItem( + new AnonymousCommand(() => { }) + { + Result = CommandResult.Confirm(confirmTwiceArgs), + }) + { + Title = "Confirm twice before doing something", + }, + new ListItem( + new AnonymousCommand(() => + { + var fg = PInvoke.GetForegroundWindow(); + var bufferSize = PInvoke.GetWindowTextLength(fg) + 1; + unsafe + { + fixed (char* windowNameChars = new char[bufferSize]) + { + if (PInvoke.GetWindowText(fg, windowNameChars, bufferSize) == 0) + { + var emptyToast = new ToastStatusMessage(new StatusMessage() { Message = "FG Window didn't have a title", State = MessageState.Warning }); + emptyToast.Show(); + } + + var windowName = new string(windowNameChars); + var nameToast = new ToastStatusMessage(new StatusMessage() { Message = $"FG Window is {windowName}", State = MessageState.Success }); + nameToast.Show(); + } + } + }) + { + Result = CommandResult.KeepOpen(), + }) + { + Title = "Get the name of the Foreground window", + }, + ]; + } +} diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleListPageWithDetails.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleListPageWithDetails.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleListPageWithDetails.cs diff --git a/src/modules/cmdpal/exts/SamplePagesExtension/Pages/SampleMarkdownDetails.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownDetails.cs similarity index 100% rename from src/modules/cmdpal/exts/SamplePagesExtension/Pages/SampleMarkdownDetails.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownDetails.cs diff --git a/src/modules/cmdpal/exts/SamplePagesExtension/Pages/SampleMarkdownManyBodies.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownManyBodies.cs similarity index 100% rename from src/modules/cmdpal/exts/SamplePagesExtension/Pages/SampleMarkdownManyBodies.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownManyBodies.cs diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleMarkdownPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleMarkdownPage.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleMarkdownPage.cs diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleSettingsPage.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleSettingsPage.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Pages/SampleSettingsPage.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SampleSettingsPage.cs diff --git a/src/modules/cmdpal/exts/SamplePagesExtension/Pages/SendMessageCommand.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/SendMessageCommand.cs similarity index 100% rename from src/modules/cmdpal/exts/SamplePagesExtension/Pages/SendMessageCommand.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Pages/SendMessageCommand.cs diff --git a/src/modules/cmdpal/ext/SamplePagesExtension/Pages/ToastCommand.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/ToastCommand.cs new file mode 100644 index 0000000000..dfbeb5225a --- /dev/null +++ b/src/modules/cmdpal/ext/SamplePagesExtension/Pages/ToastCommand.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CommandPalette.Extensions; +using Microsoft.CommandPalette.Extensions.Toolkit; + +namespace SamplePagesExtension; + +internal sealed partial class ToastCommand(string message, MessageState state = MessageState.Info) : InvokableCommand +{ + public override ICommandResult Invoke() + { + var t = new ToastStatusMessage(new StatusMessage() + { + Message = message, + State = state, + }); + t.Show(); + + return CommandResult.KeepOpen(); + } +} diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Program.cs b/src/modules/cmdpal/ext/SamplePagesExtension/Program.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Program.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/Program.cs diff --git a/src/modules/cmdpal/exts/SamplePagesExtension/Properties/PublishProfiles/win-arm64.pubxml b/src/modules/cmdpal/ext/SamplePagesExtension/Properties/PublishProfiles/win-arm64.pubxml similarity index 100% rename from src/modules/cmdpal/exts/SamplePagesExtension/Properties/PublishProfiles/win-arm64.pubxml rename to src/modules/cmdpal/ext/SamplePagesExtension/Properties/PublishProfiles/win-arm64.pubxml diff --git a/src/modules/cmdpal/exts/SamplePagesExtension/Properties/PublishProfiles/win-x64.pubxml b/src/modules/cmdpal/ext/SamplePagesExtension/Properties/PublishProfiles/win-x64.pubxml similarity index 100% rename from src/modules/cmdpal/exts/SamplePagesExtension/Properties/PublishProfiles/win-x64.pubxml rename to src/modules/cmdpal/ext/SamplePagesExtension/Properties/PublishProfiles/win-x64.pubxml diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/Properties/launchSettings.json b/src/modules/cmdpal/ext/SamplePagesExtension/Properties/launchSettings.json similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/Properties/launchSettings.json rename to src/modules/cmdpal/ext/SamplePagesExtension/Properties/launchSettings.json diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/SampleExtension.cs b/src/modules/cmdpal/ext/SamplePagesExtension/SampleExtension.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/SampleExtension.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/SampleExtension.cs diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/SamplePagesCommandsProvider.cs b/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs similarity index 100% rename from src/modules/cmdpal/Exts/SamplePagesExtension/SamplePagesCommandsProvider.cs rename to src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesCommandsProvider.cs diff --git a/src/modules/cmdpal/Exts/SamplePagesExtension/SamplePagesExtension.csproj b/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesExtension.csproj similarity index 91% rename from src/modules/cmdpal/Exts/SamplePagesExtension/SamplePagesExtension.csproj rename to src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesExtension.csproj index c00df9b84e..e1a9cc20dd 100644 --- a/src/modules/cmdpal/Exts/SamplePagesExtension/SamplePagesExtension.csproj +++ b/src/modules/cmdpal/ext/SamplePagesExtension/SamplePagesExtension.csproj @@ -33,6 +33,12 @@ + + + all + runtime; build; native; contentfiles; analyzers + + @@ -75,6 +78,8 @@ + +