Compare commits
41 Commits
dev/migrie
...
v0.90.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
229a2894fa | ||
|
|
c893b860cb | ||
|
|
b080a73b86 | ||
|
|
8322a773a1 | ||
|
|
a641b46f57 | ||
|
|
51e9e9d46a | ||
|
|
5157ffc895 | ||
|
|
60bbf070e1 | ||
|
|
d597bd267d | ||
|
|
2623eb10f3 | ||
|
|
8e27940b77 | ||
|
|
c6750d3a62 | ||
|
|
37836c656d | ||
|
|
39e8231831 | ||
|
|
2cb63f5fbe | ||
|
|
e931135d50 | ||
|
|
5b39d1551d | ||
|
|
5e88d47f3d | ||
|
|
aeec3a967f | ||
|
|
be1968aaa5 | ||
|
|
33cc612e40 | ||
|
|
a9a41ca1a2 | ||
|
|
4e7bd34c4d | ||
|
|
43783d2cff | ||
|
|
79bd825f91 | ||
|
|
69c2e9c568 | ||
|
|
df3e3414d2 | ||
|
|
7368458a72 | ||
|
|
4d7691a56f | ||
|
|
2f9fea2287 | ||
|
|
29551898ca | ||
|
|
048b07c1ce | ||
|
|
7575c040f8 | ||
|
|
e52dd68fe4 | ||
|
|
3e9a6a1e64 | ||
|
|
14919dff10 | ||
|
|
57cbcc2c3e | ||
|
|
5d03667bcf | ||
|
|
2b4d13ccb9 | ||
|
|
665e957cde | ||
|
|
dadd306555 |
1
.github/actions/spell-check/allow/names.txt
vendored
@@ -154,6 +154,7 @@ Santossio
|
||||
Schoen
|
||||
Sekan
|
||||
Seraphima
|
||||
Shmuelie
|
||||
skttl
|
||||
somil
|
||||
Soref
|
||||
|
||||
@@ -31,22 +31,22 @@
|
||||
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
|
||||
<PackageVersion Include="MessagePack" Version="2.5.187" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
||||
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.120-preview" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
|
||||
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
|
||||
<!--
|
||||
@@ -72,26 +72,26 @@
|
||||
<PackageVersion Include="StreamJsonRpc" Version="2.19.27" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.CodeDom" Version="9.0.3" />
|
||||
<PackageVersion Include="System.CodeDom" Version="9.0.4" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="9.0.3" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="9.0.4" />
|
||||
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
|
||||
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
|
||||
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.4" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.4" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="21.0.29" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.3" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.4" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
<PackageVersion Include="UnitsNet" Version="5.56.0" />
|
||||
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
|
||||
|
||||
42
NOTICE.md
@@ -1420,22 +1420,22 @@ SOFTWARE.
|
||||
- Mages 3.0.0
|
||||
- Markdig.Signed 0.34.0
|
||||
- MessagePack 2.5.187
|
||||
- Microsoft.Bcl.AsyncInterfaces 9.0.3
|
||||
- Microsoft.Bcl.AsyncInterfaces 9.0.4
|
||||
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
|
||||
- Microsoft.Data.Sqlite 9.0.3
|
||||
- Microsoft.Data.Sqlite 9.0.4
|
||||
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
|
||||
- Microsoft.DotNet.ILCompiler (A)
|
||||
- Microsoft.Extensions.DependencyInjection 9.0.3
|
||||
- Microsoft.Extensions.Hosting 9.0.3
|
||||
- Microsoft.Extensions.Hosting.WindowsServices 9.0.3
|
||||
- Microsoft.Extensions.Logging 9.0.3
|
||||
- Microsoft.Extensions.Logging.Abstractions 9.0.3
|
||||
- Microsoft.Extensions.DependencyInjection 9.0.4
|
||||
- Microsoft.Extensions.Hosting 9.0.4
|
||||
- Microsoft.Extensions.Hosting.WindowsServices 9.0.4
|
||||
- Microsoft.Extensions.Logging 9.0.4
|
||||
- Microsoft.Extensions.Logging.Abstractions 9.0.4
|
||||
- Microsoft.NET.ILLink.Tasks (A)
|
||||
- Microsoft.SemanticKernel 1.15.0
|
||||
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
|
||||
- Microsoft.Web.WebView2 1.0.2903.40
|
||||
- Microsoft.Win32.SystemEvents 9.0.3
|
||||
- Microsoft.Windows.Compatibility 9.0.3
|
||||
- Microsoft.Win32.SystemEvents 9.0.4
|
||||
- Microsoft.Windows.Compatibility 9.0.4
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.2.0
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
|
||||
@@ -1454,23 +1454,23 @@ SOFTWARE.
|
||||
- SharpCompress 0.37.2
|
||||
- StreamJsonRpc 2.19.27
|
||||
- StyleCop.Analyzers 1.2.0-beta.556
|
||||
- System.CodeDom 9.0.3
|
||||
- System.CodeDom 9.0.4
|
||||
- System.CommandLine 2.0.0-beta4.22272.1
|
||||
- System.ComponentModel.Composition 9.0.3
|
||||
- System.Configuration.ConfigurationManager 9.0.3
|
||||
- System.Data.OleDb 9.0.3
|
||||
- System.ComponentModel.Composition 9.0.4
|
||||
- System.Configuration.ConfigurationManager 9.0.4
|
||||
- System.Data.OleDb 9.0.4
|
||||
- System.Data.SqlClient 4.8.6
|
||||
- System.Diagnostics.EventLog 9.0.3
|
||||
- System.Diagnostics.PerformanceCounter 9.0.3
|
||||
- System.Drawing.Common 9.0.3
|
||||
- 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.Management 9.0.3
|
||||
- System.Management 9.0.4
|
||||
- System.Reactive 6.0.1
|
||||
- System.Runtime.Caching 9.0.3
|
||||
- System.ServiceProcess.ServiceController 9.0.3
|
||||
- System.Text.Encoding.CodePages 9.0.3
|
||||
- System.Text.Json 9.0.3
|
||||
- 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
|
||||
- UnicodeInformation 2.6.0
|
||||
- UnitsNet 5.56.0
|
||||
- UTF.Unknown 2.5.1
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\x64\Microsoft.WindowsAppRuntime.1.6.msix" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?else ?>
|
||||
@@ -50,7 +49,6 @@
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\arm64\Microsoft.WindowsAppRuntime.1.6.msix" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?endif ?>
|
||||
|
||||
@@ -179,6 +179,9 @@
|
||||
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles">
|
||||
Installed AND (REMOVE="ALL")
|
||||
</Custom>
|
||||
<Custom Action="UnRegisterCmdPalPackage" Before="RemoveFiles">
|
||||
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
|
||||
</Custom>
|
||||
<Custom Action="UnsetAdvancedPasteAPIKey" Before="RemoveFiles">
|
||||
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
|
||||
</Custom>
|
||||
@@ -439,6 +442,14 @@
|
||||
DllEntry="UnRegisterContextMenuPackagesCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="UnRegisterCmdPalPackage"
|
||||
Return="ignore"
|
||||
Impersonate="yes"
|
||||
Execute="deferred"
|
||||
BinaryKey="PTCustomActions"
|
||||
DllEntry="UnRegisterCmdPalPackageCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="CheckGPO"
|
||||
Return="check"
|
||||
Impersonate="yes"
|
||||
|
||||
@@ -1081,6 +1081,41 @@ UINT __stdcall InstallCmdPalPackageCA(MSIHANDLE hInstall)
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
UINT __stdcall UnRegisterCmdPalPackageCA(MSIHANDLE hInstall)
|
||||
{
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Management::Deployment;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
UINT er = ERROR_SUCCESS;
|
||||
|
||||
hr = WcaInitialize(hInstall, "UnRegisterCmdPalPackageCA");
|
||||
|
||||
try
|
||||
{
|
||||
// Packages to unregister
|
||||
std::wstring packageToRemoveDisplayName {L"Microsoft.CommandPalette"};
|
||||
|
||||
if (!package::UnRegisterPackage(packageToRemoveDisplayName))
|
||||
{
|
||||
Logger::error(L"Failed to unregister package: " + packageToRemoveDisplayName);
|
||||
er = ERROR_INSTALL_FAILURE;
|
||||
}
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::string errorMessage{"Exception thrown while trying to unregister the CmdPal package: "};
|
||||
errorMessage += e.what();
|
||||
Logger::error(errorMessage);
|
||||
|
||||
er = ERROR_INSTALL_FAILURE;
|
||||
}
|
||||
|
||||
er = er == ERROR_SUCCESS ? (SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE) : er;
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
|
||||
UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
|
||||
{
|
||||
using namespace winrt::Windows::Foundation;
|
||||
@@ -1094,7 +1129,7 @@ UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
|
||||
try
|
||||
{
|
||||
// Packages to unregister
|
||||
const std::vector<std::wstring> packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}, {L"Microsoft.CommandPalette"}};
|
||||
const std::vector<std::wstring> packagesToRemoveDisplayName{{L"PowerRenameContextMenu"}, {L"ImageResizerContextMenu"}, {L"FileLocksmithContextMenu"}, {L"NewPlusContextMenu"}};
|
||||
|
||||
for (auto const &package : packagesToRemoveDisplayName)
|
||||
{
|
||||
|
||||
@@ -20,6 +20,7 @@ EXPORTS
|
||||
InstallDSCModuleCA
|
||||
InstallCmdPalPackageCA
|
||||
UnApplyModulesRegistryChangeSetsCA
|
||||
UnRegisterCmdPalPackageCA
|
||||
UnRegisterContextMenuPackagesCA
|
||||
UninstallEmbeddedMSIXCA
|
||||
UninstallDSCModuleCA
|
||||
|
||||
@@ -130,6 +130,7 @@ namespace CommonSharedConstants
|
||||
|
||||
// used from quick access window
|
||||
const wchar_t CMDPAL_SHOW_EVENT[] = L"Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a";
|
||||
const wchar_t CMDPAL_EXIT_EVENT[] = L"Local\\PowerToysCmdPal-ExitEvent-eb73f6be-3f22-4b36-aee3-62924ba40bfd";
|
||||
|
||||
// Max DWORD for key code to disable keys.
|
||||
const DWORD VK_DISABLED = 0x100;
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <appxpackaging.h>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <Shlwapi.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
@@ -15,11 +18,12 @@
|
||||
#include "../logger/logger.h"
|
||||
#include "../version/version.h"
|
||||
|
||||
namespace package {
|
||||
|
||||
namespace package
|
||||
{
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::Management::Deployment;
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
inline BOOL IsWin11OrGreater()
|
||||
{
|
||||
@@ -46,6 +50,118 @@ namespace package {
|
||||
dwlConditionMask);
|
||||
}
|
||||
|
||||
struct PACKAGE_VERSION
|
||||
{
|
||||
UINT16 Major;
|
||||
UINT16 Minor;
|
||||
UINT16 Build;
|
||||
UINT16 Revision;
|
||||
};
|
||||
|
||||
class ComInitializer
|
||||
{
|
||||
public:
|
||||
explicit ComInitializer(DWORD coInitFlags = COINIT_MULTITHREADED) :
|
||||
_initialized(false)
|
||||
{
|
||||
const HRESULT hr = CoInitializeEx(nullptr, coInitFlags);
|
||||
_initialized = SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
~ComInitializer()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool Succeeded() const { return _initialized; }
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
};
|
||||
|
||||
inline bool GetPackageNameAndVersionFromAppx(
|
||||
const std::wstring& appxPath,
|
||||
std::wstring& outName,
|
||||
PACKAGE_VERSION& outVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
ComInitializer comInit;
|
||||
if (!comInit.Succeeded())
|
||||
{
|
||||
Logger::error(L"COM initialization failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ComPtr<IAppxFactory> factory;
|
||||
ComPtr<IStream> stream;
|
||||
ComPtr<IAppxPackageReader> reader;
|
||||
ComPtr<IAppxManifestReader> manifest;
|
||||
ComPtr<IAppxManifestPackageId> packageId;
|
||||
|
||||
HRESULT hr = CoCreateInstance(__uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = SHCreateStreamOnFileEx(appxPath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = factory->CreatePackageReader(stream.Get(), &reader);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = reader->GetManifest(&manifest);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = manifest->GetPackageId(&packageId);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
LPWSTR name = nullptr;
|
||||
hr = packageId->GetName(&name);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
UINT64 version = 0;
|
||||
hr = packageId->GetVersion(&version);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
outName = std::wstring(name);
|
||||
CoTaskMemFree(name);
|
||||
|
||||
outVersion.Major = static_cast<UINT16>((version >> 48) & 0xFFFF);
|
||||
outVersion.Minor = static_cast<UINT16>((version >> 32) & 0xFFFF);
|
||||
outVersion.Build = static_cast<UINT16>((version >> 16) & 0xFFFF);
|
||||
outVersion.Revision = static_cast<UINT16>(version & 0xFFFF);
|
||||
|
||||
Logger::info(L"Package name: {}, version: {}.{}.{}.{}, appxPath: {}",
|
||||
outName,
|
||||
outVersion.Major,
|
||||
outVersion.Minor,
|
||||
outVersion.Build,
|
||||
outVersion.Revision,
|
||||
appxPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(L"Standard exception: {}", winrt::to_hstring(ex.what()));
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"Unknown or non-standard exception occurred.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::optional<Package> GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion)
|
||||
{
|
||||
PackageManager packageManager;
|
||||
@@ -129,7 +245,7 @@ namespace package {
|
||||
try
|
||||
{
|
||||
PackageManager packageManager;
|
||||
const static auto packages = packageManager.FindPackages();
|
||||
const static auto packages = packageManager.FindPackagesForUser({});
|
||||
|
||||
for (auto const& package : packages)
|
||||
{
|
||||
@@ -229,6 +345,59 @@ namespace package {
|
||||
return matchedFiles;
|
||||
}
|
||||
|
||||
inline bool IsPackageSatisfied(const std::wstring& appxPath)
|
||||
{
|
||||
std::wstring targetName;
|
||||
PACKAGE_VERSION targetVersion{};
|
||||
|
||||
if (!GetPackageNameAndVersionFromAppx(appxPath, targetName, targetVersion))
|
||||
{
|
||||
Logger::error(L"Failed to get package name and version from appx: " + appxPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
PackageManager pm;
|
||||
|
||||
for (const auto& package : pm.FindPackagesForUser({}))
|
||||
{
|
||||
const auto& id = package.Id();
|
||||
if (std::wstring(id.Name()) == targetName)
|
||||
{
|
||||
const auto& version = id.Version();
|
||||
|
||||
if (version.Major > targetVersion.Major ||
|
||||
(version.Major == targetVersion.Major && version.Minor > targetVersion.Minor) ||
|
||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build > targetVersion.Build) ||
|
||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build == targetVersion.Build && version.Revision >= targetVersion.Revision))
|
||||
{
|
||||
Logger::info(
|
||||
L"Package {} is already satisfied with version {}.{}.{}.{}; target version {}.{}.{}.{}; appxPath: {}",
|
||||
id.Name(),
|
||||
version.Major,
|
||||
version.Minor,
|
||||
version.Build,
|
||||
version.Revision,
|
||||
targetVersion.Major,
|
||||
targetVersion.Minor,
|
||||
targetVersion.Build,
|
||||
targetVersion.Revision,
|
||||
appxPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info(
|
||||
L"Package {} is not satisfied. Target version: {}.{}.{}.{}; appxPath: {}",
|
||||
targetName,
|
||||
targetVersion.Major,
|
||||
targetVersion.Minor,
|
||||
targetVersion.Build,
|
||||
targetVersion.Revision,
|
||||
appxPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool RegisterPackage(std::wstring pkgPath, std::vector<std::wstring> dependencies)
|
||||
{
|
||||
try
|
||||
@@ -238,7 +407,7 @@ namespace package {
|
||||
PackageManager packageManager;
|
||||
|
||||
// Declare use of an external location
|
||||
DeploymentOptions options = DeploymentOptions::ForceApplicationShutdown;
|
||||
DeploymentOptions options = DeploymentOptions::ForceTargetApplicationShutdown;
|
||||
|
||||
Collections::IVector<Uri> uris = winrt::single_threaded_vector<Uri>();
|
||||
if (!dependencies.empty())
|
||||
@@ -247,7 +416,14 @@ namespace package {
|
||||
{
|
||||
try
|
||||
{
|
||||
uris.Append(Uri(dependency));
|
||||
if (IsPackageSatisfied(dependency))
|
||||
{
|
||||
Logger::info(L"Dependency already satisfied: {}", dependency);
|
||||
}
|
||||
else
|
||||
{
|
||||
uris.Append(Uri(dependency));
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error& ex)
|
||||
{
|
||||
@@ -282,7 +458,6 @@ namespace package {
|
||||
{
|
||||
Logger::debug(L"Register {} package started.", pkgPath);
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
@@ -293,4 +468,4 @@ namespace package {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,7 +684,7 @@
|
||||
</policy>
|
||||
<policy name="NewPlusReplaceVariablesInTemplateFilenames" class="Both" displayName="$(string.NewPlusReplaceVariablesInTemplateFilenames)" explainText="$(string.NewPlusReplaceVariablesInTemplateFilenamesDescription)" key="Software\Policies\PowerToys" valueName="NewPlusReplaceVariablesInTemplateFilenames">
|
||||
<parentCategory ref="NewPlus" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_89_0" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_90_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
|
||||
@@ -299,7 +299,8 @@ If you don't configure this policy, the user will be able to control the setting
|
||||
<string id="MwbPolicyDefinedIpMappingRules">Predefined IP Address mapping rules</string>
|
||||
<string id="NewPlusHideTemplateFilenameExtension">Hide template filename extension</string>
|
||||
<string id="AllowDiagnosticData">Allow sending diagnostic data</string>
|
||||
<string id="ConfigureRunAtStartup">Configure the run at startup setting</string>
|
||||
<string id="ConfigureRunAtStartup">Configure the run at startup setting</string>
|
||||
<string id="NewPlusReplaceVariablesInTemplateFilenames">Replace variables in template filenames</string>
|
||||
</stringTable>
|
||||
|
||||
<presentationTable>
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/utils/package.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/interop/shared_constants.h>
|
||||
#include <Psapi.h>
|
||||
#include <TlHelp32.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
HINSTANCE g_hInst_cmdPal = 0;
|
||||
|
||||
@@ -42,28 +44,26 @@ private:
|
||||
//contains the non localized key of the powertoy
|
||||
std::wstring app_key;
|
||||
|
||||
void LaunchApp()
|
||||
HANDLE m_hTerminateEvent;
|
||||
|
||||
void LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated)
|
||||
{
|
||||
auto package = package::GetRegisteredPackage(L"Microsoft.CommandPalette", false);
|
||||
std::wstring dir = std::filesystem::path(appPath).parent_path();
|
||||
|
||||
if (package.has_value())
|
||||
{
|
||||
auto getAppListEntriesOperation = package->GetAppListEntriesAsync();
|
||||
auto appEntries = getAppListEntriesOperation.get();
|
||||
SHELLEXECUTEINFO sei = { 0 };
|
||||
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
||||
sei.hwnd = nullptr;
|
||||
sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE;
|
||||
sei.lpVerb = elevated ? L"runas" : L"open";
|
||||
sei.lpFile = appPath.c_str();
|
||||
sei.lpParameters = commandLineArgs.c_str();
|
||||
sei.lpDirectory = dir.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
|
||||
if (appEntries.Size() > 0)
|
||||
{
|
||||
winrt::Windows::Foundation::IAsyncOperation<bool> launchOperation = appEntries.GetAt(0).LaunchAsync();
|
||||
launchOperation.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::error(L"No app entries found for the package.");
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!ShellExecuteEx(&sei))
|
||||
{
|
||||
Logger::error(L"CmdPal package is not registered.");
|
||||
std::wstring error = get_last_error_or_default(GetLastError());
|
||||
Logger::error(L"Failed to launch process. {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,11 @@ private:
|
||||
|
||||
if (hProcess != NULL)
|
||||
{
|
||||
SetEvent(m_hTerminateEvent);
|
||||
|
||||
// Wait for 1.5 seconds for the process to end correctly and stop etw tracer
|
||||
WaitForSingleObject(hProcess, 1500);
|
||||
|
||||
TerminateProcess(hProcess, 0);
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
@@ -122,6 +127,8 @@ public:
|
||||
app_name = L"CmdPal";
|
||||
app_key = L"CmdPal";
|
||||
LoggerHelpers::init_logger(app_key, L"ModuleInterface", "CmdPal");
|
||||
|
||||
m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::CMDPAL_EXIT_EVENT);
|
||||
}
|
||||
|
||||
~CmdPal()
|
||||
@@ -235,7 +242,11 @@ public:
|
||||
Logger::error(errorMessage);
|
||||
}
|
||||
|
||||
LaunchApp();
|
||||
#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
|
||||
}
|
||||
|
||||
virtual void disable()
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.250205002" />
|
||||
<PackageVersion Include="Shmuelie.WinRTServer" Version="2.1.1" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -2,28 +2,32 @@
|
||||
// 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 Shmuelie.WinRTServer;
|
||||
using Shmuelie.WinRTServer.CsWinRT;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TemplateCmdPalExtension;
|
||||
|
||||
public class Program
|
||||
{
|
||||
[MTAThread]
|
||||
public static void Main(string[] args)
|
||||
public static async Task Main(string[] args)
|
||||
{
|
||||
if (args.Length > 0 && args[0] == "-RegisterProcessAsComServer")
|
||||
{
|
||||
using ExtensionServer server = new();
|
||||
var extensionDisposedEvent = new ManualResetEvent(false);
|
||||
var extensionInstance = new TemplateCmdPalExtension(extensionDisposedEvent);
|
||||
|
||||
await using global::Shmuelie.WinRTServer.ComServer server = new();
|
||||
ManualResetEvent extensionDisposedEvent = new(false);
|
||||
|
||||
// We are instantiating an extension instance once above, and returning it every time the callback in RegisterExtension below is called.
|
||||
// This makes sure that only one instance of SampleExtension is alive, which is returned every time the host asks for the IExtension object.
|
||||
// If you want to instantiate a new instance each time the host asks, create the new instance inside the delegate.
|
||||
server.RegisterExtension(() => extensionInstance);
|
||||
|
||||
TemplateCmdPalExtension extensionInstance = new(extensionDisposedEvent);
|
||||
server.RegisterClass<TemplateCmdPalExtension, IExtension>(() => extensionInstance);
|
||||
server.Start();
|
||||
|
||||
// This will make the main thread wait until the event is signalled by the extension class.
|
||||
// Since we have single instance of the extension object, we exit as soon as it is disposed.
|
||||
extensionDisposedEvent.WaitOne();
|
||||
|
||||
@@ -9,9 +9,7 @@ using Microsoft.CommandPalette.Extensions;
|
||||
|
||||
namespace TemplateCmdPalExtension;
|
||||
|
||||
[ComVisible(true)]
|
||||
[Guid("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF")]
|
||||
[ComDefaultInterface(typeof(IExtension))]
|
||||
public sealed partial class TemplateCmdPalExtension : IExtension, IDisposable
|
||||
{
|
||||
private readonly ManualResetEvent _extensionDisposedEvent;
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="Microsoft.Web.WebView2" />
|
||||
<PackageReference Include="System.Text.Json" />
|
||||
<PackageReference Include="Shmuelie.WinRTServer" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -22,7 +22,11 @@ public partial class AllAppsCommandProvider : CommandProvider
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\AllApps.svg");
|
||||
Settings = AllAppsSettings.Instance.Settings;
|
||||
|
||||
_listItem = new(Page) { Subtitle = Resources.search_installed_apps };
|
||||
_listItem = new(Page)
|
||||
{
|
||||
Subtitle = Resources.search_installed_apps,
|
||||
MoreCommands = [new CommandContextItem(AllAppsSettings.Instance.Settings.SettingsPage)],
|
||||
};
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands() => [_listItem];
|
||||
|
||||
@@ -25,7 +25,7 @@ internal sealed partial class AddBookmarkPage : ContentPage
|
||||
{
|
||||
var name = bookmark?.Name ?? string.Empty;
|
||||
var url = bookmark?.Bookmark ?? string.Empty;
|
||||
Icon = new IconInfo("\ued0e");
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\Bookmark.svg");
|
||||
var isAdd = string.IsNullOrEmpty(name) && string.IsNullOrEmpty(url);
|
||||
Title = isAdd ? Resources.bookmarks_add_title : Resources.bookmarks_edit_name;
|
||||
Name = isAdd ? Resources.bookmarks_add_name : Resources.bookmarks_edit_name;
|
||||
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.84868 15.8874C3.49425 16.1693 3 15.8893 3 15.4067V3.56616C3 2.14891 4.03946 1 5.3217 1H10.6783C11.9605 1 13 2.14891 13 3.56616V15.4067C13 15.8893 12.5057 16.1693 12.1513 15.8874L7.99999 12.5863L3.84868 15.8874Z" fill="url(#paint0_linear_1900_17905)"/>
|
||||
<path d="M9.17749 1.02412C9.86491 0.564808 10.6731 0.31963 11.4998 0.319592C12.6082 0.320929 13.6707 0.761812 14.4545 1.54554C15.2382 2.32931 15.6791 3.39196 15.6804 4.50037C15.6803 5.32705 15.4352 6.13515 14.9759 6.82251C14.5165 7.50998 13.8636 8.04578 13.0998 8.36219C12.3359 8.6786 11.4954 8.76138 10.6844 8.60008C9.87352 8.43878 9.12865 8.04063 8.54401 7.45599C7.95937 6.87135 7.56122 6.12647 7.39992 5.31556C7.23862 4.50464 7.3214 3.6641 7.63781 2.90023C7.95421 2.13636 8.49003 1.48347 9.17749 1.02412Z" fill="url(#paint1_linear_1900_17905)" stroke="url(#paint2_linear_1900_17905)" stroke-width="0.639184"/>
|
||||
<rect x="8" y="4" width="7" height="1" rx="0.5" fill="#0C58A2"/>
|
||||
<rect x="11" y="8" width="7" height="1" rx="0.5" transform="rotate(-90 11 8)" fill="#0C58A2"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1900_17905" x1="4.2484" y1="-0.184382" x2="12.4694" y2="16.9798" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#0A7ACC"/>
|
||||
<stop offset="1" stop-color="#0E5497"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1900_17905" x1="13.7504" y1="8.39775" x2="9.24963" y2="0.60225" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FCFCFC"/>
|
||||
<stop offset="1" stop-color="#E7E7E7"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_1900_17905" x1="9.75" y1="5.63474e-08" x2="12.8347" y2="9.07039" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#173A73" stop-opacity="0.1"/>
|
||||
<stop offset="1" stop-color="#173A73" stop-opacity="0.25"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -9,6 +9,9 @@
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>Microsoft.CmdPal.Ext.Bookmarks.pri</ProjectPriFileName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Bookmark.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
<ProjectReference Include="..\..\Exts\Microsoft.CmdPal.Ext.Indexer\Microsoft.CmdPal.Ext.Indexer.csproj" />
|
||||
@@ -21,6 +24,15 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\Bookmark.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Bookmark.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
|
||||
@@ -141,6 +141,7 @@ public sealed partial class SaveCommand : InvokableCommand
|
||||
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)
|
||||
@@ -149,7 +150,7 @@ internal sealed partial class FallbackCalculatorItem : FallbackCommandItem
|
||||
_copyCommand.Name = string.Empty;
|
||||
Title = string.Empty;
|
||||
Subtitle = Resources.calculator_placeholder_text;
|
||||
Icon = new IconInfo("\ue8ef"); // Calculator
|
||||
Icon = _cachedIcon;
|
||||
}
|
||||
|
||||
public override void UpdateQuery(string query)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using Microsoft.CmdPal.Ext.Indexer.Data;
|
||||
using Microsoft.CmdPal.Ext.Indexer.Properties;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.Indexer.Commands;
|
||||
|
||||
@@ -24,10 +23,7 @@ internal sealed partial class CopyPathCommand : InvokableCommand
|
||||
{
|
||||
try
|
||||
{
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(_item.FullPath);
|
||||
Clipboard.SetContent(dataPackage);
|
||||
Clipboard.Flush();
|
||||
ClipboardHelper.SetText(_item.FullPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -53,7 +53,11 @@ internal static class RegistryHelper
|
||||
}
|
||||
|
||||
var baseKey = query.Split('\\').FirstOrDefault() ?? string.Empty;
|
||||
var subKey = query.Replace(baseKey, string.Empty, StringComparison.InvariantCultureIgnoreCase).TrimStart('\\');
|
||||
var subKey = string.Empty;
|
||||
if (!string.IsNullOrEmpty(baseKey))
|
||||
{
|
||||
subKey = query.Replace(baseKey, string.Empty, StringComparison.InvariantCultureIgnoreCase).TrimStart('\\');
|
||||
}
|
||||
|
||||
var baseKeyResult = _baseKeys
|
||||
.Where(found => found.Key.StartsWith(baseKey, StringComparison.InvariantCultureIgnoreCase))
|
||||
|
||||
@@ -177,6 +177,15 @@ namespace Microsoft.CmdPal.Ext.Registry.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Windows Registry.
|
||||
/// </summary>
|
||||
internal static string RegistryProvider_DisplayName {
|
||||
get {
|
||||
return ResourceManager.GetString("RegistryProvider_DisplayName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Registry value.
|
||||
/// </summary>
|
||||
|
||||
@@ -185,4 +185,7 @@
|
||||
<data name="Registry_Key_Not_Found" xml:space="preserve">
|
||||
<value>Registry key not found</value>
|
||||
</data>
|
||||
<data name="RegistryProvider_DisplayName" xml:space="preserve">
|
||||
<value>Windows Registry</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.Registry.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -12,7 +13,7 @@ public partial class RegistryCommandsProvider : CommandProvider
|
||||
public RegistryCommandsProvider()
|
||||
{
|
||||
Id = "Windows.Registry";
|
||||
DisplayName = $"Windows Registry";
|
||||
DisplayName = Resources.RegistryProvider_DisplayName;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\Registry.svg");
|
||||
}
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,66 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1887_18142)">
|
||||
<rect y="4.5" width="3.75" height="1.25" fill="url(#paint0_linear_1887_18142)"/>
|
||||
<rect y="6.75" width="3.75" height="1.25" fill="url(#paint1_linear_1887_18142)"/>
|
||||
<rect y="9" width="3.75" height="1.25" fill="url(#paint2_linear_1887_18142)"/>
|
||||
<rect y="11.25" width="3.75" height="1.25" fill="url(#paint3_linear_1887_18142)"/>
|
||||
<path d="M2.62973 4.60404C2.69939 4.25294 3.00745 4 3.36539 4H14.1219C14.5947 4 14.9495 4.43219 14.8575 4.89596L13.3694 12.396C13.2998 12.7471 12.9917 13 12.6338 13H1.8773C1.40448 13 1.04962 12.5678 1.14164 12.104L2.62973 4.60404Z" fill="url(#paint4_linear_1887_18142)"/>
|
||||
<path opacity="0.8" d="M3.31 5.05188C3.34489 4.87639 3.49889 4.75 3.67781 4.75H13.6685C13.905 4.75 14.0824 4.9662 14.0363 5.19812L12.6943 11.9481C12.6594 12.1236 12.5054 12.25 12.3265 12.25H2.3358C2.09933 12.25 1.92189 12.0338 1.968 11.8019L3.31 5.05188Z" fill="url(#paint5_linear_1887_18142)"/>
|
||||
<path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M3.6776 4.75C3.49869 4.75 3.34469 4.87639 3.3098 5.05188L1.9678 11.8019C1.96623 11.8097 1.96492 11.8176 1.96387 11.8254L3.26085 5.30188C3.29574 5.12639 3.44974 5 3.62865 5H13.6194C13.8478 5 14.0212 5.20177 13.9911 5.42457L14.0361 5.19812C14.0822 4.9662 13.9048 4.75 13.6683 4.75H3.6776Z" fill="url(#paint6_linear_1887_18142)"/>
|
||||
<g filter="url(#filter0_dd_1887_18142)">
|
||||
<path d="M4.47597 6.85475C4.54535 6.50332 4.85356 6.25 5.21178 6.25L11.3792 6.25C11.8517 6.25 12.2065 6.68168 12.115 7.14525L11.5227 10.1453C11.4534 10.4967 11.1452 10.75 10.7869 10.75H4.61956C4.14704 10.75 3.79225 10.3183 3.88376 9.85475L4.47597 6.85475Z" fill="url(#paint7_linear_1887_18142)"/>
|
||||
</g>
|
||||
<path d="M5.14006 7.30488C5.17373 7.12799 5.32838 7 5.50844 7H10.912C11.1472 7 11.3243 7.21405 11.2804 7.44512L11.0424 8.69512C11.0087 8.87201 10.8541 9 10.674 9H5.2705C5.03528 9 4.85813 8.78595 4.90211 8.55488L5.14006 7.30488Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_dd_1887_18142" x="3.36914" y="5.75" width="9.26074" height="5.5" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="0.0833333"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1887_18142"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feGaussianBlur stdDeviation="0.25"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow_1887_18142" result="effect2_dropShadow_1887_18142"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1887_18142" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_1887_18142" x1="3.75" y1="5.39286" x2="3.14495e-08" y2="5.39286" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#28AFEA" stop-opacity="0.8"/>
|
||||
<stop offset="1" stop-color="#28AFEA" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1887_18142" x1="3.75" y1="7.64286" x2="3.14495e-08" y2="7.64286" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#28AFEA" stop-opacity="0.8"/>
|
||||
<stop offset="1" stop-color="#28AFEA" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_1887_18142" x1="3.75" y1="9.89286" x2="3.14495e-08" y2="9.89286" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#28AFEA" stop-opacity="0.8"/>
|
||||
<stop offset="1" stop-color="#28AFEA" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_1887_18142" x1="3.75" y1="12.1429" x2="3.14495e-08" y2="12.1429" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#28AFEA" stop-opacity="0.8"/>
|
||||
<stop offset="1" stop-color="#28AFEA" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_1887_18142" x1="5.4463" y1="4" x2="7.3315" y2="13.4932" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3CCBF4"/>
|
||||
<stop offset="1" stop-color="#1493DF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_1887_18142" x1="4.41381" y1="4.75" x2="7.74261" y2="12.255" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#E8F4FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_1887_18142" x1="4.4136" y1="4.75" x2="7.7424" y2="12.255" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#E8F4FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_1887_18142" x1="6.44166" y1="6.25" x2="7.22393" y2="11.0565" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3CCBF4"/>
|
||||
<stop offset="1" stop-color="#1493DF"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_1887_18142">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -7,6 +7,9 @@
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Run.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -17,17 +20,18 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\Run.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Run.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\Run@2x.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -15,7 +15,7 @@ internal sealed partial class ShellListPage : DynamicListPage
|
||||
|
||||
public ShellListPage(SettingsManager settingsManager)
|
||||
{
|
||||
Icon = new IconInfo("\uE756");
|
||||
Icon = Icons.RunV2;
|
||||
Id = "com.microsoft.cmdpal.shell";
|
||||
Name = Resources.cmd_plugin_name;
|
||||
PlaceholderText = Resources.list_placeholder_text;
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Microsoft.CmdPal.Ext.Shell.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Run as administrator (Ctrl+Shift+Enter).
|
||||
/// Looks up a localized string similar to Run as administrator.
|
||||
/// </summary>
|
||||
public static string cmd_run_as_administrator {
|
||||
get {
|
||||
@@ -124,7 +124,7 @@ namespace Microsoft.CmdPal.Ext.Shell.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Run as different user (Ctrl+Shift+U).
|
||||
/// Looks up a localized string similar to Run as different user.
|
||||
/// </summary>
|
||||
public static string cmd_run_as_user {
|
||||
get {
|
||||
|
||||
@@ -130,7 +130,7 @@
|
||||
<value>execute command through command shell</value>
|
||||
</data>
|
||||
<data name="cmd_run_as_administrator" xml:space="preserve">
|
||||
<value>Run as administrator (Ctrl+Shift+Enter)</value>
|
||||
<value>Run as administrator</value>
|
||||
</data>
|
||||
<data name="cmd_command_failed" xml:space="preserve">
|
||||
<value>Error running the command</value>
|
||||
@@ -139,7 +139,7 @@
|
||||
<value>Command not found</value>
|
||||
</data>
|
||||
<data name="cmd_run_as_user" xml:space="preserve">
|
||||
<value>Run as different user (Ctrl+Shift+U)</value>
|
||||
<value>Run as different user</value>
|
||||
</data>
|
||||
<data name="leave_shell_open" xml:space="preserve">
|
||||
<value>Keep shell open</value>
|
||||
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="5.5" y="0.5" width="5" height="6" rx="2.5" stroke="#666666"/>
|
||||
<rect x="2" y="4" width="12" height="12" rx="3" fill="url(#paint0_linear_1909_18087)"/>
|
||||
<circle cx="8" cy="10" r="2" fill="url(#paint1_linear_1909_18087)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1909_18087" x1="2.5" y1="4" x2="12.5" y2="16" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#E8EDF2"/>
|
||||
<stop offset="1" stop-color="#C0C1C1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1909_18087" x1="6.5" y1="8" x2="9.5" y2="12.5" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#666666"/>
|
||||
<stop offset="1" stop-color="#666666"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 745 B |
|
Before Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 597 B |
|
Before Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 726 B |
|
Before Width: | Height: | Size: 679 B |
@@ -33,11 +33,11 @@ internal static class Commands
|
||||
/// Returns a list with all system command results
|
||||
/// </summary>
|
||||
/// <param name="isUefi">Value indicating if the system is booted in uefi mode</param>
|
||||
/// <param name="splitRecycleBinResults">Value indicating if we should show two results for Recycle Bin.</param>
|
||||
/// <param name="hideEmptyRecycleBin">Value indicating if we should hide the Empty Recycle Bin command.</param>
|
||||
/// <param name="confirmCommands">A value indicating if the user should confirm the system commands</param>
|
||||
/// <param name="emptyRBSuccessMessage">Show a success message after empty Recycle Bin.</param>
|
||||
/// <returns>A list of all results</returns>
|
||||
public static List<IListItem> GetSystemCommands(bool isUefi, bool splitRecycleBinResults, bool confirmCommands, bool emptyRBSuccessMessage)
|
||||
public static List<IListItem> GetSystemCommands(bool isUefi, bool hideEmptyRecycleBin, bool confirmCommands, bool emptyRBSuccessMessage)
|
||||
{
|
||||
var results = new List<IListItem>();
|
||||
results.AddRange(new[]
|
||||
@@ -81,7 +81,7 @@ internal static class Commands
|
||||
});
|
||||
|
||||
// Show Recycle Bin results based on setting.
|
||||
if (splitRecycleBinResults)
|
||||
if (!hideEmptyRecycleBin)
|
||||
{
|
||||
results.AddRange(new[]
|
||||
{
|
||||
@@ -142,6 +142,7 @@ internal static class Commands
|
||||
}
|
||||
|
||||
CompositeFormat sysIpv4DescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_ip4_description);
|
||||
CompositeFormat sysIpv6DescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_ip6_description);
|
||||
CompositeFormat sysMacDescriptionCompositeFormate = CompositeFormat.Parse(Resources.Microsoft_plugin_sys_mac_description);
|
||||
var hideDisconnectedNetworkInfo = manager.HideDisconnectedNetworkInfo;
|
||||
|
||||
@@ -171,7 +172,7 @@ internal static class Commands
|
||||
results.Add(new ListItem(new CopyTextCommand(intInfo.GetConnectionDetails()))
|
||||
{
|
||||
Title = intInfo.IPv6Primary,
|
||||
Subtitle = string.Format(CultureInfo.InvariantCulture, sysIpv4DescriptionCompositeFormate, intInfo.ConnectionName),
|
||||
Subtitle = string.Format(CultureInfo.InvariantCulture, sysIpv6DescriptionCompositeFormate, intInfo.ConnectionName),
|
||||
Icon = Icons.NetworkAdapterIcon,
|
||||
Details = new Details() { Title = Resources.Microsoft_plugin_ext_connection_details, Body = intInfo.GetConnectionDetails() },
|
||||
});
|
||||
@@ -184,7 +185,7 @@ internal static class Commands
|
||||
Title = intInfo.PhysicalAddress,
|
||||
Subtitle = string.Format(CultureInfo.InvariantCulture, sysMacDescriptionCompositeFormate, intInfo.Adapter, intInfo.ConnectionName),
|
||||
Icon = Icons.NetworkAdapterIcon,
|
||||
Details = new Details() { Title = Resources.Microsoft_plugin_ext_connection_details, Body = intInfo.GetConnectionDetails() },
|
||||
Details = new Details() { Title = Resources.Microsoft_plugin_ext_adapter_details, Body = intInfo.GetAdapterDetails() },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -203,12 +204,12 @@ internal static class Commands
|
||||
|
||||
var isBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi;
|
||||
|
||||
var separateEmptyRB = manager.ShowSeparateResultForEmptyRecycleBin;
|
||||
var hideEmptyRB = 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);
|
||||
var systemCommands = Commands.GetSystemCommands(isBootedInUefiMode, hideEmptyRB, confirmSystemCommands, showSuccessOnEmptyRB);
|
||||
list.AddRange(systemCommands);
|
||||
list.AddRange(networkConnectionResults);
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
|
||||
public static partial class Icons
|
||||
{
|
||||
public static IconInfo FirmwareSettingsIcon { get; } = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.System\\Assets\\logoff.light.png", "Microsoft.CmdPal.Ext.System\\Assets\\logoff.dark.png");
|
||||
public static IconInfo FirmwareSettingsIcon { get; } = new IconInfo("\uE950");
|
||||
|
||||
public static IconInfo LockIcon { get; } = new IconInfo("\uE72E");
|
||||
|
||||
public static IconInfo LogoffIcon { get; } = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.System\\Assets\\logoff.light.png", "Microsoft.CmdPal.Ext.System\\Assets\\logoff.dark.png");
|
||||
public static IconInfo LogoffIcon { get; } = new IconInfo("\uF3B1");
|
||||
|
||||
public static IconInfo NetworkAdapterIcon { get; } = new IconInfo("\uEDA3");
|
||||
|
||||
@@ -22,5 +22,5 @@ public static partial class Icons
|
||||
|
||||
public static IconInfo ShutdownIcon { get; } = new IconInfo("\uE7E8");
|
||||
|
||||
public static IconInfo SleepIcon { get; } = IconHelpers.FromRelativePaths("Microsoft.CmdPal.Ext.System\\Assets\\sleep.light.png", "Microsoft.CmdPal.Ext.System\\Assets\\sleep.dark.png");
|
||||
public static IconInfo SleepIcon { get; } = new IconInfo("\uE708");
|
||||
}
|
||||
|
||||
@@ -19,6 +19,14 @@ namespace Microsoft.CmdPal.Ext.System.Helpers;
|
||||
/// </summary>
|
||||
internal sealed class NetworkConnectionProperties
|
||||
{
|
||||
/// <summary>
|
||||
/// Decimal unicode value for green circle emoji.
|
||||
/// We need to generate it in the code because it does not render using Markdown emoji syntax or Unicode character syntax.
|
||||
/// </summary>
|
||||
/// <seealso cref="https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/MarkdownTextBlock/samples/MarkdownTextBlock.md"/>
|
||||
/// <seealso cref="https://github.com/xoofx/markdig/blob/master/src/Markdig/Extensions/Emoji/EmojiMapping.cs"/>
|
||||
private const int GreenCircleCharacter = 128994;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the adapter
|
||||
/// </summary>
|
||||
@@ -161,12 +169,12 @@ internal sealed class NetworkConnectionProperties
|
||||
/// <returns>String with the details</returns>
|
||||
internal string GetAdapterDetails()
|
||||
{
|
||||
return $"{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
|
||||
$"\n{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}";
|
||||
return $"**{Resources.Microsoft_plugin_sys_AdapterName}:** {Adapter}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_State}:** " + (State == OperationalStatus.Up ? char.ConvertFromUtf32(GreenCircleCharacter) + " " + Resources.Microsoft_plugin_sys_Connected : ":red_circle: " + Resources.Microsoft_plugin_sys_Disconnected) +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_PhysicalAddress}:** {PhysicalAddress}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_Speed}:** {GetFormattedSpeedValue(Speed)}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_Type}:** {GetAdapterTypeAsString(Type)}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_ConnectionName}:** {ConnectionName}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -175,24 +183,24 @@ internal sealed class NetworkConnectionProperties
|
||||
/// <returns>String with the details</returns>
|
||||
internal string GetConnectionDetails()
|
||||
{
|
||||
return $"{Resources.Microsoft_plugin_sys_ConnectionName}: {ConnectionName}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_State}: " + (State == OperationalStatus.Up ? Resources.Microsoft_plugin_sys_Connected : Resources.Microsoft_plugin_sys_Disconnected) +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Type}: {GetAdapterTypeAsString(Type)}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Suffix}: {Suffix}" +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4Address}: ", IPv4) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip4SubnetMask}: ", IPv4Mask) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Address}:\n\t", IPv6Global) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Temp}:\n\t", IPv6Temporary) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Link}:\n\t", IPv6LinkLocal) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Site}:\n\t", IPv6SiteLocal) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Ip6Unique}:\n\t", IPv6UniqueLocal) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Gateways}:\n\t", Gateways) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dhcp}:\n\t", DhcpServers == null ? string.Empty : DhcpServers) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Dns}:\n\t", DnsServers == null ? string.Empty : DnsServers) +
|
||||
CreateIpInfoForDetailsText($"{Resources.Microsoft_plugin_sys_Wins}:\n\t", WinsServers == null ? string.Empty : WinsServers) +
|
||||
$"\n\n{Resources.Microsoft_plugin_sys_AdapterName}: {Adapter}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_PhysicalAddress}: {PhysicalAddress}" +
|
||||
$"\n{Resources.Microsoft_plugin_sys_Speed}: {GetFormattedSpeedValue(Speed)}";
|
||||
return $"**{Resources.Microsoft_plugin_sys_ConnectionName}:** {ConnectionName}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_State}:** " + (State == OperationalStatus.Up ? char.ConvertFromUtf32(GreenCircleCharacter) + " " + Resources.Microsoft_plugin_sys_Connected : ":red_circle: " + Resources.Microsoft_plugin_sys_Disconnected) +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_Type}:** {GetAdapterTypeAsString(Type)}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_Suffix}:** {Suffix}" +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip4Address}:** ", IPv4) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip4SubnetMask}:** ", IPv4Mask) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Address}:**\n\n* ", IPv6Global) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Temp}:**\n\n* ", IPv6Temporary) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Link}:**\n\n* ", IPv6LinkLocal) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Site}:**\n\n* ", IPv6SiteLocal) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Ip6Unique}:**\n\n* ", IPv6UniqueLocal) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Gateways}:**\n\n* ", Gateways) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Dhcp}:**\n\n* ", DhcpServers == null ? string.Empty : DhcpServers) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Dns}:**\n\n* ", DnsServers == null ? string.Empty : DnsServers) +
|
||||
CreateIpInfoForDetailsText($"**{Resources.Microsoft_plugin_sys_Wins}:**\n\n* ", WinsServers == null ? string.Empty : WinsServers) +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_AdapterName}:** {Adapter}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_PhysicalAddress}:** {PhysicalAddress}" +
|
||||
$"\n\n**{Resources.Microsoft_plugin_sys_Speed}:** {GetFormattedSpeedValue(Speed)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -304,13 +312,13 @@ internal sealed class NetworkConnectionProperties
|
||||
switch (property)
|
||||
{
|
||||
case string:
|
||||
return $"\n{title}{property}";
|
||||
return string.IsNullOrWhiteSpace(property) ? string.Empty : $"\n\n{title}{property}";
|
||||
case List<string> listString:
|
||||
return listString.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
|
||||
return listString.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}";
|
||||
case List<IPAddress> listIP:
|
||||
return listIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
|
||||
return listIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}";
|
||||
case IPAddressCollection collectionIP:
|
||||
return collectionIP.Count == 0 ? string.Empty : $"\n{title}{string.Join("\n\t", property)}";
|
||||
return collectionIP.Count == 0 ? string.Empty : $"\n\n{title}{string.Join("\n\n* ", property)}";
|
||||
case null:
|
||||
return string.Empty;
|
||||
default:
|
||||
|
||||
@@ -25,23 +25,23 @@ public class SettingsManager : JsonSettingsManager
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage,
|
||||
false); // TODO -- double check default value
|
||||
|
||||
private readonly ToggleSetting _showSeparateResultForEmptyRecycleBin = new(
|
||||
Namespaced(nameof(ShowSeparateResultForEmptyRecycleBin)),
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate,
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate,
|
||||
true); // TODO -- double check default value
|
||||
private readonly ToggleSetting _hideEmptyRecycleBin = new(
|
||||
Namespaced(nameof(HideEmptyRecycleBin)),
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_HideEmpty,
|
||||
Resources.Microsoft_plugin_sys_RecycleBin_HideEmpty,
|
||||
false);
|
||||
|
||||
private readonly ToggleSetting _hideDisconnectedNetworkInfo = new(
|
||||
Namespaced(nameof(HideDisconnectedNetworkInfo)),
|
||||
Resources.Microsoft_plugin_ext_settings_hideDisconnectedNetworkInfo,
|
||||
Resources.Microsoft_plugin_ext_settings_hideDisconnectedNetworkInfo,
|
||||
true); // TODO -- double check default value
|
||||
false);
|
||||
|
||||
public bool ShowDialogToConfirmCommand => _showDialogToConfirmCommand.Value;
|
||||
|
||||
public bool ShowSuccessMessageAfterEmptyingRecycleBin => _showSuccessMessageAfterEmptyingRecycleBin.Value;
|
||||
|
||||
public bool ShowSeparateResultForEmptyRecycleBin => _showSeparateResultForEmptyRecycleBin.Value;
|
||||
public bool HideEmptyRecycleBin => _hideEmptyRecycleBin.Value;
|
||||
|
||||
public bool HideDisconnectedNetworkInfo => _hideDisconnectedNetworkInfo.Value;
|
||||
|
||||
@@ -60,7 +60,7 @@ public class SettingsManager : JsonSettingsManager
|
||||
|
||||
Settings.Add(_showDialogToConfirmCommand);
|
||||
Settings.Add(_showSuccessMessageAfterEmptyingRecycleBin);
|
||||
Settings.Add(_showSeparateResultForEmptyRecycleBin);
|
||||
Settings.Add(_hideEmptyRecycleBin);
|
||||
Settings.Add(_hideDisconnectedNetworkInfo);
|
||||
|
||||
// Load settings from file upon initialization
|
||||
|
||||
@@ -17,6 +17,14 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\SystemCommand.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\SystemCommand.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
|
||||
@@ -15,7 +15,8 @@ public sealed partial class SystemCommandPage : ListPage
|
||||
public SystemCommandPage(SettingsManager settingsManager)
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_ext_system_page_name;
|
||||
Icon = new IconInfo("\uE72E");
|
||||
Name = Resources.Microsoft_plugin_ext_system_page_name;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\SystemCommand.svg");
|
||||
_settingsManager = settingsManager;
|
||||
ShowDetails = true;
|
||||
}
|
||||
|
||||
@@ -159,6 +159,15 @@ namespace Microsoft.CmdPal.Ext.System {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adapter Details.
|
||||
/// </summary>
|
||||
public static string Microsoft_plugin_ext_adapter_details {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_ext_adapter_details", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection Details.
|
||||
/// </summary>
|
||||
@@ -528,6 +537,15 @@ namespace Microsoft.CmdPal.Ext.System {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Hide the Empty Recycle Bin command.
|
||||
/// </summary>
|
||||
public static string Microsoft_plugin_sys_RecycleBin_HideEmpty {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_HideEmpty", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Recycle Bin is empty..
|
||||
/// </summary>
|
||||
@@ -546,15 +564,6 @@ namespace Microsoft.CmdPal.Ext.System {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show separate result for Empty Recycle Bin command.
|
||||
/// </summary>
|
||||
public static string Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show a success message after emptying the Recycle Bin.
|
||||
/// </summary>
|
||||
|
||||
@@ -151,6 +151,9 @@
|
||||
<data name="Microsoft_plugin_ext_connection_details" xml:space="preserve">
|
||||
<value>Connection Details</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_ext_adapter_details" xml:space="preserve">
|
||||
<value>Adapter Details</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_ext_copy" xml:space="preserve">
|
||||
<value>Copy to clipboard</value>
|
||||
</data>
|
||||
@@ -309,8 +312,8 @@
|
||||
<value>Empty Recycle Bin</value>
|
||||
<comment>This should align to the action in Windows of emptying the recycle bin on your computer.</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_sys_RecycleBin_ShowEmptySeparate" xml:space="preserve">
|
||||
<value>Show separate result for Empty Recycle Bin command</value>
|
||||
<data name="Microsoft_plugin_sys_RecycleBin_HideEmpty" xml:space="preserve">
|
||||
<value>Hide the Empty Recycle Bin command</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_sys_RecycleBin_ShowEmptySuccessMessage" xml:space="preserve">
|
||||
<value>Show a success message after emptying the Recycle Bin</value>
|
||||
|
||||
@@ -18,16 +18,18 @@ public partial class SystemCommandExtensionProvider : CommandProvider
|
||||
public SystemCommandExtensionProvider()
|
||||
{
|
||||
DisplayName = Resources.Microsoft_plugin_ext_system_page_name;
|
||||
Id = "System";
|
||||
_commands = [
|
||||
new CommandItem(Page)
|
||||
{
|
||||
Title = DisplayName,
|
||||
Title = Resources.Microsoft_plugin_ext_system_page_name,
|
||||
Icon = Page.Icon,
|
||||
MoreCommands = [new CommandContextItem(_settingsManager.Settings.SettingsPage)],
|
||||
},
|
||||
];
|
||||
|
||||
Icon = Page.Icon;
|
||||
Settings = _settingsManager.Settings;
|
||||
}
|
||||
|
||||
public override ICommandItem[] TopLevelCommands()
|
||||
|
||||
@@ -20,7 +20,7 @@ public sealed partial class SystemCommandsCache
|
||||
{
|
||||
var isBootedInUefiMode = Win32Helpers.GetSystemFirmwareType() == FirmwareType.Uefi;
|
||||
|
||||
var separateEmptyRB = manager.ShowSeparateResultForEmptyRecycleBin;
|
||||
var separateEmptyRB = manager.HideEmptyRecycleBin;
|
||||
var confirmSystemCommands = manager.ShowDialogToConfirmCommand;
|
||||
var showSuccessOnEmptyRB = manager.ShowSuccessMessageAfterEmptyingRecycleBin;
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace Microsoft.CmdPal.Ext.TimeDate {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Time &amp; Date.
|
||||
/// Looks up a localized string similar to Time and Date.
|
||||
/// </summary>
|
||||
public static string Microsoft_plugin_timedate_main_page_title {
|
||||
get {
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
<value>Error: Invalid input</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_main_page_title" xml:space="preserve">
|
||||
<value>Time &amp; Date</value>
|
||||
<value>Time and Date</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_InvalidInput_ErrorMessageSubTitle" xml:space="preserve">
|
||||
<value>Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time</value>
|
||||
|
||||
@@ -22,7 +22,7 @@ public partial class TimeDateCommandsProvider : CommandProvider
|
||||
public TimeDateCommandsProvider()
|
||||
{
|
||||
DisplayName = Resources.Microsoft_plugin_timedate_plugin_name;
|
||||
|
||||
Id = "DateTime";
|
||||
_command = new CommandItem(_timeDateExtensionPage)
|
||||
{
|
||||
Icon = _timeDateExtensionPage.Icon,
|
||||
@@ -32,6 +32,7 @@ public partial class TimeDateCommandsProvider : CommandProvider
|
||||
};
|
||||
|
||||
Icon = _timeDateExtensionPage.Icon;
|
||||
Settings = _settingsManager.Settings;
|
||||
}
|
||||
|
||||
private string GetTranslatedPluginDescription()
|
||||
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.64706 0C9.69012 0 10.5357 0.884576 10.5357 1.97576L10.5351 2.4H13.2353C13.6576 2.4 14 2.75818 14 3.2V7C13.4477 7 13 7.44772 13 8C13 8.55229 13.4477 9 14 9V12.8023C14 13.2442 13.6576 13.6023 13.2353 13.6023L10.5351 13.6016L10.5357 14.0242C10.5357 15.1154 9.69012 16 8.64706 16C7.604 16 6.75846 15.1154 6.75846 14.0242L6.75823 13.6016L4.05882 13.6023C3.63649 13.6023 3.29412 13.2442 3.29412 12.8023L3.29335 9.9768L2.88859 9.97696C1.84555 9.97696 1 9.09232 1 8.0012C1 6.91 1.84555 6.02542 2.88859 6.02542L3.29335 6.0248L3.29412 3.2C3.29412 2.75818 3.63649 2.4 4.05882 2.4H6.75823L6.75846 1.97576C6.75846 0.884576 7.604 0 8.64706 0Z" fill="url(#paint0_linear_1889_18231)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.39833 4.18539C7.43685 4.07388 7.53504 4 7.64474 4H9.42105C9.50736 4 9.58818 4.04595 9.63735 4.12297C9.68651 4.19999 9.69776 4.29829 9.66745 4.38603L8.81395 6.85714H10.7368C10.8413 6.85714 10.9359 6.92426 10.9779 7.02818C11.0198 7.13209 11.0011 7.2531 10.9303 7.33651L7.04875 11.9079C6.96343 12.0084 6.82451 12.0292 6.71732 11.9576C6.61012 11.886 6.56466 11.7419 6.60886 11.614L7.65974 8.57142H6.26316C6.17685 8.57142 6.09602 8.52547 6.04686 8.44845C5.9977 8.37143 5.98645 8.27313 6.01676 8.18539L7.39833 4.18539Z" fill="url(#paint1_linear_1889_18231)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1889_18231" x1="5.5" y1="1" x2="11.5" y2="15" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#097ED1"/>
|
||||
<stop offset="1" stop-color="#0F5499"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1889_18231" x1="6" y1="5.28571" x2="11.4103" y2="10.2689" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#32BCEF"/>
|
||||
<stop offset="1" stop-color="#128FDD"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,34 @@
|
||||
<svg width="18" height="16" viewBox="0 0 18 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect y="3" width="16" height="10" rx="2" fill="url(#paint0_linear_1900_17890)"/>
|
||||
<g filter="url(#filter0_dd_1900_17890)">
|
||||
<path d="M14.375 10.0137H10.625C10.2798 10.0137 10 9.78981 10 9.51367V6.51367C10 6.23753 10.2798 6.01367 10.625 6.01367H14.375C14.7202 6.01367 15 6.23753 15 6.51367V9.51367C15 9.78981 14.7202 10.0137 14.375 10.0137Z" fill="url(#paint1_linear_1900_17890)"/>
|
||||
</g>
|
||||
<rect x="2" y="7.01367" width="3" height="2" rx="0.5" fill="#CAD2D9"/>
|
||||
<path d="M6 7.51367C6 7.23753 6.22386 7.01367 6.5 7.01367H8.5C8.77614 7.01367 9 7.23753 9 7.51367V8.51367C9 8.78981 8.77614 9.01367 8.5 9.01367H6.5C6.22386 9.01367 6 8.78981 6 8.51367V7.51367Z" fill="#CAD2D9"/>
|
||||
<defs>
|
||||
<filter id="filter0_dd_1900_17890" x="7" y="4.01367" width="11" height="10" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="0.5"/>
|
||||
<feGaussianBlur stdDeviation="0.5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1900_17890"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dy="1"/>
|
||||
<feGaussianBlur stdDeviation="1.5"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow_1900_17890" result="effect2_dropShadow_1900_17890"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_1900_17890" result="shape"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_1900_17890" x1="14.4562" y1="13.2418" x2="10.9314" y2="0.217619" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#626F7A"/>
|
||||
<stop offset="1" stop-color="#8B9299"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1900_17890" x1="13.4016" y1="12.3863" x2="8.18482" y2="9.15927" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#28AFEA"/>
|
||||
<stop offset="0.37387" stop-color="#3CCAF4"/>
|
||||
<stop offset="0.74949" stop-color="#4BDFFC"/>
|
||||
<stop offset="1" stop-color="#50E6FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -7,6 +7,9 @@
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\WindowWalker.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extensionsdk\Microsoft.CommandPalette.Extensions.Toolkit\Microsoft.CommandPalette.Extensions.Toolkit.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -17,6 +20,14 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\WindowWalker.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\WindowWalker.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
|
||||
@@ -19,7 +19,7 @@ internal sealed partial class WindowWalkerListPage : DynamicListPage, IDisposabl
|
||||
|
||||
public WindowWalkerListPage()
|
||||
{
|
||||
Icon = new IconInfo("\ue8f9"); // SwitchApps
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WindowWalker.svg");
|
||||
Name = Resources.windowwalker_name;
|
||||
Id = "com.microsoft.cmdpal.windowwalker";
|
||||
PlaceholderText = Resources.windowwalker_PlaceholderText;
|
||||
|
||||
@@ -20,7 +20,7 @@ public partial class WindowWalkerCommandsProvider : CommandProvider
|
||||
{
|
||||
Id = "WindowWalker";
|
||||
DisplayName = Resources.windowwalker_name;
|
||||
Icon = new IconInfo("\ue8f9"); // SwitchApps
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WindowWalker.svg");
|
||||
Settings = SettingsManager.Instance.Settings;
|
||||
|
||||
_windowWalkerPageItem = new CommandItem(new WindowWalkerListPage())
|
||||
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,19 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_1909_18132)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.69797 0.156184C7.77288 0.031333 7.89773 -0.0186073 8.02258 0.031333C8.34719 0.156184 8.64683 0.281034 8.97144 0.505765C9.27109 0.680556 9.57073 0.880318 9.82043 1.10505C9.92031 1.20493 9.94527 1.35475 9.87036 1.45463C9.52078 2.05391 9.49581 2.80302 9.87036 3.45224C10.2449 4.10146 10.9191 4.45104 11.6183 4.45104C11.7681 4.45104 11.868 4.55092 11.893 4.67577C12.0178 5.34997 12.0428 6.04913 11.9179 6.72332C11.9179 6.84817 11.7931 6.94806 11.6682 6.94806C10.9691 6.94806 10.2949 7.29764 9.92031 7.94686C9.54575 8.57111 9.5957 9.34519 9.94528 9.94447C10.0202 10.0443 9.99522 10.1942 9.89534 10.2691C9.37097 10.7185 8.77168 11.0681 8.12245 11.2929C7.9976 11.3178 7.87276 11.2679 7.79785 11.168C7.44826 10.5437 6.79904 10.1442 6.04994 10.1442C5.30083 10.1442 4.65161 10.5438 4.30203 11.143C4.22712 11.2679 4.10227 11.3178 3.97742 11.2679C3.65281 11.143 3.3282 10.9932 3.02856 10.8184C2.72892 10.6436 2.42927 10.4439 2.17957 10.2191C2.07969 10.1193 2.05472 9.96944 2.12963 9.86956C2.47922 9.27028 2.50419 8.52117 2.12963 7.87195C1.75508 7.22273 1.08089 6.87314 0.381727 6.87314C0.231906 6.87314 0.132024 6.77326 0.107054 6.64841C-0.0177965 5.97422 -0.0427681 5.27506 0.0820824 4.60086C0.0820824 4.47601 0.206933 4.37613 0.331784 4.37613C1.03095 4.37613 1.70514 4.02655 2.07969 3.37733C2.45424 2.75308 2.4043 1.979 2.05472 1.37972C1.97981 1.25487 2.00478 1.10505 2.10466 1.03014C2.62904 0.580676 3.22832 0.231094 3.87754 0.00636323C4.00239 -0.0186069 4.12724 0.0313334 4.20215 0.131214C4.55173 0.755467 5.20096 1.15499 5.95006 1.15499C6.69916 1.15499 7.34838 0.755466 7.69797 0.156184ZM8.25 5.625C8.25 6.86764 7.24264 7.875 6 7.875C4.75736 7.875 3.75 6.86764 3.75 5.625C3.75 4.38236 4.75736 3.375 6 3.375C7.24264 3.375 8.25 4.38236 8.25 5.625Z" fill="url(#paint0_linear_1909_18132)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.5724 14.505C14.6177 14.5866 14.6054 14.672 14.5376 14.7254C14.3641 14.8657 14.1986 14.9922 13.9695 15.1005C13.7763 15.2109 13.5691 15.3133 13.3641 15.38C13.2766 15.4034 13.1853 15.3692 13.1539 15.2956C12.933 14.909 12.5247 14.6548 12.0438 14.6547C11.5629 14.6545 11.1523 14.917 10.928 15.3055C10.8799 15.3888 10.7924 15.4122 10.715 15.3861C10.3003 15.2391 9.90374 15.0287 9.56915 14.743C9.49977 14.703 9.48433 14.6015 9.52438 14.5322C9.7487 14.1436 9.77074 13.6568 9.53014 13.2404C9.30341 12.832 8.85723 12.6114 8.41206 12.6134C8.33252 12.623 8.25728 12.561 8.2477 12.4815C8.16617 12.0459 8.16418 11.6007 8.24759 11.1679C8.27377 11.0905 8.34158 11.0371 8.42111 11.0275C8.88016 11.0335 9.31047 10.801 9.5508 10.3847C9.79114 9.96841 9.77741 9.47946 9.55655 9.09293C9.51121 9.01125 9.52351 8.92585 9.59132 8.87249C9.76484 8.73216 9.95224 8.59985 10.1455 8.48942C10.3388 8.37899 10.5459 8.27656 10.7509 8.20991C10.8385 8.18645 10.9297 8.22064 10.9612 8.29431C11.1821 8.68084 11.5903 8.93505 12.0713 8.93521C12.5522 8.93536 12.9628 8.67287 13.1871 8.28435C13.2351 8.2011 13.3227 8.17764 13.4001 8.20382C13.8148 8.35074 14.2113 8.56118 14.5459 8.84686C14.6153 8.88691 14.6307 8.98834 14.5907 9.05772C14.3664 9.44624 14.3443 9.93304 14.5849 10.3495C14.8116 10.7579 15.2578 10.9785 15.703 10.9765C15.7964 10.9749 15.8716 11.0369 15.8812 11.1164C15.9628 11.552 15.9647 11.9972 15.8813 12.43C15.8552 12.5074 15.7873 12.5608 15.7078 12.5704C15.2488 12.5643 14.8185 12.7969 14.5781 13.2132C14.3378 13.6295 14.3515 14.1184 14.5724 14.505ZM11.3563 13.0572C10.6657 12.6585 10.4291 11.7755 10.8278 11.085C11.2265 10.3944 12.1095 10.1579 12.8 10.5565C13.4905 10.9552 13.7271 11.8382 13.3285 12.5287C12.9298 13.2192 12.0468 13.4558 11.3563 13.0572Z" fill="url(#paint1_linear_1909_18132)"/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1909_18132" x1="8.9717" y1="10.8245" x2="2.97903" y2="0.444546" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#626F7A"/>
|
||||
<stop offset="1" stop-color="#8B9299"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1909_18132" x1="8.23537" y1="11.79" x2="15.9261" y2="11.7902" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#626F7A"/>
|
||||
<stop offset="1" stop-color="#8B9299"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_1909_18132">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
@@ -10,9 +10,9 @@ using System.Linq;
|
||||
using System.ServiceProcess;
|
||||
using Microsoft.CmdPal.Ext.WindowsServices.Commands;
|
||||
using Microsoft.CmdPal.Ext.WindowsServices.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Win32;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WindowsServices.Helpers;
|
||||
|
||||
@@ -43,9 +43,14 @@ public static class ServiceHelper
|
||||
serviceList = servicesStartsWith.Concat(servicesContains);
|
||||
}
|
||||
|
||||
return serviceList.Select(s =>
|
||||
var result = serviceList.Select(s =>
|
||||
{
|
||||
var serviceResult = new ServiceResult(s);
|
||||
var serviceResult = ServiceResult.CreateServiceController(s);
|
||||
if (serviceResult == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ServiceCommand serviceCommand;
|
||||
CommandContextItem[] moreCommands;
|
||||
if (serviceResult.IsRunning)
|
||||
@@ -53,7 +58,10 @@ public static class ServiceHelper
|
||||
serviceCommand = new ServiceCommand(serviceResult, Action.Stop);
|
||||
moreCommands = [
|
||||
new CommandContextItem(new RestartServiceCommand(serviceResult)),
|
||||
new CommandContextItem(new OpenServicesCommand(serviceResult)),
|
||||
new CommandContextItem(new OpenServicesCommand(serviceResult))
|
||||
{
|
||||
RequestedShortcut = KeyChordHelpers.FromModifiers(true, false, false, false, (int)VirtualKey.O, 0),
|
||||
},
|
||||
];
|
||||
}
|
||||
else
|
||||
@@ -89,7 +97,9 @@ public static class ServiceHelper
|
||||
// ToolTipData = new ToolTipData(serviceResult.DisplayName, serviceResult.ServiceName),
|
||||
// IcoPath = icoPath,
|
||||
};
|
||||
});
|
||||
}).Where(s => s != null);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO GH #118 IPublicAPI contextAPI isn't used anymore, but we need equivalent ways to show notifications and status
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>Microsoft.CmdPal.Ext.WindowsServices.pri</ProjectPriFileName>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Services.svg" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.ServiceProcess.ServiceController" />
|
||||
</ItemGroup>
|
||||
@@ -21,6 +24,14 @@
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\Services.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="Assets\Services.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
|
||||
@@ -61,11 +61,11 @@ namespace Microsoft.CmdPal.Ext.WindowsServices.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to .
|
||||
/// Looks up a localized string similar to Windows Services.
|
||||
/// </summary>
|
||||
internal static string test_value {
|
||||
internal static string WindowsServicesProvider_DisplayName {
|
||||
get {
|
||||
return ResourceManager.GetString("test_value", resourceCulture);
|
||||
return ResourceManager.GetString("WindowsServicesProvider_DisplayName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,9 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="WindowsServicesProvider_DisplayName" xml:space="preserve">
|
||||
<value>Windows Services</value>
|
||||
</data>
|
||||
<data name="wox_plugin_service_continue_pending" xml:space="preserve">
|
||||
<value>Continue</value>
|
||||
</data>
|
||||
@@ -124,7 +127,7 @@
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="wox_plugin_service_open_services" xml:space="preserve">
|
||||
<value>Open services (Ctrl+O)</value>
|
||||
<value>Open services</value>
|
||||
</data>
|
||||
<data name="wox_plugin_service_paused" xml:space="preserve">
|
||||
<value>Paused</value>
|
||||
@@ -139,7 +142,7 @@
|
||||
<value>Service</value>
|
||||
</data>
|
||||
<data name="wox_plugin_service_restart" xml:space="preserve">
|
||||
<value>Restart (Ctrl+R)</value>
|
||||
<value>Restart</value>
|
||||
</data>
|
||||
<data name="wox_plugin_service_restarted_notification" xml:space="preserve">
|
||||
<value>The service has been restarted</value>
|
||||
@@ -204,7 +207,4 @@
|
||||
<data name="wox_plugin_service_stop_pending" xml:space="preserve">
|
||||
<value>Stopping</value>
|
||||
</data>
|
||||
<data name="test_value" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
</root>
|
||||
@@ -17,7 +17,7 @@ public class ServiceResult
|
||||
|
||||
public bool IsRunning { get; }
|
||||
|
||||
public ServiceResult(ServiceController serviceController)
|
||||
private ServiceResult(ServiceController serviceController)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(serviceController);
|
||||
|
||||
@@ -26,4 +26,21 @@ public class ServiceResult
|
||||
StartMode = serviceController.StartType;
|
||||
IsRunning = serviceController.Status != ServiceControllerStatus.Stopped && serviceController.Status != ServiceControllerStatus.StopPending;
|
||||
}
|
||||
|
||||
public static ServiceResult CreateServiceController(ServiceController serviceController)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = new ServiceResult(serviceController);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// try to log the exception in the future
|
||||
// retrieve properties from serviceController will throw exception. Such as PlatformNotSupportedException.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.WindowsServices.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -10,12 +11,12 @@ namespace Microsoft.CmdPal.Ext.WindowsServices;
|
||||
public partial class WindowsServicesCommandsProvider : CommandProvider
|
||||
{
|
||||
// For giggles, "%windir%\\system32\\filemgmt.dll" also _just works_.
|
||||
public static IconInfo ServicesIcon { get; } = new("\ue9f5");
|
||||
public static IconInfo ServicesIcon { get; } = IconHelpers.FromRelativePath("Assets\\Services.svg");
|
||||
|
||||
public WindowsServicesCommandsProvider()
|
||||
{
|
||||
Id = "Windows.Services";
|
||||
DisplayName = $"Windows Services";
|
||||
DisplayName = Resources.WindowsServicesProvider_DisplayName;
|
||||
Icon = ServicesIcon;
|
||||
}
|
||||
|
||||
|
||||
@@ -4731,6 +4731,15 @@ namespace Microsoft.CmdPal.Ext.WindowsSettings.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Windows Settings.
|
||||
/// </summary>
|
||||
internal static string WindowsSettingsProvider_DisplayName {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowsSettingsProvider_DisplayName", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Windows Update.
|
||||
/// </summary>
|
||||
|
||||
@@ -2082,4 +2082,7 @@
|
||||
<data name="OpenSettings" xml:space="preserve">
|
||||
<value>Open Settings</value>
|
||||
</data>
|
||||
<data name="WindowsSettingsProvider_DisplayName" xml:space="preserve">
|
||||
<value>Windows Settings</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using Microsoft.CmdPal.Ext.WindowsSettings.Helpers;
|
||||
using Microsoft.CmdPal.Ext.WindowsSettings.Properties;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
@@ -19,7 +20,7 @@ public partial class WindowsSettingsCommandsProvider : CommandProvider
|
||||
public WindowsSettingsCommandsProvider()
|
||||
{
|
||||
Id = "Windows.Settings";
|
||||
DisplayName = $"Windows Settings";
|
||||
DisplayName = Resources.WindowsSettingsProvider_DisplayName;
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\WindowsSettings.svg");
|
||||
|
||||
_windowsSettings = JsonSettingsListHelper.ReadAllPossibleSettings();
|
||||
|
||||
@@ -54,6 +54,11 @@ internal sealed partial class SampleListPage : ListPage
|
||||
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",
|
||||
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,25 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M8.64706 0C9.69012 0 10.5357 0.884576 10.5357 1.97576L10.5351 2.4H13.2353C13.6576 2.4 14 2.75818 14 3.2V7C13.4477 7 13 7.44772 13 8C13 8.55229 13.4477 9 14 9V12.8023C14 13.2442 13.6576 13.6023 13.2353 13.6023L10.5351 13.6016L10.5357 14.0242C10.5357 15.1154 9.69012 16 8.64706 16C7.604 16 6.75846 15.1154 6.75846 14.0242L6.75823 13.6016L4.05882 13.6023C3.63649 13.6023 3.29412 13.2442 3.29412 12.8023L3.29335 9.9768L2.88859 9.97696C1.84555 9.97696 1 9.09232 1 8.0012C1 6.91 1.84555 6.02542 2.88859 6.02542L3.29335 6.0248L3.29412 3.2C3.29412 2.75818 3.63649 2.4 4.05882 2.4H6.75823L6.75846 1.97576C6.75846 0.884576 7.604 0 8.64706 0Z" fill="url(#paint0_linear_1889_18238)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.39833 4.18539C7.43685 4.07388 7.53504 4 7.64474 4H9.42105C9.50736 4 9.58818 4.04595 9.63735 4.12297C9.68651 4.19999 9.69776 4.29829 9.66745 4.38603L8.81395 6.85714H10.7368C10.8413 6.85714 10.9359 6.92426 10.9779 7.02818C11.0198 7.13209 11.0011 7.2531 10.9303 7.33651L7.04875 11.9079C6.96343 12.0084 6.82451 12.0292 6.71732 11.9576C6.61012 11.886 6.56466 11.7419 6.60886 11.614L7.65974 8.57142H6.26316C6.17685 8.57142 6.09602 8.52547 6.04686 8.44845C5.9977 8.37143 5.98645 8.27313 6.01676 8.18539L7.39833 4.18539Z" fill="url(#paint1_linear_1889_18238)"/>
|
||||
<path d="M9.17749 8.02412C9.86491 7.56481 10.6731 7.31963 11.4998 7.31959C12.6082 7.32093 13.6707 7.76181 14.4545 8.54554C15.2382 9.32931 15.6791 10.392 15.6804 11.5004C15.6803 12.3271 15.4352 13.1352 14.9759 13.8225C14.5165 14.51 13.8636 15.0458 13.0998 15.3622C12.3359 15.6786 11.4954 15.7614 10.6844 15.6001C9.87352 15.4388 9.12865 15.0406 8.54401 14.456C7.95937 13.8713 7.56122 13.1265 7.39992 12.3156C7.23862 11.5046 7.3214 10.6641 7.63781 9.90023C7.95421 9.13636 8.49003 8.48347 9.17749 8.02412Z" fill="url(#paint2_linear_1889_18238)" stroke="url(#paint3_linear_1889_18238)" stroke-width="0.639184"/>
|
||||
<rect x="8" y="11" width="7" height="1" rx="0.5" fill="#0C58A2"/>
|
||||
<rect x="11" y="15" width="7" height="1" rx="0.5" transform="rotate(-90 11 15)" fill="#0C58A2"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1889_18238" x1="5.5" y1="1" x2="11.5" y2="15" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#097ED1"/>
|
||||
<stop offset="1" stop-color="#0F5499"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_1889_18238" x1="6" y1="5.28571" x2="11.4103" y2="10.2689" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#32BCEF"/>
|
||||
<stop offset="1" stop-color="#128FDD"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_1889_18238" x1="13.7504" y1="15.3978" x2="9.24963" y2="7.60225" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FCFCFC"/>
|
||||
<stop offset="1" stop-color="#E7E7E7"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_1889_18238" x1="9.75" y1="7" x2="12.8347" y2="16.0704" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#173A73" stop-opacity="0.1"/>
|
||||
<stop offset="1" stop-color="#173A73" stop-opacity="0.25"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -146,6 +146,10 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
|
||||
UpdateProperty(nameof(Title));
|
||||
UpdateProperty(nameof(Subtitle));
|
||||
UpdateProperty(nameof(Icon));
|
||||
|
||||
// Load-bearing: if you don't raise a IsInitialized here, then
|
||||
// TopLevelViewModel will never know what the command's ID is, so it
|
||||
// will never be able to load Hotkeys & aliases
|
||||
UpdateProperty(nameof(IsInitialized));
|
||||
|
||||
Initialized |= InitializedState.Initialized;
|
||||
|
||||
@@ -6,6 +6,8 @@ using ManagedCommon;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
@@ -20,9 +22,9 @@ public sealed class CommandProviderWrapper
|
||||
|
||||
private readonly TaskScheduler _taskScheduler;
|
||||
|
||||
public ICommandItem[] TopLevelItems { get; private set; } = [];
|
||||
public TopLevelViewModel[] TopLevelItems { get; private set; } = [];
|
||||
|
||||
public IFallbackCommandItem[] FallbackItems { get; private set; } = [];
|
||||
public TopLevelViewModel[] FallbackItems { get; private set; } = [];
|
||||
|
||||
public string DisplayName { get; private set; } = string.Empty;
|
||||
|
||||
@@ -38,7 +40,13 @@ public sealed class CommandProviderWrapper
|
||||
|
||||
public CommandSettingsViewModel? Settings { get; private set; }
|
||||
|
||||
public string ProviderId => $"{Extension?.PackageFamilyName ?? string.Empty}/{Id}";
|
||||
public string ProviderId
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.IsNullOrEmpty(Extension?.ExtensionUniqueId) ? Id : Extension.ExtensionUniqueId;
|
||||
}
|
||||
}
|
||||
|
||||
public CommandProviderWrapper(ICommandProvider provider, TaskScheduler mainThread)
|
||||
{
|
||||
@@ -105,13 +113,25 @@ public sealed class CommandProviderWrapper
|
||||
isValid = true;
|
||||
}
|
||||
|
||||
public async Task LoadTopLevelCommands()
|
||||
private ProviderSettings GetProviderSettings(SettingsModel settings)
|
||||
{
|
||||
return settings.GetProviderSettings(this);
|
||||
}
|
||||
|
||||
public async Task LoadTopLevelCommands(IServiceProvider serviceProvider, WeakReference<IPageContext> pageContext)
|
||||
{
|
||||
if (!isValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
|
||||
if (!GetProviderSettings(settings).IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ICommandItem[]? commands = null;
|
||||
IFallbackCommandItem[]? fallbacks = null;
|
||||
|
||||
@@ -119,7 +139,7 @@ public sealed class CommandProviderWrapper
|
||||
{
|
||||
var model = _commandProvider.Unsafe!;
|
||||
|
||||
var t = new Task<ICommandItem[]>(model.TopLevelCommands);
|
||||
Task<ICommandItem[]> t = new(model.TopLevelCommands);
|
||||
t.Start();
|
||||
commands = await t.ConfigureAwait(false);
|
||||
|
||||
@@ -134,6 +154,8 @@ public sealed class CommandProviderWrapper
|
||||
Settings = new(model.Settings, this, _taskScheduler);
|
||||
Settings.InitializeProperties();
|
||||
|
||||
InitializeCommands(commands, fallbacks, serviceProvider, pageContext);
|
||||
|
||||
Logger.LogDebug($"Loaded commands from {DisplayName} ({ProviderId})");
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -142,15 +164,33 @@ public sealed class CommandProviderWrapper
|
||||
Logger.LogError($"Extension was {Extension!.PackageFamilyName}");
|
||||
Logger.LogError(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeCommands(ICommandItem[] commands, IFallbackCommandItem[] fallbacks, IServiceProvider serviceProvider, WeakReference<IPageContext> pageContext)
|
||||
{
|
||||
var settings = serviceProvider.GetService<SettingsModel>()!;
|
||||
|
||||
Func<ICommandItem?, bool, TopLevelViewModel> makeAndAdd = (ICommandItem? i, bool fallback) =>
|
||||
{
|
||||
CommandItemViewModel commandItemViewModel = new(new(i), pageContext);
|
||||
TopLevelViewModel topLevelViewModel = new(commandItemViewModel, fallback, ExtensionHost, ProviderId, settings, serviceProvider);
|
||||
|
||||
topLevelViewModel.ItemViewModel.SlowInitializeProperties();
|
||||
|
||||
return topLevelViewModel;
|
||||
};
|
||||
if (commands != null)
|
||||
{
|
||||
TopLevelItems = commands;
|
||||
TopLevelItems = commands
|
||||
.Select(c => makeAndAdd(c, false))
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
if (fallbacks != null)
|
||||
{
|
||||
FallbackItems = fallbacks;
|
||||
FallbackItems = fallbacks
|
||||
.Select(c => makeAndAdd(c, true))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public partial class CommandViewModel : ExtensionObjectViewModel
|
||||
return;
|
||||
}
|
||||
|
||||
Id = model.Id ?? string.Empty;
|
||||
Name = model.Name ?? string.Empty;
|
||||
IsFastInitialized = true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.Immutable;
|
||||
using System.Collections.Specialized;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Ext.Apps;
|
||||
@@ -73,7 +74,6 @@ public partial class MainListPage : DynamicListPage,
|
||||
{
|
||||
return _tlcManager
|
||||
.TopLevelCommands
|
||||
.Select(tlc => tlc)
|
||||
.Where(tlc => !string.IsNullOrEmpty(tlc.Title))
|
||||
.ToArray();
|
||||
}
|
||||
@@ -102,12 +102,7 @@ public partial class MainListPage : DynamicListPage,
|
||||
var commands = _tlcManager.TopLevelCommands;
|
||||
lock (commands)
|
||||
{
|
||||
// This gets called on a background thread, because ListViewModel
|
||||
// updates the .SearchText of all extensions on a BG thread.
|
||||
foreach (var command in commands)
|
||||
{
|
||||
command.TryUpdateFallbackText(newSearch);
|
||||
}
|
||||
UpdateFallbacks(newSearch, commands.ToImmutableArray());
|
||||
|
||||
// Cleared out the filter text? easy. Reset _filteredItems, and bail out.
|
||||
if (string.IsNullOrEmpty(newSearch))
|
||||
@@ -138,6 +133,26 @@ public partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFallbacks(string newSearch, IReadOnlyList<TopLevelViewModel> commands)
|
||||
{
|
||||
// fire and forget
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
var needsToUpdate = false;
|
||||
|
||||
foreach (var command in commands)
|
||||
{
|
||||
var changedVisibility = command.SafeUpdateFallbackTextSynchronous(newSearch);
|
||||
needsToUpdate = needsToUpdate || changedVisibility;
|
||||
}
|
||||
|
||||
if (needsToUpdate)
|
||||
{
|
||||
RaiseItemsChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private bool ActuallyLoading()
|
||||
{
|
||||
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
@@ -150,46 +165,72 @@ public partial class MainListPage : DynamicListPage,
|
||||
// _always_ show up first.
|
||||
private int ScoreTopLevelItem(string query, IListItem topLevelOrAppItem)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
var title = topLevelOrAppItem.Title;
|
||||
if (string.IsNullOrEmpty(title))
|
||||
if (string.IsNullOrWhiteSpace(title))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var isWhiteSpace = string.IsNullOrWhiteSpace(query);
|
||||
|
||||
var isFallback = false;
|
||||
var isAliasSubstringMatch = false;
|
||||
var isAliasMatch = false;
|
||||
var id = IdForTopLevelOrAppItem(topLevelOrAppItem);
|
||||
|
||||
var extensionDisplayName = string.Empty;
|
||||
if (topLevelOrAppItem is TopLevelCommandItemWrapper toplevel)
|
||||
if (topLevelOrAppItem is TopLevelViewModel topLevel)
|
||||
{
|
||||
isFallback = toplevel.IsFallback;
|
||||
if (toplevel.Alias?.Alias is string alias)
|
||||
isFallback = topLevel.IsFallback;
|
||||
if (topLevel.HasAlias)
|
||||
{
|
||||
var alias = topLevel.AliasText;
|
||||
isAliasMatch = alias == query;
|
||||
isAliasSubstringMatch = isAliasMatch || alias.StartsWith(query, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
extensionDisplayName = toplevel.ExtensionHost?.Extension?.PackageDisplayName ?? string.Empty;
|
||||
extensionDisplayName = topLevel.ExtensionHost?.Extension?.PackageDisplayName ?? string.Empty;
|
||||
}
|
||||
|
||||
var nameMatch = StringMatcher.FuzzySearch(query, title);
|
||||
var descriptionMatch = StringMatcher.FuzzySearch(query, topLevelOrAppItem.Subtitle);
|
||||
var extensionTitleMatch = StringMatcher.FuzzySearch(query, extensionDisplayName);
|
||||
// StringMatcher.FuzzySearch will absolutely BEEF IT if you give it a
|
||||
// whitespace-only query.
|
||||
//
|
||||
// in that scenario, we'll just use a simple string contains for the
|
||||
// query. Maybe someone is really looking for things with a space in
|
||||
// them, I don't know.
|
||||
|
||||
// Title:
|
||||
// * whitespace query: 1 point
|
||||
// * otherwise full weight match
|
||||
var nameMatch = isWhiteSpace ?
|
||||
(title.Contains(query) ? 1 : 0) :
|
||||
StringMatcher.FuzzySearch(query, title).Score;
|
||||
|
||||
// Subtitle:
|
||||
// * whitespace query: 1/2 point
|
||||
// * otherwise ~half weight match. Minus a bit, because subtitles tend to be longer
|
||||
var descriptionMatch = isWhiteSpace ?
|
||||
(topLevelOrAppItem.Subtitle.Contains(query) ? .5 : 0) :
|
||||
(StringMatcher.FuzzySearch(query, topLevelOrAppItem.Subtitle).Score - 4) / 2.0;
|
||||
|
||||
// Extension title: despite not being visible, give the extension name itself some weight
|
||||
// * whitespace query: 0 points
|
||||
// * otherwise more weight than a subtitle, but not much
|
||||
var extensionTitleMatch = isWhiteSpace ? 0 : StringMatcher.FuzzySearch(query, extensionDisplayName).Score / 1.5;
|
||||
|
||||
var scores = new[]
|
||||
{
|
||||
nameMatch.Score,
|
||||
(descriptionMatch.Score - 4) / 2.0,
|
||||
nameMatch,
|
||||
descriptionMatch,
|
||||
isFallback ? 1 : 0, // Always give fallbacks a chance...
|
||||
};
|
||||
var max = scores.Max();
|
||||
max = max + (extensionTitleMatch.Score / 1.5);
|
||||
|
||||
// _Add_ the extension name. This will bubble items that match both
|
||||
// title and extension name up above ones that just match title.
|
||||
// e.g. "git" will up-weight "GitHub searches" from the GitHub extension
|
||||
// above "git" from "whatever"
|
||||
max = max + extensionTitleMatch;
|
||||
|
||||
// ... but downweight them
|
||||
var matchSomething = (max / (isFallback ? 3 : 1))
|
||||
@@ -221,9 +262,9 @@ public partial class MainListPage : DynamicListPage,
|
||||
|
||||
private string IdForTopLevelOrAppItem(IListItem topLevelOrAppItem)
|
||||
{
|
||||
if (topLevelOrAppItem is TopLevelCommandItemWrapper toplevel)
|
||||
if (topLevelOrAppItem is TopLevelViewModel topLevel)
|
||||
{
|
||||
return toplevel.Id;
|
||||
return topLevel.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ public partial class NewExtensionPage : ContentPage
|
||||
{
|
||||
Name = Properties.Resources.builtin_create_extension_name;
|
||||
Title = Properties.Resources.builtin_create_extension_title;
|
||||
Icon = new IconInfo("\uEA86"); // Puzzle
|
||||
Icon = IconHelpers.FromRelativePath("Assets\\CreateExtension.svg");
|
||||
|
||||
_inputForm.FormSubmitted += FormSubmitted;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ using Windows.Storage.Streams;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class IconDataViewModel : ObservableObject
|
||||
public partial class IconDataViewModel : ObservableObject, IIconData
|
||||
{
|
||||
private readonly ExtensionObject<IIconData> _model = new(null);
|
||||
|
||||
@@ -25,6 +25,8 @@ public partial class IconDataViewModel : ObservableObject
|
||||
// first. Hence why we're sticking this into an ExtensionObject
|
||||
public ExtensionObject<IRandomAccessStreamReference> Data { get; private set; } = new(null);
|
||||
|
||||
IRandomAccessStreamReference? IIconData.Data => Data.Unsafe;
|
||||
|
||||
public IconDataViewModel(IIconData? icon)
|
||||
{
|
||||
_model = new(icon);
|
||||
|
||||
@@ -8,7 +8,7 @@ using Microsoft.CommandPalette.Extensions;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class IconInfoViewModel : ObservableObject
|
||||
public partial class IconInfoViewModel : ObservableObject, IIconInfo
|
||||
{
|
||||
private readonly ExtensionObject<IIconInfo> _model = new(null);
|
||||
|
||||
@@ -28,6 +28,10 @@ public partial class IconInfoViewModel : ObservableObject
|
||||
|
||||
public bool IsSet => _model.Unsafe != null;
|
||||
|
||||
IIconData? IIconInfo.Dark => Dark;
|
||||
|
||||
IIconData? IIconInfo.Light => Light;
|
||||
|
||||
public IconInfoViewModel(IIconInfo? icon)
|
||||
{
|
||||
_model = new(icon);
|
||||
|
||||
@@ -18,16 +18,19 @@ public record PerformCommandMessage
|
||||
|
||||
public bool WithAnimation { get; set; } = true;
|
||||
|
||||
public CommandPaletteHost? ExtensionHost { get; private set; }
|
||||
|
||||
public PerformCommandMessage(ExtensionObject<ICommand> command)
|
||||
{
|
||||
Command = command;
|
||||
Context = null;
|
||||
}
|
||||
|
||||
public PerformCommandMessage(TopLevelCommandItemWrapper topLevelCommand)
|
||||
public PerformCommandMessage(TopLevelViewModel topLevelCommand)
|
||||
{
|
||||
Command = new(topLevelCommand.Command);
|
||||
Command = topLevelCommand.CommandViewModel.Model;
|
||||
Context = null;
|
||||
ExtensionHost = topLevelCommand.ExtensionHost;
|
||||
}
|
||||
|
||||
public PerformCommandMessage(ExtensionObject<ICommand> command, ExtensionObject<IListItem> context)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// 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.UI.ViewModels.Messages;
|
||||
|
||||
public record SettingsWindowClosedMessage
|
||||
{
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\CreateExtension.svg" />
|
||||
<None Remove="Assets\template.zip" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -48,11 +49,22 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\CreateExtension.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Update="Assets\CreateExtension.svg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -131,6 +131,7 @@ public class ExtensionService : IExtensionService, IDisposable
|
||||
try
|
||||
{
|
||||
_installedExtensions.RemoveAll(i => removedExtensions.Contains(i));
|
||||
_enabledExtensions.RemoveAll(i => removedExtensions.Contains(i));
|
||||
|
||||
OnExtensionRemoved?.Invoke(this, removedExtensions);
|
||||
}
|
||||
|
||||
@@ -106,14 +106,14 @@ public class ExtensionWrapper : IExtensionWrapper
|
||||
{
|
||||
Logger.LogDebug($"Starting {ExtensionDisplayName} ({ExtensionClassId})");
|
||||
|
||||
nint extensionPtr = nint.Zero;
|
||||
var extensionPtr = nint.Zero;
|
||||
try
|
||||
{
|
||||
// -2147024809: E_INVALIDARG
|
||||
// -2147467262: E_NOINTERFACE
|
||||
// -2147024893: E_PATH_NOT_FOUND
|
||||
Guid guid = typeof(IExtension).GUID;
|
||||
global::Windows.Win32.Foundation.HRESULT hr = PInvoke.CoCreateInstance(Guid.Parse(ExtensionClassId), null, CLSCTX.CLSCTX_LOCAL_SERVER, guid, out object? extensionObj);
|
||||
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)
|
||||
{
|
||||
@@ -179,7 +179,7 @@ public class ExtensionWrapper : IExtensionWrapper
|
||||
{
|
||||
await StartExtensionAsync();
|
||||
|
||||
object? supportedProviders = GetExtensionObject()?.GetProvider(_providerTypeMap[typeof(T)]);
|
||||
var supportedProviders = GetExtensionObject()?.GetProvider(_providerTypeMap[typeof(T)]);
|
||||
if (supportedProviders is IEnumerable<T> multipleProvidersSupported)
|
||||
{
|
||||
return multipleProvidersSupported;
|
||||
|
||||
@@ -303,6 +303,15 @@ namespace Microsoft.CmdPal.UI.ViewModels.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Disabled.
|
||||
/// </summary>
|
||||
public static string builtin_disabled_extension {
|
||||
get {
|
||||
return ResourceManager.GetString("builtin_disabled_extension", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Built-in commands.
|
||||
/// </summary>
|
||||
|
||||
@@ -236,4 +236,7 @@
|
||||
<data name="builtin_reload_display_title" xml:space="preserve">
|
||||
<value>Reload Command Palette extensions</value>
|
||||
</data>
|
||||
<data name="builtin_disabled_extension" xml:space="preserve">
|
||||
<value>Disabled</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -10,23 +10,14 @@ public class ProviderSettings
|
||||
{
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
[JsonIgnore]
|
||||
public string PackageFamilyName { get; set; } = string.Empty;
|
||||
|
||||
[JsonIgnore]
|
||||
public string Id { get; set; } = string.Empty;
|
||||
|
||||
[JsonIgnore]
|
||||
public string ProviderDisplayName { get; set; } = string.Empty;
|
||||
|
||||
// Originally, I wanted to do:
|
||||
// public string ProviderId => $"{PackageFamilyName}/{ProviderDisplayName}";
|
||||
// but I think that's actually a bad idea, because the Display Name can be localized.
|
||||
[JsonIgnore]
|
||||
public string ProviderId => $"{PackageFamilyName}/{Id}";
|
||||
public string ProviderId { get; private set; } = string.Empty;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsBuiltin => string.IsNullOrEmpty(PackageFamilyName);
|
||||
public bool IsBuiltin { get; private set; }
|
||||
|
||||
public ProviderSettings(CommandProviderWrapper wrapper)
|
||||
{
|
||||
@@ -41,10 +32,12 @@ public class ProviderSettings
|
||||
|
||||
public void Connect(CommandProviderWrapper wrapper)
|
||||
{
|
||||
PackageFamilyName = wrapper.Extension?.PackageFamilyName ?? string.Empty;
|
||||
Id = wrapper.DisplayName;
|
||||
ProviderId = wrapper.ProviderId;
|
||||
IsBuiltin = wrapper.Extension == null;
|
||||
|
||||
ProviderDisplayName = wrapper.DisplayName;
|
||||
if (ProviderId == "/")
|
||||
|
||||
if (string.IsNullOrEmpty(ProviderId))
|
||||
{
|
||||
throw new InvalidDataException("Did you add a built-in command and forget to set the Id? Make sure you do that!");
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Properties;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
@@ -14,14 +17,13 @@ public partial class ProviderSettingsViewModel(
|
||||
ProviderSettings _providerSettings,
|
||||
IServiceProvider _serviceProvider) : ObservableObject
|
||||
{
|
||||
private readonly TopLevelCommandManager _tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
|
||||
private readonly SettingsModel _settings = _serviceProvider.GetService<SettingsModel>()!;
|
||||
|
||||
public string DisplayName => _provider.DisplayName;
|
||||
|
||||
public string ExtensionName => _provider.Extension?.ExtensionDisplayName ?? "Built-in";
|
||||
|
||||
public string ExtensionSubtext => $"{ExtensionName}, {TopLevelCommands.Count} commands";
|
||||
public string ExtensionSubtext => IsEnabled ? $"{ExtensionName}, {TopLevelCommands.Count} commands" : Resources.builtin_disabled_extension;
|
||||
|
||||
[MemberNotNullWhen(true, nameof(Extension))]
|
||||
public bool IsFromExtension => _provider.Extension != null;
|
||||
@@ -35,7 +37,29 @@ public partial class ProviderSettingsViewModel(
|
||||
public bool IsEnabled
|
||||
{
|
||||
get => _providerSettings.IsEnabled;
|
||||
set => _providerSettings.IsEnabled = value;
|
||||
set
|
||||
{
|
||||
if (value != _providerSettings.IsEnabled)
|
||||
{
|
||||
_providerSettings.IsEnabled = value;
|
||||
Save();
|
||||
WeakReferenceMessenger.Default.Send<ReloadCommandsMessage>(new());
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
OnPropertyChanged(nameof(ExtensionSubtext));
|
||||
}
|
||||
|
||||
if (value == true)
|
||||
{
|
||||
_provider.CommandsChanged -= Provider_CommandsChanged;
|
||||
_provider.CommandsChanged += Provider_CommandsChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Provider_CommandsChanged(CommandProviderWrapper sender, CommandPalette.Extensions.IItemsChangedEventArgs args)
|
||||
{
|
||||
OnPropertyChanged(nameof(ExtensionSubtext));
|
||||
OnPropertyChanged(nameof(TopLevelCommands));
|
||||
}
|
||||
|
||||
public bool HasSettings => _provider.Settings != null && _provider.Settings.SettingsPage != null;
|
||||
@@ -58,24 +82,12 @@ public partial class ProviderSettingsViewModel(
|
||||
|
||||
private List<TopLevelViewModel> BuildTopLevelViewModels()
|
||||
{
|
||||
var topLevelCommands = _tlcManager.TopLevelCommands;
|
||||
var thisProvider = _provider;
|
||||
var providersCommands = thisProvider.TopLevelItems;
|
||||
List<TopLevelViewModel> results = [];
|
||||
|
||||
// Remember! This comes in on the UI thread!
|
||||
// TODO: GH #426
|
||||
// Probably just do a background InitializeProperties
|
||||
// Or better yet, merge TopLevelCommandWrapper and TopLevelViewModel
|
||||
foreach (var command in providersCommands)
|
||||
{
|
||||
var match = topLevelCommands.Where(tlc => tlc.Model.Unsafe == command).FirstOrDefault();
|
||||
if (match != null)
|
||||
{
|
||||
results.Add(new(match, _settings, _serviceProvider));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
return [.. providersCommands];
|
||||
}
|
||||
|
||||
private void Save() => SettingsModel.SaveSettings(_settings);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ public partial class SettingsModel : ObservableObject
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// SETTINGS HERE
|
||||
public HotkeySettings? Hotkey { get; set; } = new HotkeySettings(true, false, true, false, 0x20); // win+alt+space
|
||||
public static HotkeySettings DefaultActivationShortcut { get; } = new HotkeySettings(true, false, true, false, 0x20); // win+alt+space
|
||||
|
||||
public HotkeySettings? Hotkey { get; set; } = DefaultActivationShortcut;
|
||||
|
||||
public bool ShowAppDetails { get; set; }
|
||||
|
||||
@@ -50,6 +52,23 @@ public partial class SettingsModel : ObservableObject
|
||||
FilePath = SettingsJsonPath();
|
||||
}
|
||||
|
||||
public ProviderSettings GetProviderSettings(CommandProviderWrapper provider)
|
||||
{
|
||||
ProviderSettings? settings;
|
||||
if (!ProviderSettings.TryGetValue(provider.ProviderId, out settings))
|
||||
{
|
||||
settings = new ProviderSettings(provider);
|
||||
settings.Connect(provider);
|
||||
ProviderSettings[provider.ProviderId] = settings;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Connect(provider);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
public static SettingsModel LoadSettings()
|
||||
{
|
||||
if (string.IsNullOrEmpty(FilePath))
|
||||
|
||||
@@ -3,22 +3,26 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class SettingsViewModel
|
||||
public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
public HotkeySettings? Hotkey
|
||||
{
|
||||
get => _settings.Hotkey;
|
||||
set
|
||||
{
|
||||
_settings.Hotkey = value;
|
||||
_settings.Hotkey = value ?? SettingsModel.DefaultActivationShortcut;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Hotkey)));
|
||||
Save();
|
||||
}
|
||||
}
|
||||
@@ -95,18 +99,7 @@ public partial class SettingsViewModel
|
||||
|
||||
foreach (var item in activeProviders)
|
||||
{
|
||||
if (!allProviderSettings.TryGetValue(item.ProviderId, out var value))
|
||||
{
|
||||
allProviderSettings[item.ProviderId] = new ProviderSettings(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.Connect(item);
|
||||
}
|
||||
|
||||
var providerSettings = allProviderSettings.TryGetValue(item.ProviderId, out var value2) ?
|
||||
value2 :
|
||||
new ProviderSettings(item);
|
||||
var providerSettings = settings.GetProviderSettings(item);
|
||||
|
||||
var settingsModel = new ProviderSettingsViewModel(item, providerSettings, _serviceProvider);
|
||||
CommandProviders.Add(settingsModel);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// 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.Runtime.Versioning;
|
||||
using CommunityToolkit.Common;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -13,7 +15,7 @@ using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.Win32;
|
||||
using WinRT;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
@@ -148,14 +150,16 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched
|
||||
// need to handle that
|
||||
_activeExtension = extension;
|
||||
|
||||
var extensionComObject = _activeExtension?.GetExtensionObject();
|
||||
if (extensionComObject != null)
|
||||
var extensionWinRtObject = _activeExtension?.GetExtensionObject();
|
||||
if (extensionWinRtObject != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var hr = PInvoke.CoAllowSetForegroundWindow(extensionComObject);
|
||||
var winrtObj = (IWinRTObject)extensionWinRtObject;
|
||||
var intPtr = winrtObj.NativeObject.ThisPtr;
|
||||
var hr = Native.CoAllowSetForegroundWindow(intPtr);
|
||||
if (hr != 0)
|
||||
{
|
||||
Logger.LogWarning($"Error giving foreground rights: 0x{hr.Value:X8}");
|
||||
@@ -174,4 +178,18 @@ public partial class ShellViewModel(IServiceProvider _serviceProvider, TaskSched
|
||||
{
|
||||
SetActiveExtension(null);
|
||||
}
|
||||
|
||||
// You may ask yourself, why aren't we using CsWin32 for this?
|
||||
// The CsWin32 projected version includes some object marshalling, like so:
|
||||
//
|
||||
// HRESULT CoAllowSetForegroundWindow([MarshalAs(UnmanagedType.IUnknown)] object pUnk,...)
|
||||
//
|
||||
// And if you do it like that, then the IForegroundTransfer interface isn't marshalled correctly
|
||||
internal sealed class Native
|
||||
{
|
||||
[DllImport("OLE32.dll", ExactSpelling = true)]
|
||||
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
|
||||
[SupportedOSPlatform("windows5.0")]
|
||||
internal static extern unsafe global::Windows.Win32.Foundation.HRESULT CoAllowSetForegroundWindow(nint pUnk, [Optional] void* lpvReserved);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,224 +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 CommunityToolkit.Mvvm.Messaging;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WyHash;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Abstraction of a top-level command. Currently owns just a live ICommandItem
|
||||
/// from an extension (or in-proc command provider), but in the future will
|
||||
/// also support stub top-level items.
|
||||
/// </summary>
|
||||
public partial class TopLevelCommandItemWrapper : ListItem
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly string _commandProviderId;
|
||||
|
||||
public ExtensionObject<ICommandItem> Model { get; }
|
||||
|
||||
public bool IsFallback { get; private set; }
|
||||
|
||||
private readonly string _idFromModel = string.Empty;
|
||||
private string _generatedId = string.Empty;
|
||||
|
||||
public string Id => string.IsNullOrEmpty(_idFromModel) ? _generatedId : _idFromModel;
|
||||
|
||||
private readonly TopLevelCommandWrapper _topLevelCommand;
|
||||
|
||||
public CommandAlias? Alias { get; private set; }
|
||||
|
||||
private HotkeySettings? _hotkey;
|
||||
|
||||
public HotkeySettings? Hotkey
|
||||
{
|
||||
get => _hotkey;
|
||||
set
|
||||
{
|
||||
UpdateHotkey();
|
||||
UpdateTags();
|
||||
}
|
||||
}
|
||||
|
||||
public CommandPaletteHost ExtensionHost { get => _topLevelCommand.ExtensionHost; }
|
||||
|
||||
public TopLevelCommandItemWrapper(
|
||||
ExtensionObject<ICommandItem> commandItem,
|
||||
bool isFallback,
|
||||
CommandPaletteHost extensionHost,
|
||||
string commandProviderId,
|
||||
IServiceProvider serviceProvider)
|
||||
: base(new TopLevelCommandWrapper(
|
||||
commandItem.Unsafe?.Command ?? new NoOpCommand(),
|
||||
extensionHost))
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_topLevelCommand = (TopLevelCommandWrapper)this.Command!;
|
||||
_commandProviderId = commandProviderId;
|
||||
|
||||
IsFallback = isFallback;
|
||||
|
||||
// TODO: In reality, we should do an async fetch when we're created
|
||||
// from an extension object. Probably have an
|
||||
// `static async Task<TopLevelCommandWrapper> FromExtension(ExtensionObject<ICommandItem>)`
|
||||
// or a
|
||||
// `async Task PromoteStub(ExtensionObject<ICommandItem>)`
|
||||
Model = commandItem;
|
||||
try
|
||||
{
|
||||
var model = Model.Unsafe;
|
||||
if (model == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_topLevelCommand.UnsafeInitializeProperties();
|
||||
|
||||
_idFromModel = _topLevelCommand.Id;
|
||||
|
||||
Title = model.Title;
|
||||
Subtitle = model.Subtitle;
|
||||
Icon = model.Icon;
|
||||
MoreCommands = model.MoreCommands;
|
||||
|
||||
model.PropChanged += Model_PropChanged;
|
||||
_topLevelCommand.PropChanged += Model_PropChanged;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
GenerateId();
|
||||
|
||||
UpdateAlias();
|
||||
UpdateHotkey();
|
||||
UpdateTags();
|
||||
}
|
||||
|
||||
private void GenerateId()
|
||||
{
|
||||
// Use WyHash64 to generate stable ID hashes.
|
||||
// manually seeding with 0, so that the hash is stable across launches
|
||||
var result = WyHash64.ComputeHash64(_commandProviderId + Title + Subtitle, seed: 0);
|
||||
_generatedId = $"{_commandProviderId}{result}";
|
||||
}
|
||||
|
||||
public void UpdateAlias(CommandAlias? newAlias)
|
||||
{
|
||||
_serviceProvider.GetService<AliasManager>()!.UpdateAlias(Id, newAlias);
|
||||
UpdateAlias();
|
||||
UpdateTags();
|
||||
}
|
||||
|
||||
private void UpdateAlias()
|
||||
{
|
||||
// Add tags for the alias, if we have one.
|
||||
var aliases = _serviceProvider.GetService<AliasManager>();
|
||||
if (aliases != null)
|
||||
{
|
||||
Alias = aliases.AliasFromId(Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHotkey()
|
||||
{
|
||||
var settings = _serviceProvider.GetService<SettingsModel>()!;
|
||||
var hotkey = settings.CommandHotkeys.Where(hk => hk.CommandId == Id).FirstOrDefault();
|
||||
if (hotkey != null)
|
||||
{
|
||||
_hotkey = hotkey.Hotkey;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTags()
|
||||
{
|
||||
var tags = new List<Tag>();
|
||||
|
||||
if (Hotkey != null)
|
||||
{
|
||||
tags.Add(new Tag() { Text = Hotkey.ToString() });
|
||||
}
|
||||
|
||||
if (Alias != null)
|
||||
{
|
||||
tags.Add(new Tag() { Text = Alias.SearchPrefix });
|
||||
}
|
||||
|
||||
this.Tags = tags.ToArray();
|
||||
}
|
||||
|
||||
private void Model_PropChanged(object sender, IPropChangedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var propertyName = args.PropertyName;
|
||||
var model = Model.Unsafe;
|
||||
if (model == null)
|
||||
{
|
||||
return; // throw?
|
||||
}
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case nameof(_topLevelCommand.Name):
|
||||
case nameof(Title):
|
||||
this.Title = model.Title;
|
||||
break;
|
||||
case nameof(Subtitle):
|
||||
this.Subtitle = model.Subtitle;
|
||||
break;
|
||||
case nameof(Icon):
|
||||
var listIcon = model.Icon;
|
||||
Icon = model.Icon;
|
||||
break;
|
||||
case nameof(MoreCommands):
|
||||
this.MoreCommands = model.MoreCommands;
|
||||
break;
|
||||
case nameof(Command):
|
||||
this.Command = model.Command;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void TryUpdateFallbackText(string newQuery)
|
||||
{
|
||||
if (!IsFallback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var model = Model.Unsafe;
|
||||
if (model is IFallbackCommandItem fallback)
|
||||
{
|
||||
var wasEmpty = string.IsNullOrEmpty(Title);
|
||||
fallback.FallbackHandler.UpdateQuery(newQuery);
|
||||
var isEmpty = string.IsNullOrEmpty(Title);
|
||||
if (wasEmpty != isEmpty)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<UpdateFallbackItemsMessage>();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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.Immutable;
|
||||
using System.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
@@ -16,7 +17,8 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class TopLevelCommandManager : ObservableObject,
|
||||
IRecipient<ReloadCommandsMessage>
|
||||
IRecipient<ReloadCommandsMessage>,
|
||||
IPageContext
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly TaskScheduler _taskScheduler;
|
||||
@@ -24,6 +26,8 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
private readonly List<CommandProviderWrapper> _builtInCommands = [];
|
||||
private readonly List<CommandProviderWrapper> _extensionCommandProviders = [];
|
||||
|
||||
TaskScheduler IPageContext.Scheduler => _taskScheduler;
|
||||
|
||||
public TopLevelCommandManager(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
@@ -31,7 +35,7 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
WeakReferenceMessenger.Default.Register<ReloadCommandsMessage>(this);
|
||||
}
|
||||
|
||||
public ObservableCollection<TopLevelCommandItemWrapper> TopLevelCommands { get; set; } = [];
|
||||
public ObservableCollection<TopLevelViewModel> TopLevelCommands { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool IsLoading { get; private set; } = true;
|
||||
@@ -58,35 +62,43 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
// May be called from a background thread
|
||||
private async Task LoadTopLevelCommandsFromProvider(CommandProviderWrapper commandProvider)
|
||||
{
|
||||
await commandProvider.LoadTopLevelCommands();
|
||||
WeakReference<IPageContext> weakSelf = new(this);
|
||||
|
||||
await commandProvider.LoadTopLevelCommands(_serviceProvider, weakSelf);
|
||||
|
||||
var settings = _serviceProvider.GetService<SettingsModel>()!;
|
||||
var makeAndAdd = (ICommandItem? i, bool fallback) =>
|
||||
{
|
||||
TopLevelCommandItemWrapper wrapper = new(
|
||||
new(i), fallback, commandProvider.ExtensionHost, commandProvider.ProviderId, _serviceProvider);
|
||||
var commandItemViewModel = new CommandItemViewModel(new(i), weakSelf);
|
||||
var topLevelViewModel = new TopLevelViewModel(commandItemViewModel, fallback, commandProvider.ExtensionHost, commandProvider.ProviderId, settings, _serviceProvider);
|
||||
|
||||
lock (TopLevelCommands)
|
||||
{
|
||||
TopLevelCommands.Add(wrapper);
|
||||
TopLevelCommands.Add(topLevelViewModel);
|
||||
}
|
||||
};
|
||||
|
||||
await Task.Factory.StartNew(
|
||||
() =>
|
||||
{
|
||||
foreach (var i in commandProvider.TopLevelItems)
|
||||
lock (TopLevelCommands)
|
||||
{
|
||||
makeAndAdd(i, false);
|
||||
}
|
||||
foreach (var item in commandProvider.TopLevelItems)
|
||||
{
|
||||
TopLevelCommands.Add(item);
|
||||
}
|
||||
|
||||
foreach (var i in commandProvider.FallbackItems)
|
||||
{
|
||||
makeAndAdd(i, true);
|
||||
foreach (var item in commandProvider.FallbackItems)
|
||||
{
|
||||
TopLevelCommands.Add(item);
|
||||
}
|
||||
}
|
||||
},
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
_taskScheduler);
|
||||
|
||||
commandProvider.CommandsChanged -= CommandProvider_CommandsChanged;
|
||||
commandProvider.CommandsChanged += CommandProvider_CommandsChanged;
|
||||
}
|
||||
|
||||
@@ -108,8 +120,8 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
{
|
||||
// Work on a clone of the list, so that we can just do one atomic
|
||||
// update to the actual observable list at the end
|
||||
List<TopLevelCommandItemWrapper> clone = [.. TopLevelCommands];
|
||||
List<TopLevelCommandItemWrapper> newItems = [];
|
||||
List<TopLevelViewModel> clone = [.. TopLevelCommands];
|
||||
List<TopLevelViewModel> newItems = [];
|
||||
var startIndex = -1;
|
||||
var firstCommand = sender.TopLevelItems[0];
|
||||
var commandsToRemove = sender.TopLevelItems.Length + sender.FallbackItems.Length;
|
||||
@@ -122,15 +134,11 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
var wrapper = clone[i];
|
||||
try
|
||||
{
|
||||
var thisCommand = wrapper.Model.Unsafe;
|
||||
if (thisCommand != null)
|
||||
var isTheSame = wrapper == firstCommand;
|
||||
if (isTheSame)
|
||||
{
|
||||
var isTheSame = thisCommand == firstCommand;
|
||||
if (isTheSame)
|
||||
{
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
startIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -138,16 +146,21 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
}
|
||||
}
|
||||
|
||||
WeakReference<IPageContext> weakSelf = new(this);
|
||||
|
||||
// Fetch the new items
|
||||
await sender.LoadTopLevelCommands();
|
||||
await sender.LoadTopLevelCommands(_serviceProvider, weakSelf);
|
||||
|
||||
var settings = _serviceProvider.GetService<SettingsModel>()!;
|
||||
|
||||
foreach (var i in sender.TopLevelItems)
|
||||
{
|
||||
newItems.Add(new(new(i), false, sender.ExtensionHost, sender.ProviderId, _serviceProvider));
|
||||
newItems.Add(i);
|
||||
}
|
||||
|
||||
foreach (var i in sender.FallbackItems)
|
||||
{
|
||||
newItems.Add(new(new(i), true, sender.ExtensionHost, sender.ProviderId, _serviceProvider));
|
||||
newItems.Add(i);
|
||||
}
|
||||
|
||||
// Slice out the old commands
|
||||
@@ -197,7 +210,7 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
extensionService.OnExtensionAdded -= ExtensionService_OnExtensionAdded;
|
||||
extensionService.OnExtensionRemoved -= ExtensionService_OnExtensionRemoved;
|
||||
|
||||
var extensions = await extensionService.GetInstalledExtensionsAsync();
|
||||
var extensions = (await extensionService.GetInstalledExtensionsAsync()).ToImmutableList();
|
||||
_extensionCommandProviders.Clear();
|
||||
if (extensions != null)
|
||||
{
|
||||
@@ -229,6 +242,7 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
// TODO This most definitely needs a lock
|
||||
foreach (var extension in extensions)
|
||||
{
|
||||
Logger.LogDebug($"Starting {extension.PackageFullName}");
|
||||
try
|
||||
{
|
||||
// start it ...
|
||||
@@ -253,7 +267,7 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
async () =>
|
||||
{
|
||||
// Then find all the top-level commands that belonged to that extension
|
||||
List<TopLevelCommandItemWrapper> commandsToRemove = [];
|
||||
List<TopLevelViewModel> commandsToRemove = [];
|
||||
lock (TopLevelCommands)
|
||||
{
|
||||
foreach (var extension in extensions)
|
||||
@@ -292,7 +306,7 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
});
|
||||
}
|
||||
|
||||
public TopLevelCommandItemWrapper? LookupCommand(string id)
|
||||
public TopLevelViewModel? LookupCommand(string id)
|
||||
{
|
||||
lock (TopLevelCommands)
|
||||
{
|
||||
@@ -310,4 +324,10 @@ public partial class TopLevelCommandManager : ObservableObject,
|
||||
|
||||
public void Receive(ReloadCommandsMessage message) =>
|
||||
ReloadAllCommandsAsync().ConfigureAwait(false);
|
||||
|
||||
void IPageContext.ShowException(Exception ex, string? extensionHint)
|
||||
{
|
||||
var errorMessage = $"A bug occurred in {$"the \"{extensionHint}\"" ?? "an unknown's"} extension's code:\n{ex.Message}\n{ex.Source}\n{ex.StackTrace}\n\n";
|
||||
CommandPaletteHost.Instance.Log(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +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.UI.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class TopLevelCommandWrapper : ICommand
|
||||
{
|
||||
private readonly ExtensionObject<ICommand> _command;
|
||||
|
||||
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged;
|
||||
|
||||
public string Name { get; private set; } = string.Empty;
|
||||
|
||||
public string Id { get; private set; } = string.Empty;
|
||||
|
||||
public IIconInfo Icon { get; private set; } = new IconInfo(null);
|
||||
|
||||
public ICommand Command => _command.Unsafe!;
|
||||
|
||||
public CommandPaletteHost ExtensionHost { get; }
|
||||
|
||||
public TopLevelCommandWrapper(ICommand command, CommandPaletteHost extensionHost)
|
||||
{
|
||||
_command = new(command);
|
||||
ExtensionHost = extensionHost;
|
||||
}
|
||||
|
||||
public void UnsafeInitializeProperties()
|
||||
{
|
||||
var model = _command.Unsafe!;
|
||||
|
||||
Name = model.Name;
|
||||
Id = model.Id;
|
||||
Icon = model.Icon;
|
||||
|
||||
model.PropChanged += Model_PropChanged;
|
||||
model.PropChanged += this.PropChanged;
|
||||
}
|
||||
|
||||
private void Model_PropChanged(object sender, IPropChangedEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var propertyName = args.PropertyName;
|
||||
var model = _command.Unsafe;
|
||||
if (model == null)
|
||||
{
|
||||
return; // throw?
|
||||
}
|
||||
|
||||
switch (propertyName)
|
||||
{
|
||||
case nameof(Name):
|
||||
this.Name = model.Name;
|
||||
break;
|
||||
case nameof(Icon):
|
||||
var listIcon = model.Icon;
|
||||
Icon = model.Icon;
|
||||
break;
|
||||
}
|
||||
|
||||
PropChanged?.Invoke(this, args);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,95 +2,279 @@
|
||||
// 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 ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Settings;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Windows.Foundation;
|
||||
using WyHash;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public sealed partial class TopLevelViewModel : ObservableObject
|
||||
public sealed partial class TopLevelViewModel : ObservableObject, IListItem
|
||||
{
|
||||
private readonly SettingsModel _settings;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly CommandItemViewModel _commandItemViewModel;
|
||||
|
||||
// TopLevelCommandItemWrapper is a ListItem, but it's in-memory for the app already.
|
||||
// We construct it either from data that we pulled from the cache, or from the
|
||||
// extension, but the data in it is all in our process now.
|
||||
private readonly TopLevelCommandItemWrapper _item;
|
||||
private readonly string _commandProviderId;
|
||||
|
||||
public IconInfoViewModel Icon { get; private set; }
|
||||
private string IdFromModel => _commandItemViewModel.Command.Id;
|
||||
|
||||
public string Title => _item.Title;
|
||||
private string _generatedId = string.Empty;
|
||||
|
||||
public string Subtitle => _item.Subtitle;
|
||||
private HotkeySettings? _hotkey;
|
||||
|
||||
private CommandAlias? Alias { get; set; }
|
||||
|
||||
public bool IsFallback { get; private set; }
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<Tag> Tags { get; set; } = [];
|
||||
|
||||
public string Id => string.IsNullOrEmpty(IdFromModel) ? _generatedId : IdFromModel;
|
||||
|
||||
public CommandPaletteHost ExtensionHost { get; private set; }
|
||||
|
||||
public CommandViewModel CommandViewModel => _commandItemViewModel.Command;
|
||||
|
||||
public CommandItemViewModel ItemViewModel => _commandItemViewModel;
|
||||
|
||||
////// ICommandItem
|
||||
public string Title => _commandItemViewModel.Title;
|
||||
|
||||
public string Subtitle => _commandItemViewModel.Subtitle;
|
||||
|
||||
public IIconInfo Icon => _commandItemViewModel.Icon;
|
||||
|
||||
ICommand? ICommandItem.Command => _commandItemViewModel.Command.Model.Unsafe;
|
||||
|
||||
IContextItem?[] ICommandItem.MoreCommands => _commandItemViewModel.MoreCommands.Select(i => i.Model.Unsafe).ToArray();
|
||||
|
||||
////// IListItem
|
||||
ITag[] IListItem.Tags => Tags.ToArray();
|
||||
|
||||
IDetails? IListItem.Details => null;
|
||||
|
||||
string IListItem.Section => string.Empty;
|
||||
|
||||
string IListItem.TextToSuggest => string.Empty;
|
||||
|
||||
////// INotifyPropChanged
|
||||
public event TypedEventHandler<object, IPropChangedEventArgs>? PropChanged;
|
||||
|
||||
public HotkeySettings? Hotkey
|
||||
{
|
||||
get => _item.Hotkey;
|
||||
get => _hotkey;
|
||||
set
|
||||
{
|
||||
_serviceProvider.GetService<HotkeyManager>()!.UpdateHotkey(_item.Id, value);
|
||||
_item.Hotkey = value;
|
||||
_serviceProvider.GetService<HotkeyManager>()!.UpdateHotkey(Id, value);
|
||||
UpdateHotkey();
|
||||
UpdateTags();
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
private string _aliasText;
|
||||
public bool HasAlias => !string.IsNullOrEmpty(AliasText);
|
||||
|
||||
public string AliasText
|
||||
{
|
||||
get => _aliasText;
|
||||
get => Alias?.Alias ?? string.Empty;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _aliasText, value))
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
UpdateAlias();
|
||||
Alias = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Alias is CommandAlias a)
|
||||
{
|
||||
a.Alias = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Alias = new CommandAlias(value, Id);
|
||||
}
|
||||
}
|
||||
|
||||
HandleChangeAlias();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isDirectAlias;
|
||||
|
||||
public bool IsDirectAlias
|
||||
{
|
||||
get => _isDirectAlias;
|
||||
get => Alias?.IsDirect ?? false;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isDirectAlias, value))
|
||||
if (Alias is CommandAlias a)
|
||||
{
|
||||
UpdateAlias();
|
||||
a.IsDirect = value;
|
||||
}
|
||||
|
||||
HandleChangeAlias();
|
||||
}
|
||||
}
|
||||
|
||||
public TopLevelViewModel(TopLevelCommandItemWrapper item, SettingsModel settings, IServiceProvider serviceProvider)
|
||||
public TopLevelViewModel(
|
||||
CommandItemViewModel item,
|
||||
bool isFallback,
|
||||
CommandPaletteHost extensionHost,
|
||||
string commandProviderId,
|
||||
SettingsModel settings,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_settings = settings;
|
||||
_commandProviderId = commandProviderId;
|
||||
_commandItemViewModel = item;
|
||||
|
||||
_item = item;
|
||||
Icon = new(item.Icon ?? item.Command?.Icon);
|
||||
Icon.InitializeProperties();
|
||||
IsFallback = isFallback;
|
||||
ExtensionHost = extensionHost;
|
||||
|
||||
var aliases = _serviceProvider.GetService<AliasManager>()!;
|
||||
_isDirectAlias = _item.Alias?.IsDirect ?? false;
|
||||
_aliasText = _item.Alias?.Alias ?? string.Empty;
|
||||
item.PropertyChanged += Item_PropertyChanged;
|
||||
|
||||
// UpdateAlias();
|
||||
// UpdateHotkey();
|
||||
// UpdateTags();
|
||||
}
|
||||
|
||||
private void Item_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.PropertyName))
|
||||
{
|
||||
PropChanged?.Invoke(this, new PropChangedEventArgs(e.PropertyName));
|
||||
|
||||
if (e.PropertyName == "IsInitialized")
|
||||
{
|
||||
GenerateId();
|
||||
|
||||
UpdateAlias();
|
||||
UpdateHotkey();
|
||||
UpdateTags();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Save() => SettingsModel.SaveSettings(_settings);
|
||||
|
||||
private void UpdateAlias()
|
||||
private void HandleChangeAlias()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_aliasText))
|
||||
{
|
||||
_item.UpdateAlias(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
var newAlias = new CommandAlias(_aliasText, _item.Id, _isDirectAlias);
|
||||
_item.UpdateAlias(newAlias);
|
||||
}
|
||||
|
||||
SetAlias(Alias);
|
||||
Save();
|
||||
}
|
||||
|
||||
public void SetAlias(CommandAlias? newAlias)
|
||||
{
|
||||
_serviceProvider.GetService<AliasManager>()!.UpdateAlias(Id, newAlias);
|
||||
UpdateAlias();
|
||||
UpdateTags();
|
||||
}
|
||||
|
||||
private void UpdateAlias()
|
||||
{
|
||||
// Add tags for the alias, if we have one.
|
||||
var aliases = _serviceProvider.GetService<AliasManager>();
|
||||
if (aliases != null)
|
||||
{
|
||||
Alias = aliases.AliasFromId(Id);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHotkey()
|
||||
{
|
||||
var hotkey = _settings.CommandHotkeys.Where(hk => hk.CommandId == Id).FirstOrDefault();
|
||||
if (hotkey != null)
|
||||
{
|
||||
_hotkey = hotkey.Hotkey;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTags()
|
||||
{
|
||||
List<Tag> tags = new();
|
||||
|
||||
if (Hotkey != null)
|
||||
{
|
||||
tags.Add(new Tag() { Text = Hotkey.ToString() });
|
||||
}
|
||||
|
||||
if (Alias != null)
|
||||
{
|
||||
tags.Add(new Tag() { Text = Alias.SearchPrefix });
|
||||
}
|
||||
|
||||
DoOnUiThread(
|
||||
() =>
|
||||
{
|
||||
ListHelpers.InPlaceUpdateList(Tags, tags);
|
||||
PropChanged?.Invoke(this, new PropChangedEventArgs(nameof(Tags)));
|
||||
});
|
||||
}
|
||||
|
||||
private void GenerateId()
|
||||
{
|
||||
// Use WyHash64 to generate stable ID hashes.
|
||||
// manually seeding with 0, so that the hash is stable across launches
|
||||
var result = WyHash64.ComputeHash64(_commandProviderId + Title + Subtitle, seed: 0);
|
||||
_generatedId = $"{_commandProviderId}{result}";
|
||||
}
|
||||
|
||||
private void DoOnUiThread(Action action)
|
||||
{
|
||||
if (_commandItemViewModel.PageContext.TryGetTarget(out var pageContext))
|
||||
{
|
||||
Task.Factory.StartNew(
|
||||
action,
|
||||
CancellationToken.None,
|
||||
TaskCreationOptions.None,
|
||||
pageContext.Scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool SafeUpdateFallbackTextSynchronous(string newQuery)
|
||||
{
|
||||
if (!IsFallback)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return UnsafeUpdateFallbackSynchronous(newQuery);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls UpdateQuery on our command, if we're a fallback item. This does
|
||||
/// RPC work, so make sure you're calling it on a BG thread.
|
||||
/// </summary>
|
||||
/// <param name="newQuery">The new search text to pass to the extension</param>
|
||||
/// <returns>true if our Title changed across this call</returns>
|
||||
private bool UnsafeUpdateFallbackSynchronous(string newQuery)
|
||||
{
|
||||
var model = _commandItemViewModel.Model.Unsafe;
|
||||
|
||||
// RPC to check type
|
||||
if (model is IFallbackCommandItem fallback)
|
||||
{
|
||||
var wasEmpty = string.IsNullOrEmpty(Title);
|
||||
|
||||
// RPC for method
|
||||
fallback.FallbackHandler.UpdateQuery(newQuery);
|
||||
var isEmpty = string.IsNullOrEmpty(Title);
|
||||
return wasEmpty != isEmpty;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.Common.Helpers;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.Ext.Apps;
|
||||
using Microsoft.CmdPal.Ext.Bookmarks;
|
||||
@@ -22,6 +24,7 @@ using Microsoft.CmdPal.UI.ViewModels.BuiltinCommands;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
@@ -40,6 +43,8 @@ public partial class App : Application
|
||||
|
||||
public Window? AppWindow { get; private set; }
|
||||
|
||||
public ETWTrace EtwTrace { get; private set; } = new ETWTrace();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
|
||||
/// </summary>
|
||||
@@ -55,6 +60,13 @@ public partial class App : Application
|
||||
Services = ConfigureServices();
|
||||
|
||||
this.InitializeComponent();
|
||||
|
||||
NativeEventWaiter.WaitForEventLoop(
|
||||
"Local\\PowerToysCmdPal-ExitEvent-eb73f6be-3f22-4b36-aee3-62924ba40bfd", () =>
|
||||
{
|
||||
EtwTrace?.Dispose();
|
||||
Environment.Exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -64,7 +76,23 @@ public partial class App : Application
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
AppWindow = new MainWindow();
|
||||
AppWindow.Activate();
|
||||
|
||||
var cmdArgs = Environment.GetCommandLineArgs();
|
||||
|
||||
var runFromPT = false;
|
||||
foreach (var arg in cmdArgs)
|
||||
{
|
||||
if (arg == "RunFromPT")
|
||||
{
|
||||
runFromPT = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!runFromPT)
|
||||
{
|
||||
AppWindow.Activate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,9 +108,6 @@ public partial class App : Application
|
||||
|
||||
// Built-in Commands. Order matters - this is the order they'll be presented by default.
|
||||
var allApps = new AllAppsCommandProvider();
|
||||
var winget = new WinGetExtensionCommandsProvider();
|
||||
var callback = allApps.LookupApp;
|
||||
winget.SetAllLookup(callback);
|
||||
services.AddSingleton<ICommandProvider>(allApps);
|
||||
services.AddSingleton<ICommandProvider, ShellCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
|
||||
@@ -93,7 +118,25 @@ public partial class App : Application
|
||||
// services.AddSingleton<ICommandProvider, ClipboardHistoryCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WebSearchCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider>(winget);
|
||||
|
||||
// GH #38440: Users might not have WinGet installed! Or they might have
|
||||
// a ridiculously old version. Or might be running as admin.
|
||||
// We shouldn't explode in the App ctor if we fail to instantiate an
|
||||
// instance of PackageManager, which will happen in the static ctor
|
||||
// for WinGetStatics
|
||||
try
|
||||
{
|
||||
var winget = new WinGetExtensionCommandsProvider();
|
||||
var callback = allApps.LookupApp;
|
||||
winget.SetAllLookup(callback);
|
||||
services.AddSingleton<ICommandProvider>(winget);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Couldn't load winget");
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
|
||||
services.AddSingleton<ICommandProvider, WindowsTerminalCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowsSettingsCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, RegistryCommandsProvider>();
|
||||
|
||||