[NewUtility]Advanced Paste (#23)

Advanced Paste V1 implementation

---------

Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
Co-authored-by: Jordi Adoumie <98557455+joadoumie@users.noreply.github.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Craig Loewen 2024-05-09 10:32:03 -04:00 committed by Jaime Bernardo
parent c601a3e3e2
commit 483f7aa464
143 changed files with 5557 additions and 755 deletions

View File

@ -47,6 +47,7 @@ body:
multiple: true
options:
- General
- Advanced Paste
- Always on Top
- Awake
- ColorPicker
@ -64,7 +65,6 @@ body:
- Keyboard Manager
- Mouse Utilities
- Mouse Without Borders
- Paste as Plain Text
- Peek
- PowerRename
- PowerToys Run

View File

@ -21,6 +21,7 @@ body:
label: Utility with translation issue
options:
- General
- Advanced Paste
- Always on Top
- Awake
- ColorPicker
@ -38,7 +39,6 @@ body:
- Keyboard Manager
- Mouse Utilities
- Mouse Without Borders
- Paste as Plain Text
- Peek
- PowerRename
- PowerToys Run

View File

@ -25,6 +25,7 @@ WHITEONBLACK
AYUV
bak
Bcl
exa
exabyte
Gbits

View File

@ -10,7 +10,6 @@ markdownpreviewhandler
mousewithoutborders
mwb
oobe
pasteplain
poweraccent
powerlauncher
POWEROCR
@ -144,6 +143,7 @@ Controlz
cortana
fancymouse
firefox
gpt
Inkscape
Markdig
modernwpf
@ -152,6 +152,7 @@ mozilla
mspaint
Newtonsoft
onenote
openai
Quickime
regedit
roslyn

View File

@ -894,6 +894,7 @@ MINIMIZEBOX
MINIMIZEEND
MINIMIZESTART
miniz
Mip
Miracast
mjpg
mkdn

View File

@ -45,7 +45,9 @@
"PowerToys.PowerOCR.dll",
"PowerToys.PowerOCR.exe",
"PowerToys.PastePlainModuleInterface.dll",
"PowerToys.AdvancedPasteModuleInterface.dll",
"WinUI3Apps\\PowerToys.AdvancedPaste.exe",
"WinUI3Apps\\PowerToys.AdvancedPaste.dll",
"PowerToys.AwakeModuleInterface.dll",
"PowerToys.Awake.exe",
@ -303,6 +305,7 @@
"MessagePack.Annotations.dll",
"MessagePack.dll",
"Nerdbank.Streams.dll",
"WinUI3Apps\\ReverseMarkdown.dll",
"WinUI3Apps\\SharpCompress.dll",
"WinUI3Apps\\ZstdSharp.dll",
"ColorCode.Core.dll",

View File

@ -4,6 +4,7 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.12" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.0.240109" />
@ -20,6 +21,7 @@
<PackageVersion Include="DotNetSeleniumExtras.WaitHelpers" Version="3.11.0" />
<PackageVersion Include="HelixToolkit" Version="2.24.0" />
<PackageVersion Include="HelixToolkit.Core.Wpf" Version="2.24.0" />
<PackageVersion Include="HtmlAgilityPack" Version="1.11.57" />
<PackageVersion Include="hyjiacan.pinyin4net" Version="4.1.1" />
<PackageVersion Include="Interop.Microsoft.Office.Interop.OneNote" Version="1.1.0.2" />
<PackageVersion Include="LazyCache" Version="2.4.0" />
@ -56,6 +58,7 @@
<PackageVersion Include="NLog" Version="5.0.4" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
<PackageVersion Include="NLog.Schema" Version="5.2.8" />
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
<PackageVersion Include="SharpCompress" Version="0.37.2" />
<PackageVersion Include="StreamJsonRpc" Version="2.14.24" />

View File

@ -1297,6 +1297,7 @@ EXHIBIT A -Mozilla Public License.
## NuGet Packages used by PowerToys
- Appium.WebDriver 4.4.5
- Azure.AI.OpenAI 1.0.0-beta.12
- CommunityToolkit.Mvvm 8.2.2
- CommunityToolkit.WinUI.Animations 8.0.240109
- CommunityToolkit.WinUI.Collections 8.0.240109
@ -1341,6 +1342,7 @@ EXHIBIT A -Mozilla Public License.
- MSTest.TestFramework 3.2.0
- NLog.Extensions.Logging 5.3.8
- NLog.Schema 5.2.8
- ReverseMarkdown 4.1.0
- ScipBe.Common.Office.OneNote 3.0.1
- SharpCompress 0.37.2
- StreamJsonRpc 2.14.24

View File

@ -512,9 +512,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI", "src\modules\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI.UnitTests", "src\modules\MouseUtils\MouseJumpUI.UnitTests\MouseJumpUI.UnitTests.csproj", "{D9C5DE64-6849-4278-91AD-9660AECF2876}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pasteplain", "pasteplain", "{9873BA05-4C41-4819-9283-CF45D795431B}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AdvancedPaste", "AdvancedPaste", "{9873BA05-4C41-4819-9283-CF45D795431B}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PastePlainModuleInterface", "src\modules\pasteplain\PastePlainModuleInterface\PastePlainModuleInterface.vcxproj", "{FC373B24-3293-453C-AAF5-CF2909DCEE6A}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AdvancedPasteModuleInterface", "src\modules\AdvancedPaste\AdvancedPasteModuleInterface\AdvancedPasteModuleInterface.vcxproj", "{FC373B24-3293-453C-AAF5-CF2909DCEE6A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AllExperiments", "src\common\AllExperiments\AllExperiments.csproj", "{9CE59ED5-7087-4353-88EB-788038A73CEC}"
EndProject
@ -566,6 +566,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithContextMenu",
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FileLocksmithLib", "src\modules\FileLocksmith\FileLocksmithLib\FileLocksmithLib.vcxproj", "{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AdvancedPaste", "src\modules\AdvancedPaste\AdvancedPaste\AdvancedPaste.csproj", "{C32D254F-7597-4CBE-BF74-D922D81CDF29}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hosts", "src\modules\Hosts\Hosts\Hosts.csproj", "{02DD46D3-F761-47D9-8894-2D6DA0124650}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RegistryPreview", "src\modules\registrypreview\RegistryPreview\RegistryPreview.csproj", "{8E23E173-7127-4A5F-9F93-3049F2B68047}"
@ -2499,6 +2501,24 @@ Global
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}.Release|x64.Build.0 = Release|x64
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}.Release|x86.ActiveCfg = Release|x64
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F}.Release|x86.Build.0 = Release|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|ARM64.ActiveCfg = Debug|ARM64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|ARM64.Build.0 = Debug|ARM64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|ARM64.Deploy.0 = Debug|ARM64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|x64.ActiveCfg = Debug|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|x64.Build.0 = Debug|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|x64.Deploy.0 = Debug|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|x86.ActiveCfg = Debug|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|x86.Build.0 = Debug|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Debug|x86.Deploy.0 = Debug|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|ARM64.ActiveCfg = Release|ARM64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|ARM64.Build.0 = Release|ARM64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|ARM64.Deploy.0 = Release|ARM64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|x64.ActiveCfg = Release|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|x64.Build.0 = Release|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|x64.Deploy.0 = Release|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|x86.ActiveCfg = Release|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|x86.Build.0 = Release|x64
{C32D254F-7597-4CBE-BF74-D922D81CDF29}.Release|x86.Deploy.0 = Release|x64
{02DD46D3-F761-47D9-8894-2D6DA0124650}.Debug|ARM64.ActiveCfg = Debug|ARM64
{02DD46D3-F761-47D9-8894-2D6DA0124650}.Debug|ARM64.Build.0 = Debug|ARM64
{02DD46D3-F761-47D9-8894-2D6DA0124650}.Debug|x64.ActiveCfg = Debug|x64
@ -2791,6 +2811,7 @@ Global
{0014D652-901F-4456-8D65-06FC5F997FB0} = {4C0D0746-BE5B-49EE-BD5D-A7811628AE8B}
{799A50D8-DE89-4ED1-8FF8-AD5A9ED8C0CA} = {AB82E5DD-C32D-4F28-9746-2C780846188E}
{9D52FD25-EF90-4F9A-A015-91EFC5DAF54F} = {AB82E5DD-C32D-4F28-9746-2C780846188E}
{C32D254F-7597-4CBE-BF74-D922D81CDF29} = {9873BA05-4C41-4819-9283-CF45D795431B}
{02DD46D3-F761-47D9-8894-2D6DA0124650} = {F05E590D-AD46-42BE-9C25-6A63ADD2E3EA}
{8E23E173-7127-4A5F-9F93-3049F2B68047} = {929C1324-22E8-4412-A9A8-80E85F3985A5}
{DFF88D16-D36F-40A4-A955-CDCAA76EF7B8} = {538ED0BB-B863-4B20-98CC-BCDF7FA0B68A}

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" >
<?include $(sys.CURRENTDIR)\Common.wxi?>
<?define AdvancedPasteAssetsFiles=?>
<?define AdvancedPasteAssetsFilesPath=$(var.BinDir)WinUI3Apps\Assets\AdvancedPaste\?>
<Fragment>
<DirectoryRef Id="WinUI3AppsAssetsFolder">
<Directory Id="AdvancedPasteAssetsFolder" Name="AdvancedPaste" />
</DirectoryRef>
<DirectoryRef Id="AdvancedPasteAssetsFolder" FileSource="$(var.AdvancedPasteAssetsFilesPath)">
<!-- Generated by generateFileComponents.ps1 -->
<!--AdvancedPasteAssetsFiles_Component_Def-->
</DirectoryRef>
<ComponentGroup Id="AdvancedPasteComponentGroup">
<Component Id="RemoveAdvancedPasteFolder" Guid="55AFE81D-F6BD-439A-A229-66AF5C360AB0" Directory="AdvancedPasteAssetsFolder" >
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveAdvancedPasteFolder" Value="" KeyPath="yes"/>
</RegistryKey>
<RemoveFolder Id="RemoveFolderAdvancedPasteAssetsFolder" Directory="AdvancedPasteAssetsFolder" On="uninstall"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>

View File

@ -15,7 +15,7 @@
<?define MeasureToolProjectName="MeasureTool"?>
<?define HostsProjectName="Hosts"?>
<?define MouseWithoutBordersProjectName="MouseWithoutBorders"?>
<?define PastePlainProjectName="PastePlain"?>
<?define AdvancedPasteProjectName="AdvancedPaste"?>
<?define RegistryPreviewProjectName="RegistryPreview"?>
<?define PeekProjectName="Peek"?>

View File

@ -31,6 +31,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
<PropertyGroup>
<RunPostBuildEvent>Always</RunPostBuildEvent>
<PostBuildEvent>
call move /Y ..\..\..\AdvancedPaste.wxs.bk ..\..\..\AdvancedPaste.wxs
call move /Y ..\..\..\Awake.wxs.bk ..\..\..\Awake.wxs
call move /Y ..\..\..\BaseApplications.wxs.bk ..\..\..\BaseApplications.wxs
call move /Y ..\..\..\ColorPicker.wxs.bk ..\..\..\ColorPicker.wxs
@ -102,6 +103,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
<Compile Include="CustomDialogs\WixUI_PTInstallDir.wxs" />
<Compile Include="Product.wxs" />
<Compile Include="AdvancedPaste.wxs" />
<Compile Include="Awake.wxs" />
<Compile Include="BaseApplications.wxs" />
<Compile Include="ColorPicker.wxs" />

View File

@ -72,7 +72,7 @@
<ComponentGroupRef Id="VideoConferenceComponentGroup" />
<ComponentGroupRef Id="MouseWithoutBordersComponentGroup" />
<ComponentGroupRef Id="EnvironmentVariablesComponentGroup" />
<ComponentGroupRef Id="AdvancedPasteComponentGroup" />
<ComponentGroupRef Id="ResourcesComponentGroup" />
<ComponentGroupRef Id="WindowsAppSDKComponentGroup" />
<ComponentGroupRef Id="ToolComponentGroup" />
@ -161,6 +161,9 @@
<Custom Action="UnRegisterContextMenuPackages" Before="RemoveFiles">
Installed AND (REMOVE="ALL")
</Custom>
<Custom Action="UnsetAdvancedPasteAPIKey" Before="RemoveFiles">
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
<Custom Action="UninstallCommandNotFound" Before="RemoveFiles">
Installed AND (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL")
</Custom>
@ -285,6 +288,14 @@
DllEntry="UpgradeCommandNotFoundModuleCA"
/>
<CustomAction Id="UnsetAdvancedPasteAPIKey"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="UnsetAdvancedPasteAPIKeyCA"
/>
<CustomAction Id="TelemetryLogInstallSuccess"
Return="ignore"
Impersonate="yes"

View File

@ -24,6 +24,10 @@ Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListNa
Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName WinUI3ApplicationsFiles -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps"""
Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""WinUI3ApplicationsFiles"" -wxsFilePath $PSScriptRoot\WinUI3Applications.wxs -regroot $registryroot"
#AdvancedPaste
Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName AdvancedPasteAssetsFiles -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\WinUI3Apps\Assets\AdvancedPaste"""
Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""AdvancedPasteAssetsFiles"" -wxsFilePath $PSScriptRoot\AdvancedPaste.wxs -regroot $registryroot"
#AwakeFiles
Invoke-Expression -Command "$PSScriptRoot\generateFileList.ps1 -fileDepsJson """" -fileListName AwakeImagesFiles -wxsFilePath $PSScriptRoot\Awake.wxs -depsPath ""$PSScriptRoot..\..\..\$platform\Release\Assets\Awake"""
Invoke-Expression -Command "$PSScriptRoot\generateFileComponents.ps1 -fileListName ""AwakeImagesFiles"" -wxsFilePath $PSScriptRoot\Awake.wxs -regroot $registryroot"

View File

@ -14,6 +14,7 @@
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Management.Deployment.h>
#include <winrt/Windows.Security.Credentials.h>
#include <wtsapi32.h>
#include <processthreadsapi.h>
@ -433,6 +434,31 @@ UINT __stdcall RemoveWindowsServiceByName(std::wstring serviceName)
return ERROR_SUCCESS;
}
UINT __stdcall UnsetAdvancedPasteAPIKeyCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
try
{
winrt::Windows::Security::Credentials::PasswordVault vault;
winrt::Windows::Security::Credentials::PasswordCredential cred;
hr = WcaInitialize(hInstall, "UnsetAdvancedPasteAPIKey");
ExitOnFailure(hr, "Failed to initialize");
cred = vault.Retrieve(L"https://platform.openai.com/api-keys", L"PowerToys_AdvancedPaste_OpenAIKey");
vault.Remove(cred);
}
catch (...)
{
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall UninstallCommandNotFoundModuleCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
@ -1051,9 +1077,10 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
processes.resize(bytes / sizeof(processes[0]));
std::array<std::wstring_view, 31> processesToTerminate = {
std::array<std::wstring_view, 32> processesToTerminate = {
L"PowerToys.PowerLauncher.exe",
L"PowerToys.Settings.exe",
L"PowerToys.AdvancedPaste.exe",
L"PowerToys.Awake.exe",
L"PowerToys.FancyZones.exe",
L"PowerToys.FancyZonesEditor.exe",

View File

@ -24,4 +24,5 @@ EXPORTS
UninstallEmbeddedMSIXCA
UninstallServicesCA
UninstallCommandNotFoundModuleCA
UpgradeCommandNotFoundModuleCA
UpgradeCommandNotFoundModuleCA
UnsetAdvancedPasteAPIKeyCA

View File

@ -48,6 +48,7 @@
<PreBuildEvent>
<Command>
call cmd /C "copy ""$(ProjectDir)DepsFilesLists.h"" ""$(ProjectDir)DepsFilesLists.h.bk"""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\AdvancedPaste.wxs"" ""$(ProjectDir)..\PowerToysSetup\AdvancedPaste.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Awake.wxs"" ""$(ProjectDir)..\PowerToysSetup\Awake.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\BaseApplications.wxs"" ""$(ProjectDir)..\PowerToysSetup\BaseApplications.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs"" ""$(ProjectDir)..\PowerToysSetup\ColorPicker.wxs.bk""""

View File

@ -30,6 +30,7 @@ namespace Common.UI
CropAndLock,
EnvironmentVariables,
Dashboard,
AdvancedPaste,
}
private static string SettingsWindowNameToString(SettingsWindow value)
@ -74,6 +75,8 @@ namespace Common.UI
return "EnvironmentVariables";
case SettingsWindow.Dashboard:
return "Dashboard";
case SettingsWindow.AdvancedPaste:
return "AdvancedPaste";
default:
{
return string.Empty;

View File

@ -124,9 +124,9 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredTextExtractorEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredPastePlainEnabledValue()
GpoRuleConfigured GPOWrapper::GetConfiguredAdvancedPasteEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredPastePlainEnabledValue());
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredAdvancedPasteEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredVideoConferenceMuteEnabledValue()
{

View File

@ -38,7 +38,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue();
static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue();
static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue();
static GpoRuleConfigured GetConfiguredPastePlainEnabledValue();
static GpoRuleConfigured GetConfiguredAdvancedPasteEnabledValue();
static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue();
static GpoRuleConfigured GetConfiguredPeekEnabledValue();
static GpoRuleConfigured GetDisableNewUpdateToastValue();

View File

@ -42,7 +42,7 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredScreenRulerEnabledValue();
static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue();
static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue();
static GpoRuleConfigured GetConfiguredPastePlainEnabledValue();
static GpoRuleConfigured GetConfiguredAdvancedPasteEnabledValue();
static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue();
static GpoRuleConfigured GetConfiguredPeekEnabledValue();
static GpoRuleConfigured GetDisableNewUpdateToastValue();

View File

@ -47,9 +47,9 @@ namespace PowerToys.GPOWrapperProjection
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredTextExtractorEnabledValue();
}
public static GpoRuleConfigured GetConfiguredPastePlainEnabledValue()
public static GpoRuleConfigured GetConfiguredAdvancedPasteEnabledValue()
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredPastePlainEnabledValue();
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAdvancedPasteEnabledValue();
}
public static GpoRuleConfigured GetConfiguredPeekEnabledValue()

View File

@ -6,6 +6,7 @@ namespace ManagedCommon
{
public enum ModuleType
{
AdvancedPaste,
AlwaysOnTop,
Awake,
ColorPicker,
@ -21,7 +22,6 @@ namespace ManagedCommon
MouseJump,
MousePointerCrosshairs,
MouseWithoutBorders,
PastePlain,
Peek,
PowerRename,
PowerLauncher,

View File

@ -204,6 +204,18 @@ public
return gcnew String(CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT);
}
static String ^ ShowAdvancedPasteSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_ADVANCED_PASTE_SHARED_EVENT);
}
static String ^ AdvancedPasteMarkdownEvent() {
return gcnew String(CommonSharedConstants::ADVANCED_PASTE_MARKDOWN_EVENT);
}
static String ^ AdvancedPasteJsonEvent() {
return gcnew String(CommonSharedConstants::ADVANCED_PASTE_JSON_EVENT);
}
static String ^ ShowPowerOCRSharedEvent() {
return gcnew String(CommonSharedConstants::SHOW_POWEROCR_SHARED_EVENT);
}

View File

@ -25,6 +25,13 @@ namespace CommonSharedConstants
const wchar_t COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT[] = L"Local\\ColorPickerSettingsTelemetryEvent-6c7071d8-4014-46ec-b687-913bd8a422f1";
// Path to the event used to show Advanced Paste UI
const wchar_t SHOW_ADVANCED_PASTE_SHARED_EVENT[] = L"Local\\ShowAdvancedPasteEvent-9a46be2a-3e05-4186-b56b-4ae986ef2526";
const wchar_t ADVANCED_PASTE_MARKDOWN_EVENT[] = L"Local\\AdvancedPasteJsonEvent-a18c0798-3ee6-4fc5-bb9f-114c57ac0d47";
const wchar_t ADVANCED_PASTE_JSON_EVENT[] = L"Local\\AdvancedPasteJsonEvent-9ed021ab-b711-4cf3-9f33-135a698a9d21";
// Path to the event used to show Color Picker
const wchar_t SHOW_COLOR_PICKER_SHARED_EVENT[] = L"Local\\ShowColorPickerEvent-8c46be2a-3e05-4186-b56b-4ae986ef2525";

View File

@ -51,7 +51,7 @@ namespace powertoys_gpo {
const std::wstring POLICY_CONFIGURE_ENABLED_SCREEN_RULER = L"ConfigureEnabledUtilityScreenRuler";
const std::wstring POLICY_CONFIGURE_ENABLED_SHORTCUT_GUIDE = L"ConfigureEnabledUtilityShortcutGuide";
const std::wstring POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR = L"ConfigureEnabledUtilityTextExtractor";
const std::wstring POLICY_CONFIGURE_ENABLED_PASTE_PLAIN = L"ConfigureEnabledUtilityPastePlain";
const std::wstring POLICY_CONFIGURE_ENABLED_ADVANCED_PASTE = L"ConfigureEnabledUtilityAdvancedPaste";
const std::wstring POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE = L"ConfigureEnabledUtilityVideoConferenceMute";
const std::wstring POLICY_CONFIGURE_ENABLED_REGISTRY_PREVIEW = L"ConfigureEnabledUtilityRegistryPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_WITHOUT_BORDERS = L"ConfigureEnabledUtilityMouseWithoutBorders";
@ -361,9 +361,9 @@ namespace powertoys_gpo {
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR);
}
inline gpo_rule_configured_t getConfiguredPastePlainEnabledValue()
inline gpo_rule_configured_t getConfiguredAdvancedPasteEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_PASTE_PLAIN);
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_ADVANCED_PASTE);
}
inline gpo_rule_configured_t getConfiguredVideoConferenceMuteEnabledValue()

View File

@ -5,6 +5,8 @@ properties:
directives:
description: Configure PowerToys
settings:
AdvancedPaste:
Enabled: false
AlwaysOnTop:
Enabled: false
Awake:
@ -63,8 +65,6 @@ properties:
Enabled: false
Hosts:
Enabled: false
PastePlain:
Enabled: false
RegistryPreview:
Enabled: false

View File

@ -5,6 +5,8 @@ properties:
directives:
description: Configure PowerToys
settings:
AdvancedPaste:
Enabled: true
AlwaysOnTop:
Enabled: true
Awake:
@ -63,8 +65,6 @@ properties:
Enabled: true
Hosts:
Enabled: true
PastePlain:
Enabled: true
RegistryPreview:
Enabled: true

View File

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.8" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.9" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyNamespaces>
<target prefix="powertoys" namespace="Microsoft.Policies.PowerToys" />
</policyNamespaces>
<resources minRequiredRevision="1.8"/><!-- Last changed with PowerToys v0.78.0 -->
<resources minRequiredRevision="1.9"/><!-- Last changed with PowerToys v0.81.0 -->
<supportedOn>
<definitions>
<definition name="SUPPORTED_POWERTOYS_0_64_0" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0)"/>
@ -17,20 +17,21 @@
<definition name="SUPPORTED_POWERTOYS_0_76_0" displayName="$(string.SUPPORTED_POWERTOYS_0_76_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_77_0" displayName="$(string.SUPPORTED_POWERTOYS_0_77_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_78_0" displayName="$(string.SUPPORTED_POWERTOYS_0_78_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_81_0" displayName="$(string.SUPPORTED_POWERTOYS_0_81_0)"/>
</definitions>
</supportedOn>
<categories>
<category name="PowerToys" displayName="$(string.PowerToys)" />
<category name="InstallerUpdates" displayName="$(string.InstallerUpdates)">
<parentCategory ref="PowerToys" />
<category name="InstallerUpdates" displayName="$(string.InstallerUpdates)">
<parentCategory ref="PowerToys" />
</category>
<category name="PowerToysRun" displayName="$(string.PowerToysRun)">
<parentCategory ref="PowerToys" />
<category name="PowerToysRun" displayName="$(string.PowerToysRun)">
<parentCategory ref="PowerToys" />
</category>
</categories>
<policies>
<policy name="ConfigureGlobalUtilityEnabledState" class="Both" displayName="$(string.ConfigureGlobalUtilityEnabledState)" explainText="$(string.ConfigureGlobalUtilityEnabledStateDescription)" key="Software\Policies\PowerToys" valueName="ConfigureGlobalUtilityEnabledState">
<policy name="ConfigureGlobalUtilityEnabledState" class="Both" displayName="$(string.ConfigureGlobalUtilityEnabledState)" explainText="$(string.ConfigureGlobalUtilityEnabledStateDescription)" key="Software\Policies\PowerToys" valueName="ConfigureGlobalUtilityEnabledState">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0" />
<enabledValue>
@ -40,7 +41,17 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityAdvancedPaste" class="Both" displayName="$(string.ConfigureEnabledUtilityAdvancedPaste)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityAdvancedPaste">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_81_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityAlwaysOnTop" class="Both" displayName="$(string.ConfigureEnabledUtilityAlwaysOnTop)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityAlwaysOnTop">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />
@ -311,16 +322,6 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityPastePlain" class="Both" displayName="$(string.ConfigureEnabledUtilityPastePlain)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityPastePlain">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_68_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityPeek" class="Both" displayName="$(string.ConfigureEnabledUtilityPeek)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityPeek">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_70_0" />
@ -361,17 +362,17 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityRegistryPreview" class="Both" displayName="$(string.ConfigureEnabledUtilityRegistryPreview)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityRegistryPreview">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_69_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityScreenRuler" class="Both" displayName="$(string.ConfigureEnabledUtilityScreenRuler)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityScreenRuler">
<policy name="ConfigureEnabledUtilityRegistryPreview" class="Both" displayName="$(string.ConfigureEnabledUtilityRegistryPreview)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityRegistryPreview">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_69_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityScreenRuler" class="Both" displayName="$(string.ConfigureEnabledUtilityScreenRuler)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityScreenRuler">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />
<enabledValue>
@ -411,7 +412,7 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="DisablePerUserInstallation" class="Machine" displayName="$(string.DisablePerUserInstallation)" explainText="$(string.DisablePerUserInstallationDescription)" key="Software\Policies\PowerToys" valueName="PerUserInstallationDisabled">
<policy name="DisablePerUserInstallation" class="Machine" displayName="$(string.DisablePerUserInstallation)" explainText="$(string.DisablePerUserInstallationDescription)" key="Software\Policies\PowerToys" valueName="PerUserInstallationDisabled">
<parentCategory ref="InstallerUpdates" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_69_0" />
<enabledValue>
@ -421,7 +422,7 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="DisableAutomaticUpdateDownload" class="Both" displayName="$(string.DisableAutomaticUpdateDownload)" explainText="$(string.DisableAutomaticUpdateDownloadDescription)" key="Software\Policies\PowerToys" valueName="AutomaticUpdateDownloadDisabled">
<policy name="DisableAutomaticUpdateDownload" class="Both" displayName="$(string.DisableAutomaticUpdateDownload)" explainText="$(string.DisableAutomaticUpdateDownloadDescription)" key="Software\Policies\PowerToys" valueName="AutomaticUpdateDownloadDisabled">
<parentCategory ref="InstallerUpdates" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_68_0" />
<enabledValue>
@ -431,7 +432,7 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="SuspendNewUpdateToast" class="Both" displayName="$(string.SuspendNewUpdateToast)" explainText="$(string.SuspendNewUpdateToastDescription)" key="Software\Policies\PowerToys" valueName="SuspendNewUpdateAvailableToast">
<policy name="SuspendNewUpdateToast" class="Both" displayName="$(string.SuspendNewUpdateToast)" explainText="$(string.SuspendNewUpdateToastDescription)" key="Software\Policies\PowerToys" valueName="SuspendNewUpdateAvailableToast">
<parentCategory ref="InstallerUpdates" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_68_0" />
<enabledValue>
@ -441,7 +442,7 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="DisableNewUpdateToast" class="Both" displayName="$(string.DisableNewUpdateToast)" explainText="$(string.DisableNewUpdateToastDescription)" key="Software\Policies\PowerToys" valueName="DisableNewUpdateAvailableToast">
<policy name="DisableNewUpdateToast" class="Both" displayName="$(string.DisableNewUpdateToast)" explainText="$(string.DisableNewUpdateToastDescription)" key="Software\Policies\PowerToys" valueName="DisableNewUpdateAvailableToast">
<parentCategory ref="InstallerUpdates" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_78_0" />
<enabledValue>
@ -451,7 +452,7 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="DoNotShowWhatsNewAfterUpdates" class="Both" displayName="$(string.DoNotShowWhatsNewAfterUpdates)" explainText="$(string.DoNotShowWhatsNewAfterUpdatesDescription)" key="Software\Policies\PowerToys" valueName="DoNotShowWhatsNewAfterUpdates">
<policy name="DoNotShowWhatsNewAfterUpdates" class="Both" displayName="$(string.DoNotShowWhatsNewAfterUpdates)" explainText="$(string.DoNotShowWhatsNewAfterUpdatesDescription)" key="Software\Policies\PowerToys" valueName="DoNotShowWhatsNewAfterUpdates">
<parentCategory ref="InstallerUpdates" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_78_0" />
<enabledValue>
@ -471,7 +472,7 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="PowerToysRunAllPluginsEnabledState" class="Both" displayName="$(string.PowerToysRunAllPluginsEnabledState)" explainText="$(string.PowerToysRunAllPluginsEnabledStateDescription)" key="Software\Policies\PowerToys" valueName="PowerLauncherAllPluginsEnabledState">
<policy name="PowerToysRunAllPluginsEnabledState" class="Both" displayName="$(string.PowerToysRunAllPluginsEnabledState)" explainText="$(string.PowerToysRunAllPluginsEnabledStateDescription)" key="Software\Policies\PowerToys" valueName="PowerLauncherAllPluginsEnabledState">
<parentCategory ref="PowerToysRun" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0" />
<enabledValue>
@ -481,12 +482,12 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="PowerToysRunIndividualPluginEnabledState" class="Both" displayName="$(string.PowerToysRunIndividualPluginEnabledState)" explainText="$(string.PowerToysRunIndividualPluginEnabledStateDescription)" presentation="$(presentation.PowerToysRunIndividualPluginEnabledState)" key="Software\Policies\PowerToys\PowerLauncherIndividualPluginEnabledList">
<policy name="PowerToysRunIndividualPluginEnabledState" class="Both" displayName="$(string.PowerToysRunIndividualPluginEnabledState)" explainText="$(string.PowerToysRunIndividualPluginEnabledStateDescription)" presentation="$(presentation.PowerToysRunIndividualPluginEnabledState)" key="Software\Policies\PowerToys\PowerLauncherIndividualPluginEnabledList">
<parentCategory ref="PowerToysRun" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0"/>
<elements>
<list id="PowerToysRunIndividualPluginEnabledList" explicitValue="true" />
</elements>
<elements>
<list id="PowerToysRunIndividualPluginEnabledList" explicitValue="true" />
</elements>
</policy>
</policies>
</policyDefinitions>

View File

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.8" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.9" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<displayName>PowerToys</displayName>
<description>PowerToys</description>
<resources>
<stringTable>
<string id="PowerToys">Microsoft PowerToys</string>
<string id="InstallerUpdates">Installer and Updates</string>
<string id="PowerToysRun">PowerToys Run</string>
<string id="InstallerUpdates">Installer and Updates</string>
<string id="PowerToysRun">PowerToys Run</string>
<string id="SUPPORTED_POWERTOYS_0_64_0">PowerToys version 0.64.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_68_0">PowerToys version 0.68.0 or later</string>
@ -19,8 +19,9 @@
<string id="SUPPORTED_POWERTOYS_0_76_0">PowerToys version 0.76.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_77_0">PowerToys version 0.77.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_78_0">PowerToys version 0.78.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_81_0">PowerToys version 0.81.0 or later</string>
<string id="ConfigureGlobalUtilityEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
<string id="ConfigureGlobalUtilityEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.
If you enable this setting, all utilities will be always enabled and the user won't be able to disable it.
@ -31,7 +32,7 @@ If you don't configure this setting, users are able to enable or disable the uti
The individual enabled state policies for the utilities will override this policy.
</string>
<string id="ConfigureEnabledUtilityDescription">This policy configures the enabled state for a PowerToys utility.
<string id="ConfigureEnabledUtilityDescription">This policy configures the enabled state for a PowerToys utility.
If you enable this setting, the utility will be always enabled and the user won't be able to disable it.
@ -119,6 +120,7 @@ You can set the enabled state for all plugins not configured by this policy usin
Note: Changes require a restart of PowerToys Run.
</string>
<string id="ConfigureGlobalUtilityEnabledState">Configure global utility enabled state</string>
<string id="ConfigureEnabledUtilityAdvancedPaste">Advanced Paste: Configure enabled state</string>
<string id="ConfigureEnabledUtilityAlwaysOnTop">Always On Top: Configure enabled state</string>
<string id="ConfigureEnabledUtilityAwake">Awake: Configure enabled state</string>
<string id="ConfigureEnabledUtilityColorPicker">Color Picker: Configure enabled state</string>
@ -144,12 +146,11 @@ Note: Changes require a restart of PowerToys Run.
<string id="ConfigureEnabledUtilityMouseJump">Mouse Jump: Configure enabled state</string>
<string id="ConfigureEnabledUtilityMousePointerCrosshairs">Mouse Pointer Crosshairs: Configure enabled state</string>
<string id="ConfigureEnabledUtilityMouseWithoutBorders">Mouse Without Borders: Configure enabled state</string>
<string id="ConfigureEnabledUtilityPastePlain">Paste as Plain Text: Configure enabled state</string>
<string id="ConfigureEnabledUtilityPeek">Peek: Configure enabled state</string>
<string id="ConfigureEnabledUtilityPowerRename">Power Rename: Configure enabled state</string>
<string id="ConfigureEnabledUtilityPowerLauncher">PowerToys Run: Configure enabled state</string>
<string id="ConfigureEnabledUtilityQuickAccent">Quick Accent: Configure enabled state</string>
<string id="ConfigureEnabledUtilityRegistryPreview">Registry Preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityRegistryPreview">Registry Preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityScreenRuler">Screen Ruler: Configure enabled state</string>
<string id="ConfigureEnabledUtilityShortcutGuide">Shortcut Guide: Configure enabled state</string>
<string id="ConfigureEnabledUtilityTextExtractor">Text Extractor: Configure enabled state</string>
@ -165,13 +166,13 @@ Note: Changes require a restart of PowerToys Run.
<string id="ConfigureEnabledUtilityFileExplorerQOIPreview">QOI file preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileExplorerQOIThumbnails">QOI file thumbnail: Configure enabled state</string>
</stringTable>
<presentationTable>
<presentation id="PowerToysRunIndividualPluginEnabledState">
<presentationTable>
<presentation id="PowerToysRunIndividualPluginEnabledState">
<listBox refId="PowerToysRunIndividualPluginEnabledList">List of managed plugins:</listBox>
</presentation>
</presentationTable>
</presentationTable>
</resources>
</policyDefinitionResources>

View File

@ -0,0 +1,118 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
<UseWinUI>true</UseWinUI>
<ApplicationIcon>Assets\AdvancedPaste\AdvancedPaste.ico</ApplicationIcon>
<ApplicationManifest>app.manifest</ApplicationManifest>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
<WindowsPackageType>None</WindowsPackageType>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<SelfContained>true</SelfContained>
<AssemblyName>PowerToys.AdvancedPaste</AssemblyName>
<AssemblyDescription>PowerToys AdvancedPaste</AssemblyDescription>
<RootNamespace>AdvancedPaste</RootNamespace>
<DisableWinExeOutputInference>true</DisableWinExeOutputInference>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
<ProjectPriFileName>PowerToys.AdvancedPaste.pri</ProjectPriFileName>
<DefineConstants>DISABLE_XAML_GENERATED_MAIN,TRACE</DefineConstants>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
</PropertyGroup>
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
<PropertyGroup>
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
</PropertyGroup>
<ItemGroup>
<None Remove="AdvancedPasteXAML\Controls\PromptBox.xaml" />
<None Remove="Assets\AdvancedPaste\AIIcon.png" />
<None Remove="Assets\AdvancedPaste\Gradient.png" />
<None Remove="AdvancedPasteXAML\Controls\AnimatedContentControl\AnimatedBorderBrush.xaml" />
<None Remove="AdvancedPasteXAML\Views\MainPage.xaml" />
</ItemGroup>
<ItemGroup>
<Page Remove="AdvancedPasteXAML\App.xaml" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="AdvancedPasteXAML\App.xaml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="CommunityToolkit.WinUI.Animations" />
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.WindowsAppSDK" />
<PackageReference Include="Microsoft.Windows.CsWin32" />
<PackageReference Include="Microsoft.Windows.CsWinRT" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
<PackageReference Include="ReverseMarkdown" />
<!-- HACK: To align Microsoft.Bcl.AsyncInterfaces.dll version with PowerToys.Settings.csproj. -->
<PackageReference Include="StreamJsonRpc" />
<PackageReference Include="WinUIEx" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<PropertyGroup>
<!-- TODO: fix issues and reenable -->
<!-- These are caused by streamjsonrpc dependency on Microsoft.VisualStudio.Threading.Analyzers -->
<!-- We might want to add that to the project and fix the issues as well -->
<NoWarn>VSTHRD002;VSTHRD110;VSTHRD100;VSTHRD200;VSTHRD101</NoWarn>
</PropertyGroup>
<!--
Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
Tools extension to be activated for this project even if the Windows App SDK Nuget
package has not yet been restored.
-->
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<ProjectCapability Include="Msix" />
</ItemGroup>
<ItemGroup>
<!-- HACK: Common.UI is referenced, even if it is not used, to force dll versions to be the same as in other projects that use it. It's still unclear why this is the case, but this is need for flattening the install directory. -->
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
</ItemGroup>
<!--
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
Explorer "Package and Publish" context menu entry to be enabled for this project even if
the Windows App SDK Nuget package has not yet been restored.
-->
<PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'">
<HasPackageAndPublishMenu>true</HasPackageAndPublishMenu>
</PropertyGroup>
<ItemGroup>
<Page Update="AdvancedPasteXAML\Controls\PromptBox.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

View File

@ -0,0 +1,167 @@
<?xml version="1.0" encoding="utf-8" ?>
<Application
x:Class="AdvancedPaste.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdvancedPaste">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<ResourceDictionary Source="ms-appx:///AdvancedPasteXAML/Controls/AnimatedContentControl/AnimatedContentControl.xaml" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SubtleFillColorSecondary" />
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SubtleFillColorTertiary" />
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SubtleFillColorSecondary" />
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SubtleFillColorTertiary" />
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SubtleFillColorTransparent" />
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="TextFillColorPrimary" />
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="TextFillColorPrimary" />
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="TextFillColorSecondary" />
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="TextFillColorDisabled" />
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<StaticResource x:Key="SubtleButtonBackground" ResourceKey="SystemColorWindowColorBrush" />
<StaticResource x:Key="SubtleButtonBackgroundPointerOver" ResourceKey="SystemColorHighlightTextColorBrush" />
<StaticResource x:Key="SubtleButtonBackgroundPressed" ResourceKey="SystemColorWindowColorBrush" />
<StaticResource x:Key="SubtleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="SubtleButtonBorderBrush" ResourceKey="SystemColorWindowColorBrush" />
<StaticResource x:Key="SubtleButtonBorderBrushPointerOver" ResourceKey="SystemColorHighlightColorBrush" />
<StaticResource x:Key="SubtleButtonBorderBrushPressed" ResourceKey="SystemColorHighlightColorBrush" />
<StaticResource x:Key="SubtleButtonBorderBrushDisabled" ResourceKey="SystemColorGrayTextColor" />
<StaticResource x:Key="SubtleButtonForeground" ResourceKey="SystemColorButtonTextColorBrush" />
<StaticResource x:Key="SubtleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="SubtleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="SubtleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Style x:Key="SubtleButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{ThemeResource SubtleButtonBackground}" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="Foreground" Value="{ThemeResource SubtleButtonForeground}" />
<Setter Property="BorderBrush" Value="{ThemeResource SubtleButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="CornerRadius" Value="{ThemeResource ControlCornerRadius}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ContentPresenter
x:Name="ContentPresenter"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
AnimatedIcon.State="Normal"
AutomationProperties.AccessibilityView="Raw"
Background="{TemplateBinding Background}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
CornerRadius="{TemplateBinding CornerRadius}"
Foreground="{TemplateBinding Foreground}">
<ContentPresenter.BackgroundTransition>
<BrushTransition Duration="0:0:0.083" />
</ContentPresenter.BackgroundTransition>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="PointerOver" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Pressed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonBorderBrushDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource SubtleButtonForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
<VisualState.Setters>
<!-- DisabledVisual Should be handled by the control, not the animated icon. -->
<Setter Target="ContentPresenter.(AnimatedIcon.State)" Value="Normal" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -0,0 +1,169 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.ViewModels;
using ManagedCommon;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Windows.ApplicationModel.DataTransfer;
using Windows.Graphics;
using WinUIEx;
using static AdvancedPaste.Helpers.NativeMethods;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace AdvancedPaste
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
public partial class App : Application, IDisposable
{
public IHost Host { get; private set; }
private MainWindow window;
private nint windowHwnd;
private OptionsViewModel viewModel;
private bool disposedValue;
/// <summary>
/// Initializes a new instance of the <see cref="App"/> class.
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) =>
{
services.AddSingleton<OptionsViewModel>();
}).Build();
viewModel = GetService<OptionsViewModel>();
UnhandledException += App_UnhandledException;
}
public MainWindow GetMainWindow()
{
return window;
}
public static T GetService<T>()
where T : class
{
if ((App.Current as App)!.Host.Services.GetService(typeof(T)) is not T service)
{
throw new ArgumentException($"{typeof(T)} needs to be registered in ConfigureServices within App.xaml.cs.");
}
return service;
}
/// <summary>
/// Invoked when the application is launched.
/// </summary>
/// <param name="args">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
var cmdArgs = Environment.GetCommandLineArgs();
if (cmdArgs?.Length > 1)
{
if (int.TryParse(cmdArgs[1], out int powerToysRunnerPid))
{
RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () =>
{
Environment.Exit(0);
});
}
}
NativeEventWaiter.WaitForEventLoop(interop.Constants.ShowAdvancedPasteSharedEvent(), OnAdvancedPasteHotkey);
NativeEventWaiter.WaitForEventLoop(interop.Constants.AdvancedPasteMarkdownEvent(), OnAdvancedPasteMarkdownHotkey);
NativeEventWaiter.WaitForEventLoop(interop.Constants.AdvancedPasteJsonEvent(), OnAdvancedPasteJsonHotkey);
}
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
{
Logger.LogError("Unhandled exception", e.Exception);
}
private void OnAdvancedPasteJsonHotkey()
{
viewModel.GetClipboardData();
viewModel.ToJsonFunction(true);
}
private void OnAdvancedPasteMarkdownHotkey()
{
viewModel.GetClipboardData();
viewModel.ToMarkdownFunction(true);
}
private void OnAdvancedPasteHotkey()
{
viewModel.OnShow();
if (window is null)
{
window = new MainWindow();
windowHwnd = window.GetWindowHandle();
MoveWindowToActiveMonitor();
window.Activate();
}
else
{
MoveWindowToActiveMonitor();
Windows.Win32.PInvoke.ShowWindow((Windows.Win32.Foundation.HWND)windowHwnd, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_SHOW);
WindowHelpers.BringToForeground(windowHwnd);
}
window.SetFocus();
}
private void MoveWindowToActiveMonitor()
{
if (GetCursorPos(out PointInter cursorPosition))
{
DisplayArea displayArea = DisplayArea.GetFromPoint(new PointInt32(cursorPosition.X, cursorPosition.Y), DisplayAreaFallback.Nearest);
var x = displayArea.WorkArea.X + (displayArea.WorkArea.Width / 2) - (window.Width / 2);
var y = displayArea.WorkArea.Y + (displayArea.WorkArea.Height / 2) - (window.Height / 2);
window.MoveAndResize(x, y, window.Width, window.Height);
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
window.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@ -0,0 +1,165 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Numerics;
using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Media;
using Windows.Storage;
using Windows.Storage.Streams;
namespace AdvancedPaste.Controls
{
public partial class AnimatedBorderBrush : XamlCompositionBrushBase
{
public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(
nameof(IsLoading),
typeof(bool),
typeof(AnimatedBorderBrush),
new PropertyMetadata(defaultValue: false, OnIsLoadingChanged));
public bool IsLoading
{
get => (bool)GetValue(IsLoadingProperty);
set => SetValue(IsLoadingProperty, value);
}
public static readonly DependencyProperty DurationProperty = DependencyProperty.Register(
nameof(Duration),
typeof(int),
typeof(AnimatedBorderBrush),
new PropertyMetadata(defaultValue: 400, OnDurationChanged));
public int Duration
{
get => (int)GetValue(DurationProperty);
set => SetValue(DurationProperty, value);
}
private static void OnIsLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs newValue)
{
var selectionRectangle = (AnimatedBorderBrush)d;
selectionRectangle.IsLoadingChanged();
}
private static void OnDurationChanged(DependencyObject d, DependencyPropertyChangedEventArgs newValue)
{
var selectionRectangle = (AnimatedBorderBrush)d;
selectionRectangle.DurationChanged();
}
private Compositor compositor;
private bool isConnected;
private double centerWidth;
private double centerHeight;
private CompositionAnimationGroup animationGroup;
private CompositionSurfaceBrush gradientBrush;
public AnimatedBorderBrush()
{
compositor = CompositionTarget.GetCompositorForCurrentThread();
}
private void DurationChanged()
{
if (!isConnected)
{
return;
}
if (IsLoading)
{
PlayAnimation(true);
}
else
{
animationGroup = null;
}
}
protected override void OnConnected()
{
isConnected = true;
IsLoadingChanged();
}
protected override void OnDisconnected()
{
isConnected = false;
CompositionBrush = null;
gradientBrush = null;
}
private void IsLoadingChanged()
{
if (!isConnected)
{
return;
}
if (IsLoading)
{
if (CompositionBrush == null)
{
var brush = compositor.CreateSurfaceBrush();
var loadedSurface = LoadedImageSurface.StartLoadFromUri(new Uri("ms-appx:///Assets/AdvancedPaste/Gradient.png"));
brush.Surface = loadedSurface;
brush.HorizontalAlignmentRatio = 0.5f;
brush.VerticalAlignmentRatio = 0.5f;
brush.Stretch = CompositionStretch.UniformToFill;
brush.BitmapInterpolationMode = CompositionBitmapInterpolationMode.MagLinearMinLinearMipLinear;
brush.Scale = new Vector2(1.4f, 1.4f);
CompositionBrush = brush;
gradientBrush = brush;
gradientBrush.CenterPoint = new Vector2((float)centerWidth / 2, (float)centerHeight / 2);
}
PlayAnimation(false);
}
else
{
if (animationGroup != null && gradientBrush != null)
{
gradientBrush.StopAnimationGroup(animationGroup);
}
}
}
public void UpdateSize(double width, double height)
{
centerWidth = width;
centerHeight = height;
if (gradientBrush != null)
{
gradientBrush.CenterPoint = new Vector2((float)width / 2, (float)height / 2);
}
}
private void PlayAnimation(bool reset)
{
if (reset || animationGroup == null)
{
InitializeAnimation();
}
gradientBrush.StopAnimationGroup(animationGroup);
gradientBrush.StartAnimationGroup(animationGroup);
}
private void InitializeAnimation()
{
animationGroup = compositor.CreateAnimationGroup();
var animation = compositor.CreateScalarKeyFrameAnimation();
animation.Duration = TimeSpan.FromMilliseconds(Duration);
animation.IterationBehavior = AnimationIterationBehavior.Forever;
var easing = compositor.CreateLinearEasingFunction();
animation.InsertKeyFrame(0, 0, easing);
animation.InsertKeyFrame(1, 360, easing);
animation.Target = "RotationAngleInDegrees";
animationGroup.Add(animation);
}
}
}

View File

@ -0,0 +1,7 @@
<XamlCompositionBrushBase
x:Class="AdvancedPaste.Controls.AnimatedBorderBrush"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" />

View File

@ -0,0 +1,91 @@
// 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.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace AdvancedPaste.Controls
{
[TemplatePart(Name = LoadingGrid, Type = typeof(Grid))]
[TemplatePart(Name = LoadingBrush, Type = typeof(AnimatedBorderBrush))]
public class AnimatedContentControl : ContentControl
{
internal const string LoadingGrid = "PART_LoadingGrid";
internal const string LoadingBrush = "PART_LoadingBrush";
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
nameof(Text),
typeof(string),
typeof(AnimatedContentControl),
new PropertyMetadata(defaultValue: null));
public bool IsLoading
{
get => (bool)GetValue(IsLoadingProperty);
set => SetValue(IsLoadingProperty, value);
}
public static readonly DependencyProperty IsLoadingProperty = DependencyProperty.Register(
nameof(IsLoading),
typeof(bool),
typeof(AnimatedContentControl),
new PropertyMetadata(defaultValue: false, (d, e) => ((AnimatedContentControl)d).OnIsLoadingPropertyChanged((bool)e.OldValue, (bool)e.NewValue)));
public AnimatedContentControl()
{
this.DefaultStyleKey = typeof(AnimatedContentControl);
}
protected override void OnApplyTemplate()
{
this.SizeChanged -= AICard_SizeChanged;
OnIsLoadingChanged();
UpdateSize(Width, Height);
this.SizeChanged += AICard_SizeChanged;
}
private void AICard_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (GetTemplateChild(LoadingBrush) is AnimatedBorderBrush loadingBrush)
{
UpdateSize(e.NewSize.Width, e.NewSize.Height);
}
}
private void UpdateSize(double width, double height)
{
if (GetTemplateChild(LoadingBrush) is AnimatedBorderBrush loadingBrush)
{
loadingBrush.UpdateSize(width, height);
}
}
protected virtual void OnIsLoadingPropertyChanged(bool oldValue, bool newValue)
{
OnIsLoadingChanged();
}
private void OnIsLoadingChanged()
{
if (GetTemplateChild(LoadingBrush) is AnimatedBorderBrush loadingBrush)
{
UpdateSize(ActualWidth, ActualHeight);
loadingBrush.IsLoading = IsLoading;
}
if (GetTemplateChild(LoadingGrid) is Grid loadingGrid)
{
loadingGrid.Visibility = IsLoading ? Visibility.Visible : Visibility.Collapsed;
UpdateSize(ActualWidth, ActualHeight);
}
}
}
}

View File

@ -0,0 +1,58 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
xmlns:local="using:AdvancedPaste.Controls">
<Style BasedOn="{StaticResource DefaultAnimatedContentControlStyle}" TargetType="local:AnimatedContentControl" />
<Style x:Key="DefaultAnimatedContentControlStyle" TargetType="local:AnimatedContentControl">
<Style.Setters>
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:AnimatedContentControl">
<Grid
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}">
<ContentPresenter Content="{TemplateBinding Content}" />
<Grid
x:Name="PART_LoadingGrid"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="Transparent"
BorderThickness="4"
CornerRadius="{TemplateBinding CornerRadius}"
Visibility="Collapsed">
<!-- CornerRadius needs to be > 0 -->
<Grid.BorderBrush>
<local:AnimatedBorderBrush
x:Name="PART_LoadingBrush"
IsLoading="True"
Duration="900" />
</Grid.BorderBrush>
<animations:Implicit.ShowAnimations>
<animations:OpacityAnimation
From="0"
To="1.0"
Duration="0:0:0.4" />
</animations:Implicit.ShowAnimations>
<animations:Implicit.HideAnimations>
<animations:OpacityAnimation
From="1.0"
To="0"
Duration="0:0:0.2" />
</animations:Implicit.HideAnimations>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,662 @@
<UserControl
x:Class="AdvancedPaste.Controls.PromptBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:animations="using:CommunityToolkit.WinUI.Animations"
xmlns:converters="using:CommunityToolkit.WinUI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:AdvancedPaste.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<Color x:Key="AccentGradientColor">#65C8F2</Color>
<LinearGradientBrush x:Key="AccentGradientBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0.0" Color="#98EFFE" />
<GradientStop Offset="0.25" Color="#48B1E9" />
<GradientStop Offset="1.0" Color="{StaticResource AccentGradientColor}" />
</LinearGradientBrush>
</ResourceDictionary>
<ResourceDictionary x:Key="Light">
<Color x:Key="AccentGradientColor">#005FB8</Color>
<LinearGradientBrush x:Key="AccentGradientBrush" StartPoint="0,0" EndPoint="1,1">
<GradientStop Offset="0.0" Color="#4992C7" />
<GradientStop Offset="0.25" Color="#1353A0" />
<GradientStop Offset="1.0" Color="{StaticResource AccentGradientColor}" />
</LinearGradientBrush>
</ResourceDictionary>
<ResourceDictionary x:Key="HighContrast">
<Color x:Key="AccentGradientColor">#48B1E9</Color>
<SolidColorBrush x:Key="AccentGradientBrush" Color="{StaticResource AccentGradientColor}" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Style x:Key="CustomTextBoxStyle" TargetType="TextBox">
<Setter Property="Foreground" Value="{ThemeResource TextControlForeground}" />
<Setter Property="Background" Value="{ThemeResource TextControlBackground}" />
<Setter Property="BorderBrush" Value="{ThemeResource TextControlBorderBrush}" />
<Setter Property="SelectionHighlightColor" Value="{ThemeResource TextControlSelectionHighlightColor}" />
<Setter Property="BorderThickness" Value="{ThemeResource TextControlBorderThemeThickness}" />
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
<Setter Property="ScrollViewer.HorizontalScrollMode" Value="Auto" />
<Setter Property="ScrollViewer.VerticalScrollMode" Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled" Value="False" />
<Setter Property="MinHeight" Value="{ThemeResource TextControlThemeMinHeight}" />
<Setter Property="MinWidth" Value="{ThemeResource TextControlThemeMinWidth}" />
<Setter Property="Padding" Value="{ThemeResource TextControlThemePadding}" />
<Setter Property="UseSystemFocusVisuals" Value="{ThemeResource IsApplicationFocusVisualKindReveal}" />
<Setter Property="ContextFlyout" Value="{StaticResource TextControlCommandBarContextFlyout}" />
<Setter Property="SelectionFlyout" Value="{StaticResource TextControlCommandBarSelectionFlyout}" />
<Setter Property="CornerRadius" Value="8" />
<Setter Property="BackgroundSizing" Value="InnerBorderEdge" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Grid>
<Grid.Resources>
<Style x:Name="DeleteButtonStyle" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid
x:Name="ButtonLayoutGrid"
Margin="{ThemeResource TextBoxInnerButtonMargin}"
Background="{ThemeResource TextControlButtonBackground}"
BackgroundSizing="{TemplateBinding BackgroundSizing}"
BorderBrush="{ThemeResource TextControlButtonBorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
Visibility="Collapsed">
<TextBlock
x:Name="GlyphElement"
HorizontalAlignment="Center"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontFamily="{ThemeResource SymbolThemeFontFamily}"
FontSize="{ThemeResource TextBoxIconFontSize}"
FontStyle="Normal"
Foreground="{ThemeResource TextControlButtonForeground}"
Text="&#xE894;" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBorderBrushPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBackgroundPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ButtonLayoutGrid" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonBorderBrushPressed}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="GlyphElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlButtonForegroundPressed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation
Storyboard.TargetName="ButtonLayoutGrid"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0" />
<ColumnDefinition Width="32" />
</Grid.ColumnDefinitions>
<ContentPresenter
x:Name="HeaderContentPresenter"
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Margin="{ThemeResource TextBoxTopHeaderMargin}"
VerticalAlignment="Top"
x:DeferLoadStrategy="Lazy"
Content="{TemplateBinding Header}"
ContentTemplate="{TemplateBinding HeaderTemplate}"
FontWeight="Normal"
Foreground="{ThemeResource TextControlHeaderForeground}"
TextWrapping="Wrap"
Visibility="Collapsed" />
<Border
x:Name="BorderElement"
Grid.Row="1"
Grid.RowSpan="1"
Grid.Column="0"
Grid.ColumnSpan="4"
MinWidth="{TemplateBinding MinWidth}"
MinHeight="{TemplateBinding MinHeight}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Control.IsTemplateFocusTarget="True"
CornerRadius="{TemplateBinding CornerRadius}" />
<Viewbox
Grid.Row="1"
Width="16"
Height="16"
Margin="8,0,0,0">
<PathIcon
x:Name="AIGlyph"
AutomationProperties.AccessibilityView="Raw"
Data="M128 766q0-42 24-77t65-48l178-57q32-11 61-30t52-42q50-50 71-114l58-179q13-40 48-65t78-26q42 0 77 24t50 65l58 177q21 66 72 117 49 50 117 72l176 58q43 14 69 48t26 80q0 41-25 76t-64 49l-178 58q-66 21-117 72-32 32-51 73t-33 84-26 83-30 73-45 51-71 20q-42 0-77-24t-49-65l-58-178q-8-25-19-47t-28-43q-34-43-77-68t-89-41-89-27-78-29-55-45-21-75zm1149 7q-76-29-145-53t-129-60-104-88-73-138l-57-176-67 176q-18 48-42 89t-60 78q-34 34-76 61t-89 43l-177 57q75 29 144 53t127 60 103 89 73 137l57 176 67-176q37-97 103-168t168-103l177-57zm-125 759q0-31 20-57t49-36l99-32q34-11 53-34t30-51 20-59 20-54 33-41 58-16q32 0 59 19t38 50q6 20 11 40t13 40 17 38 25 34q16 17 39 26t48 18 49 16 44 20 31 32 12 50q0 33-18 60t-51 38q-19 6-39 11t-41 13-39 17-34 25q-24 25-35 62t-24 73-35 61-68 25q-32 0-59-19t-38-50q-6-18-11-39t-13-41-17-40-24-33q-18-17-41-27t-47-17-49-15-43-20-30-33-12-54zm583 4q-43-13-74-30t-55-41-40-55-32-74q-12 41-29 72t-42 55-55 42-71 31q81 23 128 71t71 129q15-43 31-74t40-54 53-40 75-32z"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</Viewbox>
<ScrollViewer
x:Name="ContentElement"
Grid.Row="1"
Grid.Column="1"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
Foreground="{TemplateBinding Foreground}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsTabStop="False"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
ZoomMode="Disabled" />
<TextBlock
x:Name="PlaceholderTextContentPresenter"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="Center"
Foreground="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForeground}}"
IsHitTestVisible="False"
Text="{TemplateBinding PlaceholderText}"
TextAlignment="{TemplateBinding TextAlignment}"
TextWrapping="{TemplateBinding TextWrapping}" />
<Button
x:Name="DeleteButton"
Grid.Row="1"
Grid.Column="2"
Width="30"
Padding="{ThemeResource HelperButtonThemePadding}"
VerticalAlignment="Stretch"
AutomationProperties.AccessibilityView="Raw"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
FontSize="{TemplateBinding FontSize}"
IsTabStop="False"
Style="{StaticResource DeleteButtonStyle}"
Visibility="Collapsed" />
<ContentPresenter
x:Name="DescriptionPresenter"
Grid.Row="2"
Grid.ColumnSpan="3"
x:Load="False"
AutomationProperties.AccessibilityView="Raw"
Content="{TemplateBinding Description}" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="HeaderContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlHeaderForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="AIGlyph" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundDisabled}}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderBrushPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundPointerOver}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundPointerOver}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundFocused}}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="Background">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBackgroundFocused}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderBrush">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<LinearGradientBrush MappingMode="Absolute" StartPoint="0,0" EndPoint="0,2">
<LinearGradientBrush.RelativeTransform>
<ScaleTransform CenterY="0.5" ScaleY="-1" />
</LinearGradientBrush.RelativeTransform>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="1.0" Color="{ThemeResource AccentGradientColor}" />
<GradientStop Offset="1.0" Color="{ThemeResource ControlStrokeColorDefault}" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="BorderElement" Storyboard.TargetProperty="BorderThickness">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlBorderThemeThicknessFocused}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentElement" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundFocused}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="ButtonStates">
<VisualState x:Name="ButtonVisible">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="DeleteButton" Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="ButtonCollapsed" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="PromptBoxGrid" Loaded="Grid_Loaded">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<local:AnimatedContentControl
x:Name="Loader"
MinHeight="48"
CornerRadius="8">
<Grid>
<TextBox
x:Name="InputTxtBox"
HorizontalAlignment="Stretch"
x:FieldModifier="public"
IsEnabled="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay}"
KeyDown="InputTxtBox_KeyDown"
PlaceholderText="{x:Bind ViewModel.InputTxtBoxPlaceholderText, Mode=OneWay}"
Style="{StaticResource CustomTextBoxStyle}"
TabIndex="0"
Text="{x:Bind Prompt, Mode=TwoWay}"
TextChanging="InputTxtBox_TextChanging">
<ToolTipService.ToolTip>
<TextBlock x:Uid="InputTxtBoxTooltip" />
</ToolTipService.ToolTip>
<FlyoutBase.AttachedFlyout>
<Flyout
x:Name="PreviewFlyout"
AreOpenCloseAnimationsEnabled="False"
Opened="PreviewFlyout_Opened"
Placement="Bottom"
ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style BasedOn="{StaticResource DefaultFlyoutPresenterStyle}" TargetType="FlyoutPresenter">
<Style.Setters>
<Setter Property="MaxWidth" Value="3000" />
<Setter Property="Padding" Value="0" />
</Style.Setters>
</Style>
</Flyout.FlyoutPresenterStyle>
<Grid x:Name="PreviewGrid" Background="{ThemeResource SolidBackgroundFillColorBaseBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid
Padding="12,8,12,8"
Background="{ThemeResource LayerFillColorAltBrush}"
CornerRadius="8,8,0,0"
RowSpacing="4">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Uid="ResultTitleTxt" VerticalAlignment="Center" />
<StackPanel
HorizontalAlignment="Right"
VerticalAlignment="Center"
Orientation="Horizontal"
Spacing="2">
<Button
Padding="4"
Click="ThumbUpDown_Click"
CommandParameter="True"
Content="{ui:FontIcon Glyph=&#xE8E1;,
FontSize=14}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="ThumbsUpFeedback" />
</ToolTipService.ToolTip>
</Button>
<Button
Padding="4"
Click="ThumbUpDown_Click"
CommandParameter="False"
Content="{ui:FontIcon Glyph=&#xE8E0;,
FontSize=14}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="ThumbsDownFeedback" />
</ToolTipService.ToolTip>
</Button>
</StackPanel>
<ScrollViewer
Grid.Row="1"
MinHeight="104"
MaxHeight="320">
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
IsTextSelectionEnabled="True"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.CustomFormatResult, Mode=OneWay}"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
<Rectangle
Grid.Row="1"
Height="1"
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
<Grid
Grid.Row="2"
Margin="12"
ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel
Margin="-4,0,0,0"
Orientation="Horizontal"
Spacing="2"
Visibility="{x:Bind ViewModel.HasMultipleResponses, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<Button
x:Uid="PreviousResultBtnAutomation"
Padding="4"
VerticalAlignment="Stretch"
Command="{x:Bind ViewModel.PreviousCustomFormatCommand}"
Content="{ui:FontIcon Glyph=&#xE76B;,
FontSize=14}"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="PreviousResultBtnToolTip" />
</ToolTipService.ToolTip>
</Button>
<TextBlock
Margin="0,-2,0,0"
VerticalAlignment="Center"
Text="{x:Bind ViewModel.CurrentIndexDisplay, Mode=OneWay}" />
<Button
x:Uid="NextResultBtnAutomation"
Padding="4"
VerticalAlignment="Stretch"
Command="{x:Bind ViewModel.NextCustomFormatCommand}"
Content="{ui:FontIcon Glyph=&#xE76C;,
FontSize=14}"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="NextResultBtnToolTip" />
</ToolTipService.ToolTip>
</Button>
</StackPanel>
<Button
x:Uid="RegenerateBtnAutomation"
Grid.Column="1"
VerticalAlignment="Stretch"
Command="{x:Bind GenerateCustomCommand}"
Content="{ui:FontIcon Glyph=&#xE72C;,
FontSize=16}"
Style="{StaticResource SubtleButtonStyle}">
<ToolTipService.ToolTip>
<TextBlock x:Uid="RegenerateBtnToolTip" />
</ToolTipService.ToolTip>
</Button>
<Button
x:Name="PreviewPasteBtn"
x:Uid="PasteButtonAutomation"
Grid.Column="2"
HorizontalAlignment="Right"
Click="PreviewPasteBtn_Click"
Style="{StaticResource AccentButtonStyle}">
<StackPanel Orientation="Horizontal" Spacing="8">
<FontIcon FontSize="16" Glyph="&#xE77F;" />
<TextBlock x:Uid="PasteText" />
</StackPanel>
</Button>
</Grid>
</Grid>
</Flyout>
</FlyoutBase.AttachedFlyout>
</TextBox>
<!--<StackPanel
Margin="0,0,4,0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Orientation="Horizontal">-->
<!--<Button
x:Name="RecallBtn"
x:Uid="RecallButtonAutomation"
Width="32"
Height="32"
Padding="0"
Command="{x:Bind RecallCommand}"
Content="{ui:FontIcon Glyph=&#xE81C;,
FontSize=12}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource SubtleButtonStyle}"
TabIndex="2"
Visibility="Collapsed">
<ToolTipService.ToolTip>
<TextBlock x:Uid="RecallBtnToolTip" TextWrapping="WrapWholeWords" />
</ToolTipService.ToolTip>
<animations:Implicit.Animations>
<animations:TranslationAnimation Duration="0:0:1" />
<animations:ScaleAnimation Duration="0:0:1" />
<animations:OffsetAnimation Duration="0:0:1" />
</animations:Implicit.Animations>
</Button>-->
<Button
x:Name="SendBtn"
x:Uid="SendButtonAutomation"
Width="32"
Height="32"
Margin="0,0,4,0"
Padding="0"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5"
Command="{x:Bind GenerateCustomCommand}"
Content="{ui:FontIcon Glyph=&#xE724;,
FontSize=16}"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Style="{StaticResource SubtleButtonStyle}"
TabIndex="1"
Visibility="Collapsed">
<ToolTipService.ToolTip>
<TextBlock x:Uid="SendBtnToolTip" TextWrapping="WrapWholeWords" />
</ToolTipService.ToolTip>
<animations:Implicit.ShowAnimations>
<animations:ScaleAnimation
From="0.4"
To="1"
Duration="0:0:0.167" />
<animations:OpacityAnimation
From="0.0"
To="1.0"
Duration="0:0:0.167" />
</animations:Implicit.ShowAnimations>
<animations:Implicit.HideAnimations>
<animations:ScaleAnimation
From="1"
To="0.4"
Duration="0:0:0.167" />
<animations:OpacityAnimation
From="1.0"
To="0.0"
Duration="0:0:0.167" />
</animations:Implicit.HideAnimations>
</Button>
<!--</StackPanel>-->
</Grid>
</local:AnimatedContentControl>
<ContentPresenter
x:Name="DisclaimerPresenter"
Grid.Row="1"
Margin="8,8,12,0"
VerticalAlignment="Top"
Content="{x:Bind Footer, Mode=OneWay}">
<animations:Implicit.ShowAnimations>
<animations:OpacityAnimation To="1.0" Duration="0:0:0.4" />
</animations:Implicit.ShowAnimations>
<animations:Implicit.HideAnimations>
<animations:OpacityAnimation To="0.0" Duration="0:0:0.167" />
</animations:Implicit.HideAnimations>
</ContentPresenter>
<TextBlock
x:Name="LoadingText"
x:Uid="LoadingText"
Grid.Row="1"
FontWeight="SemiBold"
Foreground="{ThemeResource AccentGradientBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Visibility="Collapsed">
<animations:Implicit.ShowAnimations>
<animations:OpacityAnimation To="1.0" Duration="0:0:0.6" />
</animations:Implicit.ShowAnimations>
<animations:Implicit.HideAnimations>
<animations:OpacityAnimation To="0.0" Duration="0:0:0.167" />
</animations:Implicit.HideAnimations>
</TextBlock>
<Grid
x:Name="ErrorMessageGrid"
x:Uid="ErrorMessageGrid"
Grid.Row="1"
Margin="8,8,0,0"
ColumnSpacing="8"
Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
FontWeight="SemiBold"
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.InputTxtBoxErrorText, Mode=OneWay}" />
<HyperlinkButton
x:Uid="SettingsBtn"
Grid.Column="1"
Margin="0,-1,0,0"
Padding="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Command="{x:Bind ViewModel.OpenSettingsCommand}"
FontSize="12" />
<animations:Implicit.ShowAnimations>
<animations:OpacityAnimation To="1.0" Duration="0:0:0.6" />
</animations:Implicit.ShowAnimations>
<animations:Implicit.HideAnimations>
<animations:OpacityAnimation To="0.0" Duration="0:0:0.167" />
</animations:Implicit.HideAnimations>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="DefaultState" />
<VisualState x:Name="LoadingState">
<VisualState.Setters>
<Setter Target="Loader.IsLoading" Value="True" />
<Setter Target="InputTxtBox.IsEnabled" Value="False" />
<!--<Setter Target="RecallBtn.IsEnabled" Value="False" />-->
<Setter Target="SendBtn.IsEnabled" Value="False" />
<Setter Target="DisclaimerPresenter.Visibility" Value="Collapsed" />
<Setter Target="LoadingText.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="ErrorState">
<VisualState.Setters>
<Setter Target="InputTxtBox.BorderBrush" Value="{ThemeResource SystemFillColorCriticalBrush}" />
<Setter Target="DisclaimerPresenter.Visibility" Value="Collapsed" />
<Setter Target="ErrorMessageGrid.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</UserControl>

View File

@ -0,0 +1,178 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Net;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Settings;
using AdvancedPaste.ViewModels;
using CommunityToolkit.Mvvm.Input;
using ManagedCommon;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace AdvancedPaste.Controls
{
public sealed partial class PromptBox : Microsoft.UI.Xaml.Controls.UserControl
{
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
private UserSettings _userSettings;
public static readonly DependencyProperty PromptProperty = DependencyProperty.Register(
nameof(Prompt),
typeof(string),
typeof(PromptBox),
new PropertyMetadata(defaultValue: string.Empty));
public OptionsViewModel ViewModel { get; private set; }
public string Prompt
{
get => (string)GetValue(PromptProperty);
set => SetValue(PromptProperty, value);
}
public static readonly DependencyProperty PlaceholderTextProperty = DependencyProperty.Register(
nameof(PlaceholderText),
typeof(string),
typeof(PromptBox),
new PropertyMetadata(defaultValue: string.Empty));
public string PlaceholderText
{
get => (string)GetValue(PlaceholderTextProperty);
set => SetValue(PlaceholderTextProperty, value);
}
public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(
nameof(Footer),
typeof(object),
typeof(PromptBox),
new PropertyMetadata(defaultValue: null));
public object Footer
{
get => (object)GetValue(FooterProperty);
set => SetValue(FooterProperty, value);
}
public PromptBox()
{
this.InitializeComponent();
_userSettings = new UserSettings();
ViewModel = App.GetService<OptionsViewModel>();
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
InputTxtBox.Focus(FocusState.Programmatic);
}
[RelayCommand]
private void GenerateCustom()
{
Logger.LogTrace();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomFormatEvent());
VisualStateManager.GoToState(this, "LoadingState", true);
string inputInstructions = InputTxtBox.Text;
ViewModel.SaveQuery(inputInstructions);
var customFormatTask = ViewModel.GenerateCustomFunction(inputInstructions);
customFormatTask.ContinueWith(
t =>
{
_dispatcherQueue.TryEnqueue(() =>
{
ViewModel.CustomFormatResult = t.Result;
if (ViewModel.ApiRequestStatus == (int)HttpStatusCode.OK)
{
VisualStateManager.GoToState(this, "DefaultState", true);
if (_userSettings.ShowCustomPreview)
{
PreviewGrid.Width = InputTxtBox.ActualWidth;
PreviewFlyout.ShowAt(InputTxtBox);
}
else
{
ViewModel.PasteCustom();
InputTxtBox.Text = string.Empty;
}
}
else
{
VisualStateManager.GoToState(this, "ErrorState", true);
}
});
},
TaskScheduler.Default);
}
[RelayCommand]
private void Recall()
{
Logger.LogTrace();
InputTxtBox.IsEnabled = true;
var lastQuery = ViewModel.RecallPreviousCustomQuery();
if (lastQuery != null)
{
InputTxtBox.Text = lastQuery.Query;
}
ClipboardHelper.SetClipboardTextContent(lastQuery.ClipboardData);
}
private void InputTxtBox_TextChanging(Microsoft.UI.Xaml.Controls.TextBox sender, TextBoxTextChangingEventArgs args)
{
SendBtn.Visibility = InputTxtBox.Text.Length > 0 ? Visibility.Visible : Visibility.Collapsed;
}
private void InputTxtBox_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == Windows.System.VirtualKey.Enter && InputTxtBox.Text.Length > 0)
{
GenerateCustom();
}
}
private void PreviewPasteBtn_Click(object sender, RoutedEventArgs e)
{
ViewModel.PasteCustom();
InputTxtBox.Text = string.Empty;
}
private void ThumbUpDown_Click(object sender, RoutedEventArgs e)
{
if (sender is Button btn)
{
bool result;
if (bool.TryParse(btn.CommandParameter as string, out result))
{
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteCustomFormatOutputThumbUpDownEvent(result));
}
}
}
private void PreviewFlyout_Opened(object sender, object e)
{
PreviewPasteBtn.Focus(FocusState.Programmatic);
}
internal void IsLoading(bool loading)
{
Loader.IsLoading = loading;
}
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media;
namespace AdvancedPaste.Converters
{
public sealed class ListViewIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var presenter = value as ListViewItemPresenter;
var item = VisualTreeHelper.GetParent(presenter) as ListViewItem;
var listView = ItemsControl.ItemsControlFromItemContainer(item);
int index = listView.IndexFromContainer(item) + 1;
#pragma warning disable CA1305 // Specify IFormatProvider
return index.ToString();
#pragma warning restore CA1305 // Specify IFormatProvider
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8" ?>
<winuiex:WindowEx
x:Class="AdvancedPaste.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:pages="using:AdvancedPaste.Pages"
xmlns:winuiex="using:WinUIEx"
Width="420"
Height="308"
MinWidth="420"
MinHeight="308"
Closed="WindowEx_Closed"
IsAlwaysOnTop="True"
IsMaximizable="False"
IsMinimizable="False"
IsResizable="true"
mc:Ignorable="d">
<winuiex:WindowEx.SystemBackdrop>
<DesktopAcrylicBackdrop />
</winuiex:WindowEx.SystemBackdrop>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="titleBar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image
Width="16"
Margin="12,0,0,0"
AutomationProperties.AccessibilityView="Raw"
Source="/Assets/AdvancedPaste/AdvancedPaste.ico" />
<Rectangle
Grid.ColumnSpan="2"
Width="30"
Height="3"
HorizontalAlignment="Center"
Fill="{ThemeResource ControlStrongFillColorDefaultBrush}"
RadiusX="2"
RadiusY="2" />
</Grid>
<pages:MainPage
x:Name="MainPage"
Grid.Row="1"
x:FieldModifier="public" />
</Grid>
</winuiex:WindowEx>

View File

@ -0,0 +1,99 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using AdvancedPaste.Helpers;
using ManagedCommon;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Windows.Graphics;
using WinUIEx;
using WinUIEx.Messaging;
using static AdvancedPaste.Helpers.NativeMethods;
namespace AdvancedPaste
{
public sealed partial class MainWindow : WindowEx, IDisposable
{
private WindowMessageMonitor _msgMonitor;
private bool _disposedValue;
public MainWindow()
{
this.InitializeComponent();
AppWindow.SetIcon("Assets/AdvancedPaste/AdvancedPaste.ico");
this.ExtendsContentIntoTitleBar = true;
this.SetTitleBar(titleBar);
var loader = ResourceLoaderInstance.ResourceLoader;
Title = loader.GetString("WindowTitle");
_msgMonitor = new WindowMessageMonitor(this);
_msgMonitor.WindowMessageReceived += (_, e) =>
{
const int WM_NCLBUTTONDBLCLK = 0x00A3;
if (e.Message.MessageId == WM_NCLBUTTONDBLCLK)
{
// Disable double click on title bar to maximize window
e.Result = 0;
e.Handled = true;
}
};
WindowHelpers.BringToForeground(this.GetWindowHandle());
}
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
_msgMonitor?.Dispose();
_disposedValue = true;
}
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
{
Windows.Win32.PInvoke.ShowWindow((Windows.Win32.Foundation.HWND)this.GetWindowHandle(), Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
args.Handled = true;
}
public void SetFocus()
{
MainPage.CustomFormatTextBox.InputTxtBox.Focus(FocusState.Programmatic);
}
public void ClearInputText()
{
MainPage.CustomFormatTextBox.InputTxtBox.Text = string.Empty;
}
internal void StartLoading()
{
MainPage.CustomFormatTextBox.IsLoading(true);
}
internal void FinishLoading(bool success)
{
MainPage.CustomFormatTextBox.IsLoading(false);
if (success)
{
VisualStateManager.GoToState(MainPage.CustomFormatTextBox, "DefaultState", true);
}
}
}
}

View File

@ -0,0 +1,274 @@
<Page
x:Class="AdvancedPaste.Pages.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:AdvancedPaste.Controls"
xmlns:converters="using:AdvancedPaste.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:AdvancedPaste.Models"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="using:CommunityToolkit.WinUI"
KeyDown="Page_KeyDown"
KeyboardAcceleratorPlacementMode="Hidden"
mc:Ignorable="d">
<Page.Resources>
<converters:ListViewIndexConverter x:Name="listViewIndexConverter" />
<Style
x:Key="PaddingLessFlyoutPresenterStyle"
BasedOn="{StaticResource DefaultFlyoutPresenterStyle}"
TargetType="FlyoutPresenter">
<Style.Setters>
<Setter Property="Padding" Value="0" />
</Style.Setters>
</Style>
</Page.Resources>
<Page.KeyboardAccelerators>
<KeyboardAccelerator Key="Escape" Invoked="KeyboardAccelerator_Invoked" />
<KeyboardAccelerator
Key="Number1"
Invoked="KeyboardAccelerator_Invoked"
Modifiers="Control" />
<KeyboardAccelerator
Key="Number2"
Invoked="KeyboardAccelerator_Invoked"
Modifiers="Control" />
<KeyboardAccelerator
Key="Number3"
Invoked="KeyboardAccelerator_Invoked"
Modifiers="Control" />
</Page.KeyboardAccelerators>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<controls:PromptBox
x:Name="CustomFormatTextBox"
x:Uid="CustomFormatTextBox"
Margin="8,4,8,0"
x:FieldModifier="public"
TabIndex="0">
<controls:PromptBox.Footer>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="0,0,2,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}">
<Run x:Uid="AIMistakeNote" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
</TextBlock>
<TextBlock
Margin="4,0,2,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}">
<Hyperlink
x:Name="TermsHyperlink"
NavigateUri="https://openai.com/policies/terms-of-use"
TabIndex="3">
<Run x:Uid="TermsLink" />
</Hyperlink>
<ToolTipService.ToolTip>
<TextBlock Text="https://openai.com/policies/terms-of-use" />
</ToolTipService.ToolTip>
</TextBlock>
<TextBlock
Margin="0,0,2,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
ToolTipService.ToolTip="">
<Run x:Uid="AIFooterSeparator" Foreground="{ThemeResource TextFillColorSecondaryBrush}">|</Run>
</TextBlock>
<TextBlock
Margin="0,0,2,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}">
<Hyperlink NavigateUri="https://openai.com/policies/privacy-policy" TabIndex="3">
<Run x:Uid="PrivacyLink" />
</Hyperlink>
<ToolTipService.ToolTip>
<TextBlock Text="https://openai.com/policies/privacy-policy" />
</ToolTipService.ToolTip>
</TextBlock>
</StackPanel>
</controls:PromptBox.Footer>
</controls:PromptBox>
<Grid
Grid.Row="2"
Background="{ThemeResource LayerOnAcrylicFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="0,1,0,0"
RowSpacing="4">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView
x:Name="PasteOptionsListView"
VerticalAlignment="Bottom"
IsItemClickEnabled="True"
ItemClick="PasteOptionsListView_ItemClick"
ItemContainerTransitions="{x:Null}"
ItemsSource="{x:Bind pasteFormats, Mode=OneWay}"
SelectionMode="None"
TabIndex="1">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:PasteFormat">
<Grid>
<ToolTipService.ToolTip>
<TextBlock>
<Run Text="{x:Bind Name}" />
<Run Text="(" /><Run Text="Ctrl" /><Run Text="+" /><Run Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource listViewIndexConverter}}" /><Run Text=")" />
</TextBlock>
</ToolTipService.ToolTip>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="26" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Viewbox
x:Name="IconHolderBox"
MaxWidth="16"
MaxHeight="16"
HorizontalAlignment="Left"
VerticalAlignment="Center">
<ContentPresenter
x:Name="IconHolder"
x:Phase="2"
Content="{x:Bind Icon}" />
</Viewbox>
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
x:Phase="1"
Text="{x:Bind Name}" />
<TextBlock
Grid.Column="2"
Margin="0,0,8,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}">
<Run Text="Ctrl" /><Run Text="+" /><Run Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource listViewIndexConverter}}" />
</TextBlock>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Rectangle
Grid.Row="1"
Height="1"
HorizontalAlignment="Stretch"
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
<!-- x:Uid="ClipboardHistoryButton" -->
<Button
Grid.Row="2"
Height="32"
Margin="4,0,4,4"
Padding="{StaticResource ButtonPadding}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
IsEnabled="{x:Bind ViewModel.ClipboardHistoryEnabled, Mode=TwoWay}"
Style="{StaticResource SubtleButtonStyle}">
<Grid
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<FontIcon
Margin="0,0,0,0"
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
FontSize="16"
Glyph="&#xE81C;" />
<TextBlock
x:Uid="ClipboardHistoryButton"
Grid.Column="1"
VerticalAlignment="Center" />
<FontIcon
Grid.Column="2"
AutomationProperties.AccessibilityView="Raw"
FontSize="12"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Glyph="&#xE974;" />
</Grid>
<Button.Flyout>
<Flyout
FlyoutPresenterStyle="{StaticResource PaddingLessFlyoutPresenterStyle}"
Placement="Right"
ShouldConstrainToRootBounds="False">
<ListView
Width="320"
IsItemClickEnabled="True"
ItemClick="ClipboardHistory_ItemClick"
ItemsSource="{x:Bind clipboardHistory, Mode=OneWay}"
SelectionMode="None">
<ListView.Transitions />
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:ClipboardItem">
<Grid
Height="40"
HorizontalAlignment="Stretch"
ColumnSpacing="8"
ToolTipService.ToolTip="{x:Bind Content}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MaxWidth="240" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
HorizontalAlignment="Left"
x:Phase="2"
Source="{x:Bind Image}"
Visibility="Visible" />
<TextBlock
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
x:Phase="1"
Text="{x:Bind Content}"
TextTrimming="CharacterEllipsis"
Visibility="Visible" />
<Button
x:Name="moreInfo"
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource SubtleButtonStyle}">
<Button.Content>
<FontIcon FontSize="16" Glyph="&#xe712;" />
</Button.Content>
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem
x:Uid="ClipboardHistoryItemDeleteButton"
Click="ClipboardHistoryItemDeleteButton_Click"
CommandParameter="{x:Bind (local:ClipboardItem)}"
Icon="Delete" />
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Flyout>
</Button.Flyout>
</Button>
</Grid>
</Grid>
</Page>

View File

@ -0,0 +1,241 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.ViewModels;
using ManagedCommon;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
using Windows.System;
namespace AdvancedPaste.Pages
{
public sealed partial class MainPage : Page
{
private readonly ObservableCollection<ClipboardItem> clipboardHistory;
private readonly ObservableCollection<PasteFormat> pasteFormats;
private readonly Microsoft.UI.Dispatching.DispatcherQueue _dispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread();
public OptionsViewModel ViewModel { get; private set; }
public MainPage()
{
this.InitializeComponent();
pasteFormats =
[
new PasteFormat { Icon = new FontIcon() { Glyph = "\uE8E9" }, Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsPlainText"), Format = PasteFormats.PlainText },
new PasteFormat { Icon = new FontIcon() { Glyph = "\ue8a5" }, Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsMarkdown"), Format = PasteFormats.Markdown },
new PasteFormat { Icon = new FontIcon() { Glyph = "\uE943" }, Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsJson"), Format = PasteFormats.Json },
];
ViewModel = App.GetService<OptionsViewModel>();
clipboardHistory = new ObservableCollection<ClipboardItem>();
LoadClipboardHistoryEvent(null, null);
Clipboard.HistoryChanged += LoadClipboardHistoryEvent;
}
private void LoadClipboardHistoryEvent(object sender, object e)
{
Task.Run(() =>
{
LoadClipboardHistoryAsync();
});
}
public async void LoadClipboardHistoryAsync()
{
try
{
Logger.LogTrace();
List<ClipboardItem> items = new();
if (Clipboard.IsHistoryEnabled())
{
var historyItems = await Clipboard.GetHistoryItemsAsync();
if (historyItems.Status == ClipboardHistoryItemsResultStatus.Success)
{
foreach (var item in historyItems.Items)
{
if (item.Content.Contains(StandardDataFormats.Text))
{
string text = await item.Content.GetTextAsync();
items.Add(new ClipboardItem { Content = text, Item = item });
}
else if (item.Content.Contains(StandardDataFormats.Bitmap))
{
items.Add(new ClipboardItem { Item = item });
}
}
}
}
_dispatcherQueue.TryEnqueue(async () =>
{
clipboardHistory.Clear();
foreach (var item in items)
{
if (item.Item.Content.Contains(StandardDataFormats.Bitmap))
{
IRandomAccessStreamReference imageReceived = null;
imageReceived = await item.Item.Content.GetBitmapAsync();
if (imageReceived != null)
{
using (var imageStream = await imageReceived.OpenReadAsync())
{
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
item.Image = bitmapImage;
}
}
}
clipboardHistory.Add(item);
}
});
}
catch (Exception ex)
{
Logger.LogError("Loading clipboard history failed", ex);
}
}
private void ClipboardHistoryItemDeleteButton_Click(object sender, RoutedEventArgs e)
{
Logger.LogTrace();
if (sender is MenuFlyoutItem btn)
{
ClipboardItem item = btn.CommandParameter as ClipboardItem;
Clipboard.DeleteItemFromHistory(item.Item);
clipboardHistory.Remove(item);
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteClipboardItemDeletedEvent());
}
}
private void PasteAsPlain()
{
ViewModel.ToPlainTextFunction();
}
private void PasteAsMarkdown()
{
ViewModel.ToMarkdownFunction();
}
private void PasteAsJson()
{
ViewModel.ToJsonFunction();
}
private void PasteOptionsListView_ItemClick(object sender, ItemClickEventArgs e)
{
if (e.ClickedItem is PasteFormat format)
{
switch (format.Format)
{
case PasteFormats.PlainText:
{
PasteAsPlain();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteFormatClickedEvent(PasteFormats.PlainText));
break;
}
case PasteFormats.Markdown:
{
PasteAsMarkdown();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteFormatClickedEvent(PasteFormats.Markdown));
break;
}
case PasteFormats.Json:
{
PasteAsJson();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteFormatClickedEvent(PasteFormats.Json));
break;
}
}
}
}
private void KeyboardAccelerator_Invoked(Microsoft.UI.Xaml.Input.KeyboardAccelerator sender, Microsoft.UI.Xaml.Input.KeyboardAcceleratorInvokedEventArgs args)
{
Logger.LogTrace();
switch (sender.Key)
{
case VirtualKey.Escape:
{
(App.Current as App).GetMainWindow().Close();
break;
}
case VirtualKey.Number1:
{
PasteAsPlain();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteInAppKeyboardShortcutEvent(PasteFormats.PlainText));
break;
}
case VirtualKey.Number2:
{
PasteAsMarkdown();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteInAppKeyboardShortcutEvent(PasteFormats.Markdown));
break;
}
case VirtualKey.Number3:
{
PasteAsJson();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteInAppKeyboardShortcutEvent(PasteFormats.Json));
break;
}
default:
break;
}
}
private void Page_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Escape)
{
(App.Current as App).GetMainWindow().Close();
}
}
private async void ClipboardHistory_ItemClick(object sender, ItemClickEventArgs e)
{
var item = e.ClickedItem as ClipboardItem;
if (item is not null)
{
if (!string.IsNullOrEmpty(item.Content))
{
ClipboardHelper.SetClipboardTextContent(item.Content);
}
else if (item.Image is not null)
{
RandomAccessStreamReference image = null;
image = await item.Item.Content.GetBitmapAsync();
ClipboardHelper.SetClipboardImageContent(image);
}
}
}
}
}

View File

@ -0,0 +1,9 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AdvancedPaste">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///AdvancedPasteXAML/Controls/AnimatedContentControl/AnimatedContentControl.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 637 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,133 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.IO;
using System.Net;
using Azure;
using Azure.AI.OpenAI;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Telemetry;
using Windows.Security.Credentials;
namespace AdvancedPaste.Helpers
{
public class AICompletionsHelper
{
// Return Response and Status code from the request.
public struct AICompletionsResponse
{
public AICompletionsResponse(string response, int apiRequestStatus)
{
Response = response;
ApiRequestStatus = apiRequestStatus;
}
public string Response { get; }
public int ApiRequestStatus { get; }
}
private string _openAIKey;
public bool IsAIEnabled => !string.IsNullOrEmpty(this._openAIKey);
public AICompletionsHelper()
{
this._openAIKey = LoadOpenAIKey();
}
public void SetOpenAIKey(string openAIKey)
{
this._openAIKey = openAIKey;
}
public string GetKey()
{
return _openAIKey;
}
public static string LoadOpenAIKey()
{
PasswordVault vault = new PasswordVault();
try
{
PasswordCredential cred = vault.Retrieve("https://platform.openai.com/api-keys", "PowerToys_AdvancedPaste_OpenAIKey");
if (cred is not null)
{
return cred.Password.ToString();
}
}
catch (Exception)
{
}
return string.Empty;
}
public string GetAICompletion(string systemInstructions, string userMessage)
{
OpenAIClient azureAIClient = new OpenAIClient(_openAIKey);
var response = azureAIClient.GetCompletions(
new CompletionsOptions()
{
DeploymentName = "gpt-3.5-turbo-instruct",
Prompts =
{
systemInstructions + "\n\n" + userMessage,
},
Temperature = 0.01F,
MaxTokens = 2000,
});
if (response.Value.Choices[0].FinishReason == "length")
{
Console.WriteLine("Cut off due to length constraints");
}
return response.Value.Choices[0].Text;
}
public AICompletionsResponse AIFormatString(string inputInstructions, string inputString)
{
string systemInstructions = $@"You are tasked with reformatting user's clipboard data. Use the user's instructions, and the content of their clipboard below to edit their clipboard content as they have requested it.
Do not output anything else besides the reformatted clipboard content.";
string userMessage = $@"User instructions:
{inputInstructions}
Clipboard Content:
{inputString}
Output:
";
string aiResponse = null;
int apiRequestStatus = (int)HttpStatusCode.OK;
try
{
aiResponse = this.GetAICompletion(systemInstructions, userMessage);
}
catch (Azure.RequestFailedException error)
{
Logger.LogError("GetAICompletion failed", error);
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomErrorEvent(error.Message));
apiRequestStatus = error.Status;
}
catch (Exception error)
{
Logger.LogError("GetAICompletion failed", error);
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomErrorEvent(error.Message));
apiRequestStatus = -1;
}
return new AICompletionsResponse(aiResponse, apiRequestStatus);
}
}
}

View File

@ -0,0 +1,139 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
using Windows.System;
namespace AdvancedPaste.Helpers
{
internal static class ClipboardHelper
{
internal static void SetClipboardTextContent(string text)
{
Logger.LogTrace();
if (!string.IsNullOrEmpty(text))
{
DataPackage output = new();
output.SetText(text);
Clipboard.SetContentWithOptions(output, null);
// TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey.
// Calling inside a loop makes it work.
bool flushed = false;
for (int i = 0; i < 5; i++)
{
if (flushed)
{
break;
}
try
{
Task.Run(() =>
{
Clipboard.Flush();
}).Wait();
flushed = true;
}
catch (Exception ex)
{
Logger.LogError("Clipboard.Flush() failed", ex);
}
}
}
}
internal static void SetClipboardImageContent(RandomAccessStreamReference image)
{
Logger.LogTrace();
if (image is not null)
{
DataPackage output = new();
output.SetBitmap(image);
Clipboard.SetContentWithOptions(output, null);
// TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey.
// Calling inside a loop makes it work.
bool flushed = false;
for (int i = 0; i < 5; i++)
{
if (flushed)
{
break;
}
try
{
Task.Run(() =>
{
Clipboard.Flush();
}).Wait();
flushed = true;
}
catch (Exception ex)
{
Logger.LogError("Clipboard.Flush() failed", ex);
}
}
}
}
// Function to send a single key event
private static void SendSingleKeyboardInput(short keyCode, uint keyStatus)
{
UIntPtr ignoreKeyEventFlag = (UIntPtr)0x5555;
NativeMethods.INPUT inputShift = new NativeMethods.INPUT
{
type = NativeMethods.INPUTTYPE.INPUT_KEYBOARD,
data = new NativeMethods.InputUnion
{
ki = new NativeMethods.KEYBDINPUT
{
wVk = keyCode,
dwFlags = keyStatus,
// Any keyevent with the extraInfo set to this value will be ignored by the keyboard hook and sent to the system instead.
dwExtraInfo = ignoreKeyEventFlag,
},
},
};
NativeMethods.INPUT[] inputs = new NativeMethods.INPUT[] { inputShift };
_ = NativeMethods.SendInput(1, inputs, NativeMethods.INPUT.Size);
}
internal static void SendPasteKeyCombination()
{
Logger.LogTrace();
SendSingleKeyboardInput((short)VirtualKey.LeftControl, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.RightControl, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.LeftWindows, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.RightWindows, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.LeftShift, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.RightShift, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.LeftMenu, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.RightMenu, (uint)NativeMethods.KeyEventF.KeyUp);
// Send Ctrl + V
SendSingleKeyboardInput((short)VirtualKey.Control, (uint)NativeMethods.KeyEventF.KeyDown);
SendSingleKeyboardInput((short)VirtualKey.V, (uint)NativeMethods.KeyEventF.KeyDown);
SendSingleKeyboardInput((short)VirtualKey.V, (uint)NativeMethods.KeyEventF.KeyUp);
SendSingleKeyboardInput((short)VirtualKey.Control, (uint)NativeMethods.KeyEventF.KeyUp);
Logger.LogInfo("Paste sent");
}
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace AdvancedPaste.Helpers
{
internal static class Constants
{
internal static readonly string AdvancedPasteModuleName = "AdvancedPaste";
internal static readonly string LastQueryJsonFileName = "lastQuery.json";
}
}

View File

@ -0,0 +1,13 @@
// 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 AdvancedPaste.Settings
{
public interface IUserSettings
{
public bool ShowCustomPreview { get; }
public bool SendPasteKeyCombination { get; }
}
}

View File

@ -0,0 +1,72 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Xml;
using ManagedCommon;
using Newtonsoft.Json;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.Helpers
{
internal static class JsonHelper
{
internal static string ToJsonFromXmlOrCsv(DataPackageView clipboardData)
{
Logger.LogTrace();
if (clipboardData == null || !clipboardData.Contains(StandardDataFormats.Text))
{
Logger.LogWarning("Clipboard does not contain text data");
return string.Empty;
}
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits
string text = Task.Run(async () =>
{
string plainText = await clipboardData.GetTextAsync() as string;
return plainText;
}).Result;
#pragma warning restore VSTHRD002 // Avoid problematic synchronous waits
string jsonText = string.Empty;
// Try convert XML
try
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(text);
jsonText = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.Indented);
}
catch (Exception ex)
{
Logger.LogError("Failed parsing input as xml", ex);
}
// Try convert CSV
try
{
if (string.IsNullOrEmpty(jsonText))
{
var csv = new List<string[]>();
foreach (var line in text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries))
{
csv.Add(line.Split(","));
}
jsonText = JsonConvert.SerializeObject(csv, Newtonsoft.Json.Formatting.Indented);
}
}
catch (Exception ex)
{
Logger.LogError("Failed parsing input as csv", ex);
}
return string.IsNullOrEmpty(jsonText) ? text : jsonText;
}
}
}

View File

@ -0,0 +1,171 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using HtmlAgilityPack;
using ManagedCommon;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.Helpers
{
internal static class MarkdownHelper
{
public static string ToMarkdown(DataPackageView clipboardData)
{
Logger.LogTrace();
if (clipboardData == null)
{
Logger.LogWarning("Clipboard does not contain data");
return string.Empty;
}
string data = string.Empty;
if (clipboardData.Contains(StandardDataFormats.Html))
{
data = Task.Run(async () =>
{
string data = await clipboardData.GetHtmlFormatAsync() as string;
return data;
}).Result;
}
else if (clipboardData.Contains(StandardDataFormats.Text))
{
data = Task.Run(async () =>
{
string plainText = await clipboardData.GetTextAsync() as string;
return plainText;
}).Result;
}
if (!string.IsNullOrEmpty(data))
{
string cleanedHtml = CleanHtml(data);
return ConvertHtmlToMarkdown(cleanedHtml);
}
return string.Empty;
}
public static string PasteAsPlainTextFromClipboard(DataPackageView clipboardData)
{
Logger.LogTrace();
if (clipboardData != null)
{
if (!clipboardData.Contains(StandardDataFormats.Text))
{
Logger.LogWarning("Clipboard does not contain text data");
return string.Empty;
}
return Task.Run(async () =>
{
string plainText = await clipboardData.GetTextAsync() as string;
return plainText;
}).Result;
}
return string.Empty;
}
private static string CleanHtml(string html)
{
Logger.LogTrace();
// Remove the "StartFragment" and "EndFragment" comments
html = Regex.Replace(html, @"<!--StartFragment-->|<!--EndFragment-->", string.Empty);
HtmlDocument document = new HtmlDocument();
document.LoadHtml(html);
// Remove unwanted HTML elements
RemoveUnwantedElements(document.DocumentNode);
// Remove inline styles
RemoveInlineStyles(document.DocumentNode);
// Clean up line breaks and whitespace
CleanUpWhitespace(document.DocumentNode);
// Serialize the cleaned HTML back to string
using (var writer = new System.IO.StringWriter())
{
document.Save(writer);
return writer.ToString();
}
}
private static void RemoveUnwantedElements(HtmlNode node)
{
Logger.LogTrace();
// Remove specific elements by tag name, CSS class, or other attributes
// Example: Remove all <script> elements
foreach (var scriptNode in node.DescendantsAndSelf("script").ToArray())
{
scriptNode.Remove();
}
// Ignore specific elements like <sup> elements
foreach (var ignoredElement in node.DescendantsAndSelf("sup").ToArray())
{
ignoredElement.Remove();
}
// Ignore specific elements like <o:p> element added by MS Word
foreach (var ignoredElement in node.DescendantsAndSelf("o:p").ToArray())
{
ignoredElement.Remove();
}
}
private static void RemoveInlineStyles(HtmlNode node)
{
Logger.LogTrace();
// Remove inline styles from elements
foreach (var elementNode in node.DescendantsAndSelf())
{
elementNode.Attributes.Remove("style");
}
}
private static void CleanUpWhitespace(HtmlNode node)
{
Logger.LogTrace();
// Clean up line breaks and excessive whitespace
if (node.NodeType == HtmlNodeType.Text)
{
node.InnerHtml = Regex.Replace(node.InnerHtml, @"\s{2,}", " ");
node.InnerHtml = Regex.Replace(node.InnerHtml, @"[\r\n]+", string.Empty);
}
else
{
foreach (var childNode in node.ChildNodes.ToArray())
{
CleanUpWhitespace(childNode);
}
}
}
private static string ConvertHtmlToMarkdown(string html)
{
Logger.LogTrace();
// Perform the conversion from HTML to Markdown using your chosen library or method
var converter = new ReverseMarkdown.Converter();
string markdown = converter.Convert(html);
return markdown;
}
}
}

View File

@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
using Microsoft.UI.Dispatching;
namespace AdvancedPaste.Helpers
{
public static class NativeEventWaiter
{
public static void WaitForEventLoop(string eventName, Action callback)
{
var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
new Thread(() =>
{
var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
while (true)
{
if (eventHandle.WaitOne())
{
dispatcherQueue.TryEnqueue(() => callback());
}
}
}).Start();
}
}
}

View File

@ -0,0 +1,104 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
namespace AdvancedPaste.Helpers
{
internal static class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct INPUT
{
internal INPUTTYPE type;
internal InputUnion data;
internal static int Size
{
get { return Marshal.SizeOf(typeof(INPUT)); }
}
}
[StructLayout(LayoutKind.Explicit)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct InputUnion
{
[FieldOffset(0)]
internal MOUSEINPUT mi;
[FieldOffset(0)]
internal KEYBDINPUT ki;
[FieldOffset(0)]
internal HARDWAREINPUT hi;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct MOUSEINPUT
{
internal int dx;
internal int dy;
internal int mouseData;
internal uint dwFlags;
internal uint time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct KEYBDINPUT
{
internal short wVk;
internal short wScan;
internal uint dwFlags;
internal int time;
internal UIntPtr dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching Native Structure")]
internal struct HARDWAREINPUT
{
internal int uMsg;
internal short wParamL;
internal short wParamH;
}
internal enum INPUTTYPE : uint
{
INPUT_MOUSE = 0,
INPUT_KEYBOARD = 1,
INPUT_HARDWARE = 2,
}
[Flags]
internal enum KeyEventF
{
KeyDown = 0x0000,
ExtendedKey = 0x0001,
KeyUp = 0x0002,
Unicode = 0x0004,
Scancode = 0x0008,
}
[DllImport("user32.dll")]
internal static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern short GetAsyncKeyState(int vKey);
[StructLayout(LayoutKind.Sequential)]
internal struct PointInter
{
public int X;
public int Y;
public static explicit operator System.Windows.Point(PointInter point) => new System.Windows.Point(point.X, point.Y);
}
[DllImport("user32.dll")]
internal static extern bool GetCursorPos(out PointInter lpPoint);
}
}

View File

@ -0,0 +1,17 @@
// 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.Windows.ApplicationModel.Resources;
namespace AdvancedPaste.Helpers
{
internal static class ResourceLoaderInstance
{
internal static ResourceLoader ResourceLoader { get; private set; }
static ResourceLoaderInstance()
{
ResourceLoader = new ResourceLoader("PowerToys.AdvancedPaste.pri");
}
}
}

View File

@ -0,0 +1,82 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO.Abstractions;
using System.Threading;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
namespace AdvancedPaste.Settings
{
internal sealed class UserSettings : IUserSettings
{
private readonly SettingsUtils _settingsUtils;
private readonly IFileSystemWatcher _watcher;
private readonly object _loadingSettingsLock = new object();
private const string AdvancedPasteModuleName = "AdvancedPaste";
private const int MaxNumberOfRetry = 5;
public bool ShowCustomPreview { get; private set; }
public bool SendPasteKeyCombination { get; private set; }
public UserSettings()
{
_settingsUtils = new SettingsUtils();
ShowCustomPreview = true;
SendPasteKeyCombination = true;
LoadSettingsFromJson();
_watcher = Helper.GetFileWatcher(AdvancedPasteModuleName, "settings.json", () => LoadSettingsFromJson());
}
private void LoadSettingsFromJson()
{
lock (_loadingSettingsLock)
{
var retry = true;
var retryCount = 0;
while (retry)
{
try
{
retryCount++;
if (!_settingsUtils.SettingsExists(AdvancedPasteModuleName))
{
Logger.LogInfo("AdvancedPaste settings.json was missing, creating a new one");
var defaultSettings = new AdvancedPasteSettings();
defaultSettings.Save(_settingsUtils);
}
var settings = _settingsUtils.GetSettingsOrDefault<AdvancedPasteSettings>(AdvancedPasteModuleName);
if (settings != null)
{
ShowCustomPreview = settings.Properties.ShowCustomPreview;
SendPasteKeyCombination = settings.Properties.SendPasteKeyCombination;
}
retry = false;
}
catch (Exception ex)
{
if (retryCount > MaxNumberOfRetry)
{
retry = false;
}
Logger.LogError("Failed to read changed settings", ex);
Thread.Sleep(500);
}
}
}
}
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.UI.Xaml.Media.Imaging;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.Models
{
public class ClipboardItem
{
public string Content { get; set; }
public ClipboardHistoryItem Item { get; set; }
public BitmapImage Image { get; set; }
}
}

View File

@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using AdvancedPaste.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace AdvancedPaste.Models
{
internal sealed class CustomQuery : ISettingsConfig
{
public string Query { get; set; }
public string ClipboardData { get; set; }
public string GetModuleName() => Constants.AdvancedPasteModuleName;
public string ToJsonString() => JsonSerializer.Serialize(this);
public override string ToString()
=> JsonSerializer.Serialize(this);
public bool UpgradeSettingsConfiguration() => false;
}
}

View File

@ -0,0 +1,17 @@
// 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.UI.Xaml.Controls;
namespace AdvancedPaste.Models
{
public class PasteFormat
{
public IconElement Icon { get; set; }
public string Name { get; set; }
public PasteFormats Format { get; set; }
}
}

View File

@ -0,0 +1,14 @@
// 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 AdvancedPaste.Models
{
public enum PasteFormats
{
PlainText,
Markdown,
Json,
Custom,
}
}

View File

@ -0,0 +1 @@
ShowWindow

View File

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Threading;
using ManagedCommon;
using Microsoft.UI.Dispatching;
using Microsoft.Windows.AppLifecycle;
namespace AdvancedPaste
{
public static class Program
{
[STAThread]
public static void Main(string[] args)
{
Logger.InitializeLogger("\\AdvancedPaste\\Logs");
WinRT.ComWrappersSupport.InitializeComWrappers();
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredAdvancedPasteEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
{
Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
return;
}
var instanceKey = AppInstance.FindOrRegisterForKey("PowerToys_AdvancedPaste_Instance");
if (instanceKey.IsCurrent)
{
Microsoft.UI.Xaml.Application.Start((p) =>
{
var context = new DispatcherQueueSynchronizationContext(DispatcherQueue.GetForCurrentThread());
SynchronizationContext.SetSynchronizationContext(context);
_ = new App();
});
}
else
{
Logger.LogWarning("Another instance of AdvancedPasteUI is running. Exiting.");
}
}
}
}

View File

@ -0,0 +1,228 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root"
xmlns=""
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AIMistakeNote.Text" xml:space="preserve">
<value>AI can make mistakes.</value>
</data>
<data name="ClipboardDataTypeMismatchWarning" xml:space="preserve">
<value>Clipboard data is not text</value>
</data>
<data name="OpenAINotConfigured" xml:space="preserve">
<value>To custom with AI not enabled</value>
</data>
<data name="OpenAIApiKeyUnauthorized" xml:space="preserve">
<value>Invalid API key or endpoint</value>
</data>
<data name="OpenAIApiKeyTooManyRequests" xml:space="preserve">
<value>API key quota exceeded</value>
</data>
<data name="OpenAIApiKeyError" xml:space="preserve">
<value>OpenAI request failed with status code: </value>
</data>
<data name="ClipboardHistoryButton.Text" xml:space="preserve">
<value>Clipboard history</value>
</data>
<data name="ClipboardHistoryItemDeleteButton.Text" xml:space="preserve">
<value>Delete</value>
</data>
<data name="CustomFormatTextBox.PlaceholderText" xml:space="preserve">
<value>Describe what format you want..</value>
</data>
<data name="InputTxtBoxTooltip.Text" xml:space="preserve">
<value>Describe what format you want..</value>
</data>
<data name="LearnMoreLink.Text" xml:space="preserve">
<value>Privacy</value>
</data>
<data name="LoadingText.Text" xml:space="preserve">
<value>Connecting to AI services and generating output..</value>
</data>
<data name="PasteAsJson" xml:space="preserve">
<value>Paste as JSON</value>
</data>
<data name="PasteAsMarkdown" xml:space="preserve">
<value>Paste as markdown</value>
</data>
<data name="PasteAsPlainText" xml:space="preserve">
<value>Paste as plain text</value>
</data>
<data name="PasteButtonAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Paste</value>
</data>
<data name="PasteText.Text" xml:space="preserve">
<value>Paste</value>
</data>
<data name="ResultTitleTxt.Text" xml:space="preserve">
<value>Result</value>
</data>
<data name="RecallBtnToolTip.Text" xml:space="preserve">
<value>Revert to the last prompt and clipboard data</value>
</data>
<data name="RecallButtonAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Revert to the last prompt and clipboard data</value>
</data>
<data name="SendBtnToolTip.Text" xml:space="preserve">
<value>Generate and paste data</value>
</data>
<data name="RegenerateBtnToolTip.Text" xml:space="preserve">
<value>Regenerate</value>
</data>
<data name="RegenerateBtnAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Regenerate</value>
</data>
<data name="SendButtonAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Generate and paste data</value>
</data>
<data name="SettingsBtn.Content" xml:space="preserve">
<value>Open settings</value>
</data>
<data name="SettingsBtn.[using:Microsoft.UI.Xaml.Controls]ToolTipService.ToolTip" xml:space="preserve">
<value>Open settings</value>
</data>
<data name="ThumbsDownFeedback.Text" xml:space="preserve">
<value>Thumbs down feedback</value>
</data>
<data name="ThumbsUpFeedback.Text" xml:space="preserve">
<value>Thumbs up feedback</value>
</data>
<data name="WindowTitle" xml:space="preserve">
<value>Advanced Paste</value>
</data>
<data name="PreviousResultBtnToolTip.Text" xml:space="preserve">
<value>Previous result</value>
</data>
<data name="PreviousResultBtnAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Previous result</value>
</data>
<data name="NextResultBtnToolTip.Text" xml:space="preserve">
<value>Next result</value>
</data>
<data name="NextResultBtnAutomation.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
<value>Next result</value>
</data>
<data name="PrivacyLink.Text" xml:space="preserve">
<value>OpenAI Privacy</value>
</data>
<data name="TermsLink.Text" xml:space="preserve">
<value>OpenAI Terms</value>
</data>
</root>

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry
{
[EventData]
public class AdvancedPasteClipboardItemDeletedEvent : EventBase, IEvent
{
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry
{
[EventData]
public class AdvancedPasteCustomFormatOutputThumbUpDownEvent : EventBase, IEvent
{
public bool PositiveFeedback { get; set; }
public AdvancedPasteCustomFormatOutputThumbUpDownEvent(bool positiveFeedback)
{
PositiveFeedback = positiveFeedback;
}
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry
{
[EventData]
public class AdvancedPasteFormatClickedEvent : EventBase, IEvent
{
public PasteFormats PasteFormat { get; set; }
public AdvancedPasteFormatClickedEvent(PasteFormats format)
{
this.PasteFormat = format;
}
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry
{
[EventData]
public class AdvancedPasteGenerateCustomErrorEvent : EventBase, IEvent
{
public string Error { get; set; }
public AdvancedPasteGenerateCustomErrorEvent(string error)
{
this.Error = error;
}
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry
{
[EventData]
public class AdvancedPasteGenerateCustomFormatEvent : EventBase, IEvent
{
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Diagnostics.Tracing;
using AdvancedPaste.Models;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry
{
[EventData]
public class AdvancedPasteInAppKeyboardShortcutEvent : EventBase, IEvent
{
public PasteFormats PasteFormat { get; set; }
public AdvancedPasteInAppKeyboardShortcutEvent(PasteFormats format)
{
this.PasteFormat = format;
}
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@ -0,0 +1,410 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Settings;
using Common.UI;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.Win32;
using Windows.ApplicationModel.DataTransfer;
using WinUIEx;
namespace AdvancedPaste.ViewModels
{
public partial class OptionsViewModel : ObservableObject
{
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
private App app = App.Current as App;
private AICompletionsHelper aiHelper;
private UserSettings _userSettings;
public DataPackageView ClipboardData { get; set; }
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))]
[NotifyPropertyChangedFor(nameof(IsCustomAIEnabled))]
private bool _isClipboardDataText;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))]
private bool _isCustomAIEnabled;
[ObservableProperty]
private bool _clipboardHistoryEnabled;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(InputTxtBoxErrorText))]
private int _apiRequestStatus;
public OptionsViewModel()
{
aiHelper = new AICompletionsHelper();
_userSettings = new UserSettings();
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
ApiRequestStatus = (int)HttpStatusCode.OK;
GeneratedResponses = new ObservableCollection<string>();
GeneratedResponses.CollectionChanged += (s, e) =>
{
OnPropertyChanged(nameof(HasMultipleResponses));
OnPropertyChanged(nameof(CurrentIndexDisplay));
};
ClipboardHistoryEnabled = IsClipboardHistoryEnabled();
GetClipboardData();
}
public void GetClipboardData()
{
ClipboardData = Clipboard.GetContent();
IsClipboardDataText = ClipboardData.Contains(StandardDataFormats.Text);
}
public void OnShow()
{
GetClipboardData();
var openAIKey = AICompletionsHelper.LoadOpenAIKey();
var currentKey = aiHelper.GetKey();
bool keyChanged = openAIKey != currentKey;
if (keyChanged)
{
app.GetMainWindow().StartLoading();
Task.Run(() =>
{
aiHelper.SetOpenAIKey(openAIKey);
}).ContinueWith(
(t) =>
{
_dispatcherQueue.TryEnqueue(() =>
{
app.GetMainWindow().FinishLoading(aiHelper.IsAIEnabled);
OnPropertyChanged(nameof(InputTxtBoxPlaceholderText));
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
});
},
TaskScheduler.Default);
}
else
{
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;
}
ClipboardHistoryEnabled = IsClipboardHistoryEnabled();
GeneratedResponses.Clear();
}
// List to store generated responses
public ObservableCollection<string> GeneratedResponses { get; set; } = new ObservableCollection<string>();
// Index to keep track of the current response
private int _currentResponseIndex;
public int CurrentResponseIndex
{
get => _currentResponseIndex;
set
{
if (value >= 0 && value < GeneratedResponses.Count)
{
SetProperty(ref _currentResponseIndex, value);
CustomFormatResult = GeneratedResponses[_currentResponseIndex];
OnPropertyChanged(nameof(CurrentIndexDisplay));
}
}
}
public bool HasMultipleResponses
{
get => GeneratedResponses.Count > 1;
}
public string CurrentIndexDisplay => $"{CurrentResponseIndex + 1}/{GeneratedResponses.Count}";
public string InputTxtBoxPlaceholderText
{
get
{
app.GetMainWindow().ClearInputText();
if (!aiHelper.IsAIEnabled)
{
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAINotConfigured");
}
else if (!IsClipboardDataText)
{
return ResourceLoaderInstance.ResourceLoader.GetString("ClipboardDataTypeMismatchWarning");
}
else
{
return ResourceLoaderInstance.ResourceLoader.GetString("CustomFormatTextBox/PlaceholderText");
}
}
}
public string InputTxtBoxErrorText
{
get
{
if (ApiRequestStatus != (int)HttpStatusCode.OK)
{
if (ApiRequestStatus == (int)HttpStatusCode.TooManyRequests)
{
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyTooManyRequests");
}
else if (ApiRequestStatus == (int)HttpStatusCode.Unauthorized)
{
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyUnauthorized");
}
else
{
return ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyError") + ApiRequestStatus.ToString(CultureInfo.InvariantCulture);
}
}
return string.Empty;
}
}
[ObservableProperty]
private string _customFormatResult;
[RelayCommand]
public void PasteCustom()
{
PasteCustomFunction(GeneratedResponses[CurrentResponseIndex]);
}
// Command to select the previous custom format
[RelayCommand]
public void PreviousCustomFormat()
{
if (CurrentResponseIndex > 0)
{
CurrentResponseIndex--;
}
}
// Command to select the next custom format
[RelayCommand]
public void NextCustomFormat()
{
if (CurrentResponseIndex < GeneratedResponses.Count - 1)
{
CurrentResponseIndex++;
}
}
// Command to open the Settings window.
[RelayCommand]
public void OpenSettings()
{
SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.AdvancedPaste, true);
(App.Current as App).GetMainWindow().Close();
}
private void SetClipboardContentAndHideWindow(string content)
{
if (!string.IsNullOrEmpty(content))
{
ClipboardHelper.SetClipboardTextContent(content);
}
if (app.GetMainWindow() != null)
{
Windows.Win32.Foundation.HWND hwnd = (Windows.Win32.Foundation.HWND)app.GetMainWindow().GetWindowHandle();
Windows.Win32.PInvoke.ShowWindow(hwnd, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
}
}
internal void ToPlainTextFunction()
{
try
{
Logger.LogTrace();
string outputString = MarkdownHelper.PasteAsPlainTextFromClipboard(ClipboardData);
SetClipboardContentAndHideWindow(outputString);
if (_userSettings.SendPasteKeyCombination)
{
ClipboardHelper.SendPasteKeyCombination();
}
}
catch
{
}
}
internal void ToMarkdownFunction(bool pasteAlways = false)
{
try
{
Logger.LogTrace();
string outputString = MarkdownHelper.ToMarkdown(ClipboardData);
SetClipboardContentAndHideWindow(outputString);
if (pasteAlways || _userSettings.SendPasteKeyCombination)
{
ClipboardHelper.SendPasteKeyCombination();
}
}
catch
{
}
}
internal void ToJsonFunction(bool pasteAlways = false)
{
try
{
Logger.LogTrace();
string jsonText = JsonHelper.ToJsonFromXmlOrCsv(ClipboardData);
SetClipboardContentAndHideWindow(jsonText);
if (pasteAlways || _userSettings.SendPasteKeyCombination)
{
ClipboardHelper.SendPasteKeyCombination();
}
}
catch
{
}
}
internal async Task<string> GenerateCustomFunction(string inputInstructions)
{
Logger.LogTrace();
if (string.IsNullOrWhiteSpace(inputInstructions))
{
return string.Empty;
}
if (ClipboardData == null || !ClipboardData.Contains(StandardDataFormats.Text))
{
Logger.LogWarning("Clipboard does not contain text data");
return string.Empty;
}
string currentClipboardText = await Task.Run(async () =>
{
try
{
string text = await ClipboardData.GetTextAsync() as string;
return text;
}
catch (Exception)
{
// Couldn't get text from the clipboard. Resume with empty text.
return string.Empty;
}
});
if (string.IsNullOrWhiteSpace(currentClipboardText))
{
Logger.LogWarning("Clipboard has no usable text data");
return string.Empty;
}
var aiResponse = await Task.Run(() => aiHelper.AIFormatString(inputInstructions, currentClipboardText));
string aiOutput = aiResponse.Response;
ApiRequestStatus = aiResponse.ApiRequestStatus;
GeneratedResponses.Add(aiOutput);
CurrentResponseIndex = GeneratedResponses.Count - 1;
return aiOutput;
}
internal void PasteCustomFunction(string text)
{
Logger.LogTrace();
SetClipboardContentAndHideWindow(text);
if (_userSettings.SendPasteKeyCombination)
{
ClipboardHelper.SendPasteKeyCombination();
}
}
internal CustomQuery RecallPreviousCustomQuery()
{
return LoadPreviousQuery();
}
internal void SaveQuery(string inputQuery)
{
Logger.LogTrace();
DataPackageView clipboardData = Clipboard.GetContent();
if (clipboardData == null || !clipboardData.Contains(StandardDataFormats.Text))
{
Logger.LogWarning("Clipboard does not contain text data");
return;
}
string currentClipboardText = Task.Run(async () =>
{
string text = await clipboardData.GetTextAsync() as string;
return text;
}).Result;
var queryData = new CustomQuery
{
Query = inputQuery,
ClipboardData = currentClipboardText,
};
SettingsUtils utils = new SettingsUtils();
utils.SaveSettings(queryData.ToString(), Constants.AdvancedPasteModuleName, Constants.LastQueryJsonFileName);
}
internal CustomQuery LoadPreviousQuery()
{
SettingsUtils utils = new SettingsUtils();
var query = utils.GetSettings<CustomQuery>(Constants.AdvancedPasteModuleName, Constants.LastQueryJsonFileName);
return query;
}
private bool IsClipboardHistoryEnabled()
{
string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Clipboard\";
try
{
int enableClipboardHistory = (int)Registry.GetValue(registryKey, "EnableClipboardHistory", false);
return enableClipboardHistory != 0;
}
catch (Exception)
{
return false;
}
}
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0"
xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="AdvancedPaste.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect:
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update
-->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
</windowsSettings>
</application>
</assembly>

View File

@ -0,0 +1,8 @@
#pragma once
#include <string>
namespace AdvancedPasteConstants
{
// Name of the powertoy module.
inline const std::wstring ModuleKey = L"AdvancedPaste";
}

View File

@ -1,16 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h PastePlain.base.rc PastePlain.rc" />
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h AdvancedPaste.base.rc AdvancedPaste.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{FC373B24-3293-453C-AAF5-CF2909DCEE6A}</ProjectGuid>
<RootNamespace>PastePlain</RootNamespace>
<ProjectName>PastePlainModuleInterface</ProjectName>
<TargetName>PowerToys.PastePlainModuleInterface</TargetName>
<RootNamespace>AdvancedPaste</RootNamespace>
<ProjectName>AdvancedPasteModuleInterface</ProjectName>
<TargetName>PowerToys.AdvancedPasteModuleInterface</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
@ -39,7 +40,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="PastePlainConstants.h" />
<ClInclude Include="AdvancedPasteConstants.h" />
<ClInclude Include="pch.h" />
<None Include="packages.config" />
<None Include="resource.base.h" />
@ -62,8 +63,8 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="PastePlain.base.rc" />
<ResourceCompile Include="Generated Files\PastePlain.rc" />
<None Include="AdvancedPaste.base.rc" />
<ResourceCompile Include="Generated Files\AdvancedPaste.rc" />
</ItemGroup>
<ItemGroup>
<None Include="Resources.resx">

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
@ -24,7 +25,7 @@
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="PastePlainConstants.h">
<ClInclude Include="AdvancedPasteConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
@ -50,12 +51,12 @@
<Filter>Resource Files</Filter>
</None>
<None Include="packages.config" />
<None Include="PastePlain.base.rc">
<None Include="AdvancedPaste.base.rc">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\PastePlain.rc">
<ResourceCompile Include="Generated Files\AdvancedPaste.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>

View File

@ -59,7 +59,10 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:schema id="root"
xmlns=""
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
@ -117,10 +120,10 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="PastePlain_Name" xml:space="preserve">
<value>Paste As Plain Text</value>
<data name="Advanced_Paste_Name" xml:space="preserve">
<value>Advanced Paste</value>
</data>
<data name="PastePlain_Settings_Desc" xml:space="preserve">
<value>PowerToys integration to paste clipboard contents as plain text</value>
<data name="Advanced_Paste_Settings_Desc" xml:space="preserve">
<value>An AI powered tool to put your clipboard content into any format you need, focused towards developer workflows.</value>
</data>
</root>

View File

@ -1,21 +1,20 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include "AdvancedPasteConstants.h"
#include <interface/powertoy_module_interface.h>
#include "trace.h"
#include "Generated Files/resource.h"
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/utils/resources.h>
#include "PastePlainConstants.h"
#include <common/interop/shared_constants.h>
#include <common/utils/logger_helper.h>
#include <common/utils/winapi_error.h>
BOOL APIENTRY DllMain(HMODULE /*hModule*/,
DWORD ul_reason_for_call,
LPVOID /*lpReserved*/)
BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/)
{
switch (ul_reason_for_call)
{
@ -30,7 +29,6 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/,
Trace::UnregisterProvider();
break;
}
return TRUE;
}
@ -42,14 +40,15 @@ namespace
const wchar_t JSON_KEY_CTRL[] = L"ctrl";
const wchar_t JSON_KEY_SHIFT[] = L"shift";
const wchar_t JSON_KEY_CODE[] = L"code";
const wchar_t JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
const wchar_t JSON_KEY_PASTE_AS_PLAIN_HOTKEY[] = L"paste-as-plain-hotkey";
const wchar_t JSON_KEY_ADVANCED_PASTE_UI_HOTKEY[] = L"advanced-paste-ui-hotkey";
const wchar_t JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY[] = L"paste-as-markdown-hotkey";
const wchar_t JSON_KEY_PASTE_AS_JSON_HOTKEY[] = L"paste-as-json-hotkey";
const wchar_t JSON_KEY_SHOW_CUSTOM_PREVIEW[] = L"ShowCustomPreview";
const wchar_t JSON_KEY_VALUE[] = L"value";
}
struct ModuleSettings
{
} g_settings;
class PastePlain : public PowertoyModuleIface
class AdvancedPaste : public PowertoyModuleIface
{
private:
bool m_enabled = false;
@ -64,43 +63,128 @@ private:
// Time to wait for process to close after sending WM_CLOSE signal
static const int MAX_WAIT_MILLISEC = 10000;
Hotkey m_hotkey;
Hotkey m_paste_as_plain_hotkey = { .win = true, .ctrl = true, .shift = false, .alt = true, .key = 'V' };
Hotkey m_advanced_paste_ui_hotkey = { .win = true, .ctrl = false, .shift = true, .alt = false, .key = 'V' };
Hotkey m_paste_as_markdown_hotkey{};
Hotkey m_paste_as_json_hotkey{};
// Handle to event used to invoke PastePlain
HANDLE m_hInvokeEvent;
bool m_preview_custom_format_output = true;
void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
// Handle to event used to invoke AdvancedPaste
HANDLE m_hShowUIEvent;
HANDLE m_hPasteMarkdownEvent;
HANDLE m_hPasteJsonEvent;
Hotkey parse_single_hotkey(const wchar_t* hotkey, const winrt::Windows::Data::Json::JsonObject& settingsObject)
{
try
{
Hotkey _temp_paste_as_plain;
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(hotkey);
_temp_paste_as_plain.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
_temp_paste_as_plain.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
_temp_paste_as_plain.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
_temp_paste_as_plain.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
_temp_paste_as_plain.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
return _temp_paste_as_plain;
}
catch (...)
{
Logger::error("Failed to initialize AdvancedPaste shortcut from settings. Value will keep unchanged.");
}
return {};
}
bool migrate_data_and_remove_data_file(Hotkey& old_paste_as_plain_hotkey)
{
const wchar_t OLD_JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut";
const wchar_t OLD_MODULE_KEY[] = L"PastePlain";
try
{
const std::wstring save_file_location = PTSettingsHelper::get_module_save_file_location(OLD_MODULE_KEY);
if (std::filesystem::exists(save_file_location))
{
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(OLD_MODULE_KEY);
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
{
old_paste_as_plain_hotkey = parse_single_hotkey(OLD_JSON_KEY_ACTIVATION_SHORTCUT, settingsObject);
std::filesystem::remove(save_file_location);
return true;
}
}
}
catch (std::exception)
{
}
return false;
}
void parse_hotkeys(PowerToysSettings::PowerToyValues& settings)
{
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
// Migrate Paste As PLain text shortcut
Hotkey old_paste_as_plain_hotkey;
bool old_data_migrated = migrate_data_and_remove_data_file(old_paste_as_plain_hotkey);
if (old_data_migrated)
{
try
m_paste_as_plain_hotkey = old_paste_as_plain_hotkey;
// override settings file
json::JsonObject new_hotkey_value;
new_hotkey_value.SetNamedValue(JSON_KEY_WIN, json::value(old_paste_as_plain_hotkey.win));
new_hotkey_value.SetNamedValue(JSON_KEY_ALT, json::value(old_paste_as_plain_hotkey.alt));
new_hotkey_value.SetNamedValue(JSON_KEY_SHIFT, json::value(old_paste_as_plain_hotkey.shift));
new_hotkey_value.SetNamedValue(JSON_KEY_CTRL, json::value(old_paste_as_plain_hotkey.ctrl));
new_hotkey_value.SetNamedValue(JSON_KEY_CODE, json::value(old_paste_as_plain_hotkey.key));
if (!settingsObject.HasKey(JSON_KEY_PROPERTIES))
{
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ACTIVATION_SHORTCUT);
m_hotkey.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
m_hotkey.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
m_hotkey.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
m_hotkey.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
m_hotkey.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
}
catch (...)
{
Logger::error("Failed to initialize PastePlain start shortcut");
settingsObject.SetNamedValue(JSON_KEY_PROPERTIES, json::JsonObject{});
}
settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).SetNamedValue(JSON_KEY_PASTE_AS_PLAIN_HOTKEY, new_hotkey_value);
json::JsonObject ui_hotkey;
ui_hotkey.SetNamedValue(JSON_KEY_WIN, json::value(m_advanced_paste_ui_hotkey.win));
ui_hotkey.SetNamedValue(JSON_KEY_ALT, json::value(m_advanced_paste_ui_hotkey.alt));
ui_hotkey.SetNamedValue(JSON_KEY_SHIFT, json::value(m_advanced_paste_ui_hotkey.shift));
ui_hotkey.SetNamedValue(JSON_KEY_CTRL, json::value(m_advanced_paste_ui_hotkey.ctrl));
ui_hotkey.SetNamedValue(JSON_KEY_CODE, json::value(m_advanced_paste_ui_hotkey.key));
settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).SetNamedValue(JSON_KEY_ADVANCED_PASTE_UI_HOTKEY, ui_hotkey);
settings.save_to_settings_file();
}
else
{
Logger::info("PastePlain settings are empty");
}
if (!m_hotkey.key)
{
Logger::info("PastePlain is going to use default shortcut");
m_hotkey.win = true;
m_hotkey.alt = true;
m_hotkey.shift = false;
m_hotkey.ctrl = true;
m_hotkey.key = 'V';
if (settingsObject.GetView().Size())
{
if (settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).HasKey(JSON_KEY_PASTE_AS_PLAIN_HOTKEY))
{
m_paste_as_plain_hotkey = parse_single_hotkey(JSON_KEY_PASTE_AS_PLAIN_HOTKEY, settingsObject);
}
if (settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).HasKey(JSON_KEY_ADVANCED_PASTE_UI_HOTKEY))
{
m_advanced_paste_ui_hotkey = parse_single_hotkey(JSON_KEY_ADVANCED_PASTE_UI_HOTKEY, settingsObject);
}
if (settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).HasKey(JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY))
{
m_paste_as_markdown_hotkey = parse_single_hotkey(JSON_KEY_PASTE_AS_MARKDOWN_HOTKEY, settingsObject);
}
if (settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).HasKey(JSON_KEY_PASTE_AS_JSON_HOTKEY))
{
m_paste_as_json_hotkey = parse_single_hotkey(JSON_KEY_PASTE_AS_JSON_HOTKEY, settingsObject);
}
}
}
}
@ -109,28 +193,31 @@ private:
return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT;
}
void launch_process()
void launch_process(const std::wstring& arg = L"")
{
Logger::trace(L"Starting PastePlain process");
Logger::trace(L"Starting AdvancedPaste process");
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring executable_args = L"";
executable_args.append(std::to_wstring(powertoys_pid));
executable_args += L" " + arg;
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
sei.lpFile = L"PowerToys.PastePlain.exe";
sei.lpFile = L"WinUI3Apps\\PowerToys.AdvancedPaste.exe";
sei.nShow = SW_SHOWNORMAL;
sei.lpParameters = executable_args.data();
if (ShellExecuteExW(&sei))
{
Logger::trace("Successfully started the PastePlain process");
Logger::trace("Successfully started the Advanced Paste process");
}
else
{
Logger::error(L"PastePlain failed to start. {}", get_last_error_or_default(GetLastError()));
Logger::error(L"AdvancedPaste failed to start. {}", get_last_error_or_default(GetLastError()));
}
TerminateProcess(m_hProcess, 1);
m_hProcess = sei.hProcess;
}
@ -143,7 +230,13 @@ private:
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
parse_hotkey(settings);
parse_hotkeys(settings);
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size() && settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).HasKey(JSON_KEY_SHOW_CUSTOM_PREVIEW))
{
m_preview_custom_format_output = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHOW_CUSTOM_PREVIEW).GetNamedBoolean(JSON_KEY_VALUE);
}
}
catch (std::exception&)
{
@ -189,7 +282,7 @@ private:
DWORD errorCode = GetLastError();
auto errorMessage = get_last_error_message(errorCode);
Logger::error(L"Couldn't open the clipboard to get the text. {}", errorMessage.has_value() ? errorMessage.value() : L"");
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.OpenClipboard");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.OpenClipboard");
return;
}
HANDLE h_clipboard_data = GetClipboardData(CF_UNICODETEXT);
@ -199,7 +292,7 @@ private:
DWORD errorCode = GetLastError();
auto errorMessage = get_last_error_message(errorCode);
Logger::error(L"Failed to get clipboard data. {}", errorMessage.has_value() ? errorMessage.value() : L"");
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GetClipboardData");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GetClipboardData");
CloseClipboard();
return;
}
@ -211,7 +304,7 @@ private:
DWORD errorCode = GetLastError();
auto errorMessage = get_last_error_message(errorCode);
Logger::error(L"Couldn't lock the buffer to get the unformatted text from the clipboard. {}", errorMessage.has_value() ? errorMessage.value() : L"");
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GlobalLock");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"read.GlobalLock");
CloseClipboard();
return;
}
@ -234,7 +327,7 @@ private:
DWORD errorCode = GetLastError();
auto errorMessage = get_last_error_message(errorCode);
Logger::error(L"Couldn't get the clipboard data format type that would allow excluding the data from the clipboard history / roaming. {}", errorMessage.has_value() ? errorMessage.value() : L"");
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.RegisterClipboardFormat");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.RegisterClipboardFormat");
return;
}
@ -243,7 +336,7 @@ private:
DWORD errorCode = GetLastError();
auto errorMessage = get_last_error_message(errorCode);
Logger::error(L"Couldn't open the clipboard to copy the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L"");
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.OpenClipboard");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.OpenClipboard");
return;
}
@ -254,7 +347,7 @@ private:
DWORD errorCode = GetLastError();
auto errorMessage = get_last_error_message(errorCode);
Logger::error(L"Couldn't allocate a buffer for the unformatted text. {}", errorMessage.has_value() ? errorMessage.value() : L"");
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalAlloc");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalAlloc");
CloseClipboard();
return;
}
@ -267,7 +360,7 @@ private:
Logger::error(L"Couldn't lock the buffer to send the unformatted text to the clipboard. {}", errorMessage.has_value() ? errorMessage.value() : L"");
GlobalFree(h_clipboard_data);
CloseClipboard();
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalLock");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.GlobalLock");
return;
}
@ -283,7 +376,7 @@ private:
GlobalUnlock(h_clipboard_data);
GlobalFree(h_clipboard_data);
CloseClipboard();
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.SetClipboardData");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"write.SetClipboardData");
return;
}
@ -368,25 +461,45 @@ private:
DWORD errorCode = GetLastError();
auto errorMessage = get_last_error_message(errorCode);
Logger::error(L"SendInput failed. Expected to send {} inputs and sent only {}. {}", inputs.size(), uSent, errorMessage.has_value() ? errorMessage.value() : L"");
Trace::PastePlainError(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput");
Trace::AdvancedPaste_Error(errorCode, errorMessage.has_value() ? errorMessage.value() : L"", L"input.SendInput");
return;
}
// Clear kb state and send Ctrl+V end
}
Trace::PastePlainSuccess();
}
void bring_process_to_front()
{
auto enum_windows = [](HWND hwnd, LPARAM param) -> BOOL {
HANDLE process_handle = reinterpret_cast<HANDLE>(param);
DWORD window_process_id = 0;
GetWindowThreadProcessId(hwnd, &window_process_id);
if (GetProcessId(process_handle) == window_process_id)
{
SetForegroundWindow(hwnd);
return FALSE;
}
return TRUE;
};
EnumWindows(enum_windows, (LPARAM)m_hProcess);
}
public:
PastePlain()
AdvancedPaste()
{
app_name = GET_RESOURCE_STRING(IDS_PASTEPLAIN_NAME);
app_key = PastePlainConstants::ModuleKey;
LoggerHelpers::init_logger(app_key, L"ModuleInterface", "PastePlain");
app_name = GET_RESOURCE_STRING(IDS_ADVANCED_PASTE_NAME);
app_key = AdvancedPasteConstants::ModuleKey;
LoggerHelpers::init_logger(app_key, L"ModuleInterface", "AdvancedPaste");
m_hShowUIEvent = CreateDefaultEvent(CommonSharedConstants::SHOW_ADVANCED_PASTE_SHARED_EVENT);
m_hPasteMarkdownEvent = CreateDefaultEvent(CommonSharedConstants::ADVANCED_PASTE_MARKDOWN_EVENT);
m_hPasteJsonEvent = CreateDefaultEvent(CommonSharedConstants::ADVANCED_PASTE_JSON_EVENT);
init_settings();
}
~PastePlain()
~AdvancedPaste()
{
if (m_enabled)
{
@ -397,7 +510,7 @@ public:
// Destroy the powertoy and free memory
virtual void destroy() override
{
Logger::trace("PastePlain::destroy()");
Logger::trace("AdvancedPaste::destroy()");
delete this;
}
@ -416,7 +529,7 @@ public:
// Return the configured status for the gpo policy for the module
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
{
return powertoys_gpo::getConfiguredPastePlainEnabledValue();
return powertoys_gpo::getConfiguredAdvancedPasteEnabledValue();
}
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
@ -425,9 +538,9 @@ public:
// Create a Settings object.
PowerToysSettings::Settings settings(hinstance, get_name());
settings.set_description(GET_RESOURCE_STRING(IDS_PASTEPLAIN_SETTINGS_DESC));
settings.set_description(GET_RESOURCE_STRING(IDS_ADVANCED_PASTE_SETTINGS_DESC));
settings.set_overview_link(L"https://aka.ms/PowerToysOverview_PastePlain");
settings.set_overview_link(L"https://aka.ms/PowerToysOverview_AdvancedPaste");
return settings.serialize_to_buffer(buffer, buffer_size);
}
@ -444,7 +557,21 @@ public:
PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
parse_hotkey(values);
parse_hotkeys(values);
auto settingsObject = values.get_raw_json();
if (settingsObject.GetView().Size() && settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).HasKey(JSON_KEY_SHOW_CUSTOM_PREVIEW))
{
m_preview_custom_format_output = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHOW_CUSTOM_PREVIEW).GetNamedBoolean(JSON_KEY_VALUE);
}
// order of args matter
Trace::AdvancedPaste_SettingsTelemetry(m_paste_as_plain_hotkey,
m_advanced_paste_ui_hotkey,
m_paste_as_markdown_hotkey,
m_paste_as_json_hotkey,
m_preview_custom_format_output);
// If you don't need to do any custom processing of the settings, proceed
// to persists the values calling:
values.save_to_settings_file();
@ -459,32 +586,78 @@ public:
virtual void enable()
{
Logger::trace("PastePlain::enable()");
Logger::trace("AdvancedPaste::enable()");
Trace::AdvancedPaste_Enable(true);
ResetEvent(m_hShowUIEvent);
ResetEvent(m_hPasteMarkdownEvent);
ResetEvent(m_hPasteJsonEvent);
m_enabled = true;
Trace::EnablePastePlain(true);
launch_process();
};
virtual void disable()
{
Logger::trace("PastePlain::disable()");
m_enabled = false;
Trace::EnablePastePlain(false);
}
virtual bool on_hotkey(size_t /*hotkeyId*/) override
{
Logger::trace("AdvancedPaste::disable()");
if (m_enabled)
{
Logger::trace(L"PastePlain hotkey pressed");
ResetEvent(m_hShowUIEvent);
ResetEvent(m_hPasteMarkdownEvent);
ResetEvent(m_hPasteJsonEvent);
TerminateProcess(m_hProcess, 1);
Trace::AdvancedPaste_Enable(false);
std::thread([=]() {
// hotkey work should be kept to a minimum, or Windows might deregister our low level keyboard hook.
// Move work to another thread.
try_to_paste_as_plain_text();
}).detach();
CloseHandle(m_hProcess);
m_hProcess = 0;
}
Trace::PastePlainInvoked();
return true;
m_enabled = false;
}
virtual bool on_hotkey(size_t hotkeyId) override
{
Logger::trace(L"AdvancedPaste hotkey pressed");
if (m_enabled)
{
if (!is_process_running())
{
Logger::trace(L"Launching new process");
launch_process();
Trace::AdvancedPaste_Invoked(L"AdvancedPasteUI");
}
// hotkeyId in same order as set by get_hotkeys
if (hotkeyId == 0) { // m_paste_as_plain_hotkey
Logger::trace(L"Paste as plain text hotkey pressed");
std::thread([=]() {
// hotkey work should be kept to a minimum, or Windows might deregister our low level keyboard hook.
// Move work to another thread.
try_to_paste_as_plain_text();
}).detach();
Trace::AdvancedPaste_Invoked(L"PastePlainTextDirect");
return true;
}
if (hotkeyId == 1) { // m_advanced_paste_ui_hotkey
Logger::trace(L"Setting start up event");
bring_process_to_front();
SetEvent(m_hShowUIEvent);
return true;
}
if (hotkeyId == 2) { // m_paste_as_markdown_hotkey
Logger::trace(L"Starting paste as markdown directly");
SetEvent(m_hPasteMarkdownEvent);
return true;
}
if (hotkeyId == 3) { // m_paste_as_json_hotkey
Logger::trace(L"Starting paste as json directly");
SetEvent(m_hPasteJsonEvent);
return true;
}
}
return false;
@ -492,34 +665,23 @@ public:
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
{
if (m_hotkey.key)
if (hotkeys && buffer_size >= 4)
{
if (hotkeys && buffer_size >= 1)
{
hotkeys[0] = m_hotkey;
}
return 1;
}
else
{
return 0;
hotkeys[0] = m_paste_as_plain_hotkey;
hotkeys[1] = m_advanced_paste_ui_hotkey;
hotkeys[2] = m_paste_as_markdown_hotkey;
hotkeys[3] = m_paste_as_json_hotkey;
}
return 4;
}
virtual bool is_enabled() override
{
return m_enabled;
}
virtual void send_settings_telemetry() override
{
Logger::info("Send settings telemetry");
Trace::SettingsTelemetry(m_hotkey);
}
};
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new PastePlain();
return new AdvancedPaste();
}

View File

@ -4,4 +4,5 @@
#include <winrt/Windows.Foundation.Collections.h>
#include <ProjectTelemetry.h>
#include <shellapi.h>
#include <Shlwapi.h>
#include <Shlwapi.h>
#include <filesystem>

View File

@ -5,9 +5,9 @@
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys PastePlain"
#define INTERNAL_NAME "PowerToys.PastePlainModuleInterface"
#define ORIGINAL_FILENAME "PowerToys.PastePlainModuleInterface.dll"
#define FILE_DESCRIPTION "PowerToys AdvancedPaste"
#define INTERNAL_NAME "PowerToys.AdvancedPasteModuleInterface"
#define ORIGINAL_FILENAME "PowerToys.AdvancedPasteModuleInterface.dll"
// Non-localizable
//////////////////////////////

View File

@ -0,0 +1,102 @@
#include "pch.h"
#include "trace.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider()
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider()
{
TraceLoggingUnregister(g_hProvider);
}
// Log if the user has AdvancedPaste enabled or disabled
void Trace::AdvancedPaste_Enable(const bool enabled) noexcept
{
TraceLoggingWrite(
g_hProvider,
"AdvancedPaste_EnableAdvancedPaste",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingBoolean(enabled, "Enabled"));
}
// Log if the user has invoked AdvancedPaste
void Trace::AdvancedPaste_Invoked(std::wstring mode) noexcept
{
TraceLoggingWrite(
g_hProvider,
"AdvancedPaste_InvokeAdvancedPaste",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingValue(mode.c_str(), "Mode"));
}
// Log if an error occurs in AdvancedPaste
void Trace::AdvancedPaste_Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept
{
TraceLoggingWrite(
g_hProvider,
"AdvancedPaste_Error",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingValue(methodName.c_str(), "MethodName"),
TraceLoggingValue(errorCode, "ErrorCode"),
TraceLoggingValue(errorMessage.c_str(), "ErrorMessage"));
}
// Event to send settings telemetry.
void Trace::AdvancedPaste_SettingsTelemetry(const PowertoyModuleIface::Hotkey& pastePlainHotkey,
const PowertoyModuleIface::Hotkey& advancedPasteUIHotkey,
const PowertoyModuleIface::Hotkey& pasteMarkdownHotkey,
const PowertoyModuleIface::Hotkey& pasteJsonHotkey,
const bool preview_custom_format_output) noexcept
{
std::wstring pastePlainHotkeyStr =
std::wstring(pastePlainHotkey.win ? L"Win + " : L"") +
std::wstring(pastePlainHotkey.ctrl ? L"Ctrl + " : L"") +
std::wstring(pastePlainHotkey.shift ? L"Shift + " : L"") +
std::wstring(pastePlainHotkey.alt ? L"Alt + " : L"") +
std::wstring(L"VK ") + std::to_wstring(pastePlainHotkey.key);
std::wstring advancedPasteUIHotkeyStr =
std::wstring(advancedPasteUIHotkey.win ? L"Win + " : L"") +
std::wstring(advancedPasteUIHotkey.ctrl ? L"Ctrl + " : L"") +
std::wstring(advancedPasteUIHotkey.shift ? L"Shift + " : L"") +
std::wstring(advancedPasteUIHotkey.alt ? L"Alt + " : L"") +
std::wstring(L"VK ") + std::to_wstring(advancedPasteUIHotkey.key);
std::wstring pasteMarkdownHotkeyStr =
std::wstring(pasteMarkdownHotkey.win ? L"Win + " : L"") +
std::wstring(pasteMarkdownHotkey.ctrl ? L"Ctrl + " : L"") +
std::wstring(pasteMarkdownHotkey.shift ? L"Shift + " : L"") +
std::wstring(pasteMarkdownHotkey.alt ? L"Alt + " : L"") +
std::wstring(L"VK ") + std::to_wstring(pasteMarkdownHotkey.key);
std::wstring pasteJsonHotkeyStr =
std::wstring(pasteJsonHotkey.win ? L"Win + " : L"") +
std::wstring(pasteJsonHotkey.ctrl ? L"Ctrl + " : L"") +
std::wstring(pasteJsonHotkey.shift ? L"Shift + " : L"") +
std::wstring(pasteJsonHotkey.alt ? L"Alt + " : L"") +
std::wstring(L"VK ") + std::to_wstring(pasteJsonHotkey.key);
TraceLoggingWrite(
g_hProvider,
"AdvancedPaste_Settings",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingWideString(pastePlainHotkeyStr.c_str(), "PastePlainHotkey"),
TraceLoggingWideString(advancedPasteUIHotkeyStr.c_str(), "AdvancedPasteUIHotkey"),
TraceLoggingWideString(pasteMarkdownHotkeyStr.c_str(), "PasteMarkdownHotkey"),
TraceLoggingWideString(pasteJsonHotkeyStr.c_str(), "PasteJsonHotkey"),
TraceLoggingBoolean(preview_custom_format_output, "ShowCustomPreview")
);
}

Some files were not shown because too many files have changed in this diff Show More