mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
Merge branch 'main' into shawn/quickAccessImprove
This commit is contained in:
7
.github/actions/spell-check/allow/code.txt
vendored
7
.github/actions/spell-check/allow/code.txt
vendored
@@ -95,6 +95,7 @@ OTP
|
||||
Yubi
|
||||
Yubico
|
||||
Perplexity
|
||||
Groq
|
||||
svgl
|
||||
|
||||
# KEYS
|
||||
@@ -328,3 +329,9 @@ FFF
|
||||
HHH
|
||||
riday
|
||||
YYY
|
||||
|
||||
# GitHub issue/PR commands
|
||||
azp
|
||||
feedbackhub
|
||||
needinfo
|
||||
reportbug
|
||||
|
||||
4
.github/actions/spell-check/expect.txt
vendored
4
.github/actions/spell-check/expect.txt
vendored
@@ -221,6 +221,7 @@ clientside
|
||||
CLIPBOARDUPDATE
|
||||
CLIPCHILDREN
|
||||
CLIPSIBLINGS
|
||||
CLITo
|
||||
closesocket
|
||||
clp
|
||||
CLSCTX
|
||||
@@ -729,9 +730,9 @@ HWNDPARENT
|
||||
HWNDPREV
|
||||
hyjiacan
|
||||
IAI
|
||||
icf
|
||||
ICONERROR
|
||||
ICONLOCATION
|
||||
icf
|
||||
IDCANCEL
|
||||
IDD
|
||||
idk
|
||||
@@ -1076,6 +1077,7 @@ MVVMTK
|
||||
MWBEx
|
||||
MYICON
|
||||
NAMECHANGE
|
||||
Notavailable
|
||||
namespaceanddescendants
|
||||
nao
|
||||
NCACTIVATE
|
||||
|
||||
34
doc/devdocs/commands.md
Normal file
34
doc/devdocs/commands.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Issue/PR commands
|
||||
|
||||
The PowerToys repository uses some special keywords to help manage issues and pull requests. Here is a list of the most important commands you can use in issue and PR descriptions or comments.
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `/azp run` | Triggers the Azure Pipelines CI build for the current PR. Useful if you want to re-run the build without creating a new commit. |
|
||||
| `/bugreport` / `/reportbug` | Adds a comment with a manual for the Bug Report Tool, which helps users collect logs and system information for debugging purposes. It requests to upload this file and adds the `Needs-Author-Feedback` label. |
|
||||
| `/feedbackhub` | Adds a comment with a link to the Feedback Hub app on Windows, where users can submit feedback about PowerToys. Closes the issue and adds the `Resolution-Please File on Feedback Hub` label. |
|
||||
| `/dup #...` / `/duplicate #...` / `/dup https://...` / `/duplicate https://...` | Marks the current issue as a duplicate of another issue. It closes the current issue and applies the `Resolution-Duplicate` label. Replace `#...` with the issue number or a link to the issue. |
|
||||
| `/needinfo` | Adds the `Needs-Author-Feedback` label to the issue or PR, indicating that more information is needed from the author. |
|
||||
| `/helped` | Closes the issue and adds the `Resolution-Helped User` label. Furthermore a comment is added with a link to the PowerToys user documentation. |
|
||||
| `/loc` | Adds a comment informing the user that the issue was forwarded to the localization team and will soon be fixed. It adds the `Loc-Sent To Team` label. |
|
||||
|
||||
## Defining new commands
|
||||
|
||||
Most of these commands are using the [Microsoft GitHub Policy Service](https://github.com/apps/microsoft-github-policy-service) bot. Its commands are defined in the [PowerToys policy configuration file](/.github/policies/resourceManagement.yml).
|
||||
|
||||
## Other automated tasks
|
||||
|
||||
### Automatic labeling
|
||||
|
||||
The bot can automatically apply the correct `product-...` label for any opened issue.
|
||||
|
||||
> [!NOTE]
|
||||
> This feature is currently only available for the Workspaces module as a test.
|
||||
|
||||
### The `Needs-Author-Feedback` label
|
||||
|
||||
If an issue has this label and had no activity for 5 days, the bot will post a comment reminding the author to provide the needed information. It also adds the `Status-No recent activity` label. If no further activity occurs for another 5 days, the bot will close the issue.
|
||||
|
||||
### Filtering users that want to contribute
|
||||
|
||||
If a user utters their intention to contribute (e.g., by using the phrase "I want to contribute" in an issue or PR), the bot will add a comment with a link to the ["Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769).
|
||||
@@ -1,165 +0,0 @@
|
||||
# Localization
|
||||
|
||||
> **NOTE**: THIS DOCUMENT IS OUTDATED.
|
||||
> Follow [issue 15243](https://github.com/microsoft/PowerToys/issues/15243) for updates.
|
||||
|
||||
## Table of Contents
|
||||
1. [Localization on the pipeline (CDPX)](#localization-on-the-pipeline-cdpx)
|
||||
1. [UWP Special case](#uwp-special-case)
|
||||
2. [Enabling localization on a new project](#enabling-localization-on-a-new-project)
|
||||
1. [C++](#c)
|
||||
2. [C#](#c-1)
|
||||
3. [UWP](#uwp)
|
||||
3. [Lcl Files](#lcl-files)
|
||||
4. [Possible Issues in localization PRs (LEGO)](#possible-issues-in-localization-prs-lego)
|
||||
5. [Enabling localized MSI for a new project](#enabling-localized-msi-for-a-new-project)
|
||||
|
||||
## Localization on the pipeline (CDPX)
|
||||
[The localization step](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L45-L52) is run on the pipeline before the solution is built. This step runs the [build-localization](https://github.com/microsoft/PowerToys/blob/main/.pipelines/build-localization.cmd) script, which generates resx files for all the projects with localization enabled using the `Localization.XLoc` package.
|
||||
|
||||
The [`Localization.XLoc`](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/build-localization.cmd#L24-L25) tool is run on the repo root, and it checks for all occurrences of `LocProject.json`. Each localized project has a `LocProject.json` file in the project root, which contains the location of the English resx file, list of languages for localization, and the output path where the localized resx files are to be copied to. In addition to this, some other parameters can be set, such as whether the language ID should be added as a folder in the file path or in the file name. When the CDPX pipeline is run, the localization team is notified of changes in the English resx files. For each project with localization enabled, a `loc` folder (see [this](https://github.com/microsoft/PowerToys/tree/main/src/modules/launcher/Microsoft.Launcher/loc) for example) is created in the same directory as the `LocProject.json` file. The folder contains language specific folders which in turn have a nested folder path equivalent to `OutputPath` in the `LocProject.json`. Each of these folders contain one `lcl` file. The `lcl` files contain the English resources along with their translation for that language. These are described in more detail in the [Lcl files section](#lcl-files). Once the `.resx` files are generated, they will be used during the `Build PowerToys` step for localized versions of the modules.
|
||||
|
||||
Since the localization script requires certain nuget packages, the [`restore-localization`](https://github.com/microsoft/PowerToys/blob/main/.pipelines/restore-localization.cmd) script is run before running `build-localization` to install all the required packages. This script must [run in the `restore` step](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L37-L39) of pipeline because [the host is network isolated](https://onebranch.visualstudio.com/Pipeline/_wiki/wikis/Pipeline.wiki/2066/Consuming-Packages-in-a-CDPx-Pipeline?anchor=overview) at the `build` step. The [Toolset package source](https://github.com/microsoft/PowerToys/blob/86d77103e9c69686c297490acb04775d43ef8b76/.pipelines/pipeline.user.windows.yml#L23) is used for this.
|
||||
|
||||
The process and variables that can be tweaked on the pipeline are described in more detail on [onebranch (account required) under Localization](https://onebranch.visualstudio.com/Pipeline/_wiki/wikis/Pipeline.wiki/290/Localization).
|
||||
|
||||
The localized resource dlls for C# projects are added to the MSI only for build on the pipeline. This is done by checking if the [`IsPipeline` variable is defined](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/installer/PowerToysSetup/Product.wxs#L804-L805), which gets defined before [building the installer on the pipeline](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/.pipelines/build-installer.cmd#L4). This is done because the localized resx files are only present on the pipeline, and not having this check would result in the installer project failing to build locally.
|
||||
|
||||
## Enabling localization on a new project
|
||||
To enable localization on a new project, the first step is to create a file `LocProject.json` in the project root.
|
||||
|
||||
For example, for a project in the folder `src\path` where the resx file is present in `resources\Resources.resx`, the LocProject.json file will contain the following:
|
||||
```
|
||||
{
|
||||
"Projects": [
|
||||
{
|
||||
"LanguageSet": "Azure_Languages",
|
||||
"LocItems": [
|
||||
{
|
||||
"SourceFile": "src\\path\\resources\\Resources.resx",
|
||||
"CopyOption": "LangIDOnName",
|
||||
"OutputPath": "src\\path\\resources"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
The rest of the steps depend on the project type and are covered in the sections below. The steps to add the localized files to the MSI can be found in [Enabling localized MSI for a new project](#Enabling-localized-MSI-for-a-new-project).
|
||||
|
||||
### C++
|
||||
C++ projects do not support `resx` files, and instead use `rc` files along with `resource.h` files. The CDPX pipeline however doesn't support localizing `rc` files and the other alternative they support is directly translating the resources from the binary which makes it harder to maintain resources. To avoid this, a custom script has been added which expects a resx file and converts the entries to an rc file with a string table and adds resource declarations to a resource.h file so that the resources can be compiled with the C++ project.
|
||||
|
||||
If you already have a .rc file, copy the string table to a separate txt file and run the [convert-stringtable-to-resx.ps1](https://github.com/microsoft/PowerToys/blob/main/tools/build/convert-stringtable-to-resx.ps1) script on it. This script is not very robust to input, and requires the data in a specific format, where `IDS_ResName L"ResourceValue"` and any number of spaces can be present in between. The script converts this file to the format expected by [`resgen`](https://learn.microsoft.com/dotnet/framework/tools/resgen-exe-resource-file-generator#Convert), which will convert it to resx. The resource names are changed from all uppercase to title case, and the `IDS_` prefix is removed. Escape characters might have to be manually replaced, for example .rc files would have escaped double quotes as `""`, so this should be replaced with just `"` before converting to the resx files.
|
||||
|
||||
After generating the resx file, rename the existing rc and h files to ProjName.base.rc and resource.base.h. In the rc file remove the string table which is to be localized and in the .h file remove all `#define`s corresponding to localized resources. In the vcxproj of the C++ project, add the following build event:
|
||||
```
|
||||
<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 ProjName.base.rc ProjName.rc" />
|
||||
</Target>
|
||||
```
|
||||
|
||||
This event runs a script which generates a resource.h and ProjName.rc in the `Generated Files` folder using the strings in all the resx files along with the existing information in resource.base.h and ProjName.base.rc. The script is [convert-resx-to-rc.ps1](https://github.com/microsoft/PowerToys/blob/main/tools/build/convert-resx-to-rc.ps1). The script uses [`resgen`](https://learn.microsoft.com/dotnet/framework/tools/resgen-exe-resource-file-generator#Convert) to convert the resx file to a string table expected in the .rc file format. When the resources are added to the rc file the `IDS_` prefix is added and resource names are in upper case (as it was originally). Any occurrences of `"` in the string resource is escaped as `""` to prevent build errors. The string tables are added to the rc file in the following format:
|
||||
```
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
strings
|
||||
END
|
||||
|
||||
#endif
|
||||
```
|
||||
Since there is no API to identify the `AFX_TARG_*`, `LANG_*` or `SUBLANG_*` values from each langId from the pipeline, these are hardcoded in the script (for each language) as done in [lines 50-77 of `convert-resx-to-rc.ps1`](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/tools/build/convert-resx-to-rc.ps1#L50-L77). **If any other languages are added in the future, this script will have to be updated.** In order to determine what are the language codes, you can open the rc file in Resource View, right click the string table and press `Insert Copy` and choose the corresponding language. This autogenerates the required code and can be used to figure out the language codes. The files also add the resource declarations to a resource.h file, starting from 101 by default(this can be changed by an optional argument). Since the output files will be generated in `Generated Files`, any includes in these two files will require an additional `..\` and wherever resource.h is used, it will have to be included as `Generated Files\resource.h`. While adding `resource.base.h` and `ProjName.base.rc` to the vcxproj, these should be modified to not participate in the build to avoid build errors:
|
||||
```
|
||||
<None Include="Resources.resx" />
|
||||
```
|
||||
|
||||
Some rc/resource.h files might be used in multiple projects (for example, KBM). To ensure the projects build for these cases, the build event can be added to the entire directory so that the rc files are generated before any project is built. See [Directory.Build.targets](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/Directory.Build.targets) for an example.
|
||||
|
||||
Check [this PR](https://github.com/microsoft/PowerToys/pull/6104) for an example for making these changes for a C++ project.
|
||||
|
||||
### C#
|
||||
Since C# projects natively support `resx` files, the only step required here is to include all the resx files in the build. For .NET Core projects this is done automatically and the .csproj does not need to be modified. For other projects, the following line needs to be added:
|
||||
```
|
||||
<EmbeddedResource Include="Properties\Resources.*.resx" />
|
||||
```
|
||||
|
||||
**Note:** Building with localized resources may cause a build warning `Referenced assembly 'mscorlib.dll' targets a different processor` which is a VS bug. More details can be found in [PowerToys issue #7269](https://github.com/microsoft/PowerToys/issues/7269).
|
||||
|
||||
**Note:** If a project needs to be migrated from XAML resources to resx, the easiest way to convert the resources would be to change to format to `=` separates resources by either manually (by Ctrl+H on a text editor), or by a script, and then running [`resgen`](https://learn.microsoft.com/dotnet/framework/tools/resgen-exe-resource-file-generator#Convert) on `Developer Command Prompt for VS` to convert it to resx format.
|
||||
```
|
||||
<system:String x:Key="wox_plugin_calculator_plugin_name">Calculator</system:String>
|
||||
<system:String x:Key="wox_plugin_calculator_plugin_description">Allows to do mathematical calculations.(Try 5*3-2 in Wox)</system:String>
|
||||
<system:String x:Key="wox_plugin_calculator_not_a_number">Not a number (NaN)</system:String>
|
||||
```
|
||||
to
|
||||
```
|
||||
wox_plugin_calculator_plugin_name=Calculator
|
||||
wox_plugin_calculator_plugin_description=Allows to do mathematical calculations.(Try 5*3-2 in Wox)
|
||||
wox_plugin_calculator_not_a_number=Not a number (NaN)
|
||||
```
|
||||
After adding the resx file to the project along with the resource generator, references to the strings will have to be replaced with `Properties.Resources.resName` rather than the custom APIs. Check [this PR](https://github.com/microsoft/PowerToys/pull/6165) for an example of the changes required.
|
||||
|
||||
### UWP
|
||||
UWP projects expect `resw` files rather than `resx` (the format is almost the same). Unlike other C# projects, the files are expected in the format `fullLangId\Resources.resw`. To include these files in the build, replace the following line in the csproj:
|
||||
```
|
||||
<PRIResource Include="Strings\en-us\Resources.resw" />
|
||||
```
|
||||
to
|
||||
```
|
||||
<PRIResource Include="Strings\*\Resources.resw" />
|
||||
```
|
||||
|
||||
## Lcl Files
|
||||
Lcl files contain all the resources that are present in the English resx file, along with a translation if it has been added.
|
||||
|
||||
For example, an entry for a resource in the lcl file looks like this:
|
||||
```
|
||||
<Item ItemId=";EditKeyboard_WindowName" ItemType="0;.resx" PsrId="211" Leaf="true">
|
||||
<Str Cat="Text">
|
||||
<Val><![CDATA[Remap keys]]></Val>
|
||||
<Tgt Cat="Text" Stat="Loc" Orig="New">
|
||||
<Val><![CDATA[Remapper des touches]]></Val>
|
||||
</Tgt>
|
||||
</Str>
|
||||
<Disp Icon="Str" />
|
||||
</Item>
|
||||
```
|
||||
The `<Tgt>` element would not be present in the initial commits of the lcl files, as only the English version of the string would be present.
|
||||
|
||||
**Note:** The CDPX Localization system has a fail-safe check on the lcl files, where if the English string value which is present inside `<Val><![CDATA[*]]></Val>` does not match the value present in the English Resources.resx file then the translated value will not be copied to the localized resx file. This is present so that obsolete translations would not be loaded when the English resource has changed, and the English string will be used rather than the obsolete translation.
|
||||
|
||||
## Possible Issues in localization PRs (LEGO)
|
||||
Since the LEGO PRs update some of the strings in LCL files at a time, there can be multiple PRs which modify the same files, leading to merge conflicts. In most cases this would show up on GitHub as a merge conflict, but sometimes a bad git merge may occur, and the file could end up with incorrect formatting, such as two `<Tgt>` elements for a single resource. These can be fixed by ensuring the elements follow the format described in [this section](#lcl-files). To catch such errors, the build farm should be run for every LEGO PR and if any error occurs in the localization step, we should check the corresponding resx/lcl files for conflicts.
|
||||
|
||||
## Enabling localized MSI for a new project
|
||||
For C++ and UWP projects no additional files are generated with localization that need to be added to the MSI. For C++ projects all the resources are added to the dll/exe, while for UWP projects they are added to the `resources.pri` file (which is present even for an unlocalized project). To verify if the localized resources are added to the `resources.pri` file the following steps can be done:
|
||||
- Open `Developer Command Prompt for VS`
|
||||
- After navigating to the folder containing the pri file, run the following command:
|
||||
|
||||
makepri.exe dump /if .\resources.pri
|
||||
- Check the contents of the `resources.pri.xml` file that is generated from the command. The last section of the file will contain the resources with the strings in all the languages:
|
||||
```
|
||||
<NamedResource name="GeneralSettings_RunningAsAdminText" uri="ms-resource://f4f787a5-f0ae-47a9-be89-5408b1dd2b47/Resources/GeneralSettings_RunningAsAdminText">
|
||||
<Candidate qualifiers="Language-FR" type="String">
|
||||
<Value>Running as administrator</Value>
|
||||
</Candidate>
|
||||
<Candidate qualifiers="Language-EN-US" isDefault="true" type="String">
|
||||
<Value>Running as administrator</Value>
|
||||
</Candidate>
|
||||
</NamedResource>
|
||||
```
|
||||
|
||||
For C# projects, satellite dlls are generated when the project is built. For a project named `ProjName`, files are created in the format `langId\ProjName.resources.dll` where `langId` is in the same format as the lcl files. The satellite dlls need to be included with the MSI, but they must be added only if the solution is built from the build farm, as the localized resx files will not be present on local machines (and that could cause local builds of the installer to fail).
|
||||
This can be done by adding the directory name of the project to [Product.wxs near line 806](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/installer/PowerToysSetup/Product.wxs#L806) and a resource component for the project can be created in [Product.wxs near lines 845-847](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/installer/PowerToysSetup/Product.wxs#L845-L847) in this format:
|
||||
```
|
||||
<Component Id="ProjName_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)ProjNameInstallFolder">
|
||||
<File Id="ProjName_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\ProjName\$(var.Language)\ProjName.resources.dll" />
|
||||
</Component>
|
||||
```
|
||||
|
||||
We should also ensure the new dlls are signed by the pipeline. Currently all dlls of the form [`*.resources.dll` are signed](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/.pipelines/pipeline.user.windows.yml#L68).
|
||||
|
||||
**Note:** The resource dlls should be added to the MSI project only after the initial commit with the lcl files has been done by the Localization team. Otherwise, the pipeline will fail as there wouldn't be any resx files to generate the dlls.
|
||||
@@ -38,6 +38,11 @@ Welcome to the PowerToys developer documentation. This documentation provides in
|
||||
- [Update Process](processes/update-process.md) - How PowerToys updates work
|
||||
- [GPO Implementation](processes/gpo.md) - Group Policy Objects implementation details
|
||||
|
||||
## Other Resources
|
||||
|
||||
- [aka.ms links](akaLinks.md) - List of short links
|
||||
- [Issue/PR commands](commands.md) - Special commands for managing issues and pull requests
|
||||
|
||||
## Fork, Clone, Branch and Create your PR
|
||||
|
||||
Once you've discussed your proposed feature/fix/etc. with a team member, and an approach or a spec has been written and approved, it's time to start development:
|
||||
|
||||
@@ -51,6 +51,7 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| [RandomGen](https://github.com/ruslanlap/PowerToysRun-RandomGen) | [ruslanlap](https://github.com/ruslanlap) | 🎲 Generate random data instantly with a single keystroke. Perfect for developers, testers, designers, and anyone who needs quick access to random data. Features include secure passwords, PINs, names, business data, dates, numbers, GUIDs, color codes, and more. Especially useful for designers who need random color codes and placeholder content. |
|
||||
| [Open With Cursor](https://github.com/VictorNoxx/PowerToys-Run-Cursor/) | [VictorNoxx](https://github.com/VictorNoxx) | Open Visual Studio, VS Code recents with Cursor AI |
|
||||
| [CheatSheets](https://github.com/ruslanlap/PowerToysRun-CheatSheets) | [ruslanlap](https://github.com/ruslanlap) | 📚 Find cheat sheets and command examples instantly from tldr pages, cheat.sh, and devhints.io. Features include favorites system, categories, offline mode, and smart caching. |
|
||||
| [QuickAI](https://github.com/ruslanlap/PowerToysRun-QuickAi) | [ruslanlap](https://github.com/ruslanlap) | AI-powered assistance with instant, smart responses from multiple providers (Groq, Together, Fireworks, OpenRouter, Cohere) |
|
||||
|
||||
## Extending software plugins
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// 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 AdvancedPaste.Converters;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Windows.UI;
|
||||
|
||||
namespace AdvancedPaste.UnitTests.ConvertersTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class HexColorToColorConverterTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void TestConvert_ValidSixDigitHex_ReturnsColor()
|
||||
{
|
||||
Color? result = HexColorConverterHelper.ConvertHexColorToRgb("#FFBFAB");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var color = (Windows.UI.Color)result;
|
||||
Assert.AreEqual(255, color.R);
|
||||
Assert.AreEqual(191, color.G);
|
||||
Assert.AreEqual(171, color.B);
|
||||
Assert.AreEqual(255, color.A);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestConvert_ValidThreeDigitHex_ReturnsColor()
|
||||
{
|
||||
Color? result = HexColorConverterHelper.ConvertHexColorToRgb("#abc");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var color = (Windows.UI.Color)result;
|
||||
|
||||
// #abc should expand to #aabbcc
|
||||
Assert.AreEqual(170, color.R); // 0xaa
|
||||
Assert.AreEqual(187, color.G); // 0xbb
|
||||
Assert.AreEqual(204, color.B); // 0xcc
|
||||
Assert.AreEqual(255, color.A);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestConvert_NullOrEmpty_ReturnsNull()
|
||||
{
|
||||
Assert.IsNull(HexColorConverterHelper.ConvertHexColorToRgb(null));
|
||||
Assert.IsNull(HexColorConverterHelper.ConvertHexColorToRgb(string.Empty));
|
||||
Assert.IsNull(HexColorConverterHelper.ConvertHexColorToRgb(" "));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TestConvert_InvalidHex_ReturnsNull()
|
||||
{
|
||||
Assert.IsNull(HexColorConverterHelper.ConvertHexColorToRgb("#GGGGGG"));
|
||||
Assert.IsNull(HexColorConverterHelper.ConvertHexColorToRgb("#12345"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 AdvancedPaste.Helpers;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace AdvancedPaste.UnitTests.HelpersTests;
|
||||
|
||||
[TestClass]
|
||||
public sealed class ClipboardItemHelperTests
|
||||
{
|
||||
[TestMethod]
|
||||
[DataRow("#FFBFAB", true)]
|
||||
[DataRow("#000000", true)]
|
||||
[DataRow("#FFFFFF", true)]
|
||||
[DataRow("#fff", true)]
|
||||
[DataRow("#abc", true)]
|
||||
[DataRow("#123456", true)]
|
||||
[DataRow("#AbCdEf", true)]
|
||||
[DataRow("FFBFAB", false)] // Missing #
|
||||
[DataRow("#GGGGGG", false)] // Invalid hex characters
|
||||
[DataRow("#12345", false)] // Wrong length
|
||||
[DataRow("#1234567", false)] // Too long
|
||||
[DataRow("", false)]
|
||||
[DataRow(null, false)]
|
||||
[DataRow(" #FFF ", true)] // Whitespace should be trimmed
|
||||
[DataRow("Not a color", false)]
|
||||
[DataRow("#", false)]
|
||||
[DataRow("##FFFFFF", false)]
|
||||
public void TestIsRgbHexColor(string input, bool expected)
|
||||
{
|
||||
bool result = ClipboardItemHelper.IsRgbHexColor(input);
|
||||
Assert.AreEqual(expected, result, $"IsRgbHexColor(\"{input}\") should return {expected}");
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<converters:DateTimeToFriendlyStringConverter x:Key="DateTimeToFriendlyStringConverter" />
|
||||
<converters:HexColorToBrushConverter x:Key="HexColorToBrushConverter" />
|
||||
<tkconverters:BoolToVisibilityConverter x:Name="BoolToVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
<Grid ColumnSpacing="12">
|
||||
@@ -25,6 +26,26 @@
|
||||
Source="{x:Bind ClipboardItem.Image, Mode=OneWay}"
|
||||
Stretch="UniformToFill"
|
||||
Visibility="{x:Bind HasImage, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<!-- Color preview with text -->
|
||||
<Grid Visibility="{x:Bind HasColor, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Ellipse
|
||||
Grid.Column="0"
|
||||
Width="8"
|
||||
Height="8"
|
||||
Margin="8,0,8,0"
|
||||
Fill="{x:Bind ClipboardItem.Content, Mode=OneWay, Converter={StaticResource HexColorToBrushConverter}}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="10"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind ClipboardItem.Content, Mode=OneWay}"
|
||||
TextWrapping="NoWrap" />
|
||||
</Grid>
|
||||
<!-- Text preview -->
|
||||
<TextBlock
|
||||
Margin="8,0,0,0"
|
||||
|
||||
@@ -38,9 +38,11 @@ namespace AdvancedPaste.Controls
|
||||
|
||||
public bool HasImage => ContentImage is not null;
|
||||
|
||||
public bool HasText => !string.IsNullOrEmpty(ContentText) && !HasImage;
|
||||
public bool HasText => !string.IsNullOrEmpty(ContentText) && !HasImage && !HasColor;
|
||||
|
||||
public bool HasGlyph => !HasImage && !HasText && !string.IsNullOrEmpty(IconGlyph);
|
||||
public bool HasGlyph => !HasImage && !HasText && !HasColor && !string.IsNullOrEmpty(IconGlyph);
|
||||
|
||||
public bool HasColor => ClipboardItemHelper.IsRgbHexColor(ContentText);
|
||||
|
||||
public ClipboardHistoryItemPreviewControl()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
// 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.Converters
|
||||
{
|
||||
public static class HexColorConverterHelper
|
||||
{
|
||||
public static Windows.UI.Color? ConvertHexColorToRgb(string hexColor)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Remove # if present
|
||||
var cleanHex = hexColor.TrimStart('#');
|
||||
|
||||
// Expand 3-digit hex to 6-digit (#ABC -> #AABBCC)
|
||||
if (cleanHex.Length == 3)
|
||||
{
|
||||
cleanHex = $"{cleanHex[0]}{cleanHex[0]}{cleanHex[1]}{cleanHex[1]}{cleanHex[2]}{cleanHex[2]}";
|
||||
}
|
||||
|
||||
if (cleanHex.Length == 6)
|
||||
{
|
||||
var r = System.Convert.ToByte(cleanHex.Substring(0, 2), 16);
|
||||
var g = System.Convert.ToByte(cleanHex.Substring(2, 2), 16);
|
||||
var b = System.Convert.ToByte(cleanHex.Substring(4, 2), 16);
|
||||
|
||||
return Windows.UI.Color.FromArgb(255, r, g, b);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Invalid color format - return null
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
namespace AdvancedPaste.Converters
|
||||
{
|
||||
public sealed partial class HexColorToBrushConverter : IValueConverter
|
||||
{
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
=> throw new NotSupportedException();
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is not string hexColor || string.IsNullOrWhiteSpace(hexColor))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Windows.UI.Color? color = HexColorConverterHelper.ConvertHexColorToRgb(hexColor);
|
||||
|
||||
return color != null ? new SolidColorBrush((Windows.UI.Color)color) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using AdvancedPaste.Models;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
@@ -10,8 +11,11 @@ using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace AdvancedPaste.Helpers
|
||||
{
|
||||
internal static class ClipboardItemHelper
|
||||
internal static partial class ClipboardItemHelper
|
||||
{
|
||||
// Compiled regex for better performance when checking multiple clipboard items
|
||||
private static readonly Regex HexColorRegex = HexColorCompiledRegex();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a ClipboardItem from current clipboard data.
|
||||
/// </summary>
|
||||
@@ -55,6 +59,31 @@ namespace AdvancedPaste.Helpers
|
||||
return clipboardItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if text is a valid RGB hex color (e.g., #FFBFAB or #fff).
|
||||
/// </summary>
|
||||
public static bool IsRgbHexColor(string text)
|
||||
{
|
||||
if (text == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string trimmedText = text.Trim();
|
||||
if (trimmedText.Length > 7)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(trimmedText))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match #RGB or #RRGGBB format (case-insensitive)
|
||||
return HexColorRegex.IsMatch(trimmedText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a BitmapImage from clipboard data.
|
||||
/// </summary>
|
||||
@@ -80,5 +109,8 @@ namespace AdvancedPaste.Helpers
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$")]
|
||||
private static partial Regex HexColorCompiledRegex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,4 @@
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
public record DismissMessage()
|
||||
{
|
||||
}
|
||||
public record DismissMessage(bool ForceGoHome = false);
|
||||
|
||||
@@ -4,6 +4,4 @@
|
||||
|
||||
namespace Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
|
||||
public record NavigateBackMessage(bool FromBackspace = false)
|
||||
{
|
||||
}
|
||||
public record NavigateBackMessage(bool FromBackspace = false);
|
||||
|
||||
@@ -378,7 +378,7 @@ public partial class ShellViewModel : ObservableObject,
|
||||
{
|
||||
// Reset the palette to the main page and dismiss
|
||||
GoHome(withAnimation: false, focusSearch: false);
|
||||
WeakReferenceMessenger.Default.Send<DismissMessage>();
|
||||
WeakReferenceMessenger.Default.Send(new DismissMessage());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -398,7 +398,7 @@ public partial class ShellViewModel : ObservableObject,
|
||||
case CommandResultKind.Hide:
|
||||
{
|
||||
// Keep this page open, but hide the palette.
|
||||
WeakReferenceMessenger.Default.Send<DismissMessage>();
|
||||
WeakReferenceMessenger.Default.Send(new DismissMessage());
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@ public partial class SettingsModel : ObservableObject
|
||||
|
||||
public TimeSpan AutoGoHomeInterval { get; set; } = Timeout.InfiniteTimeSpan;
|
||||
|
||||
public EscapeKeyBehavior EscapeKeyBehaviorSetting { get; set; } = EscapeKeyBehavior.ClearSearchFirstThenGoBack;
|
||||
|
||||
// END SETTINGS
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -282,3 +284,11 @@ public enum MonitorBehavior
|
||||
InPlace = 3,
|
||||
ToLast = 4,
|
||||
}
|
||||
|
||||
public enum EscapeKeyBehavior
|
||||
{
|
||||
ClearSearchFirstThenGoBack = 0,
|
||||
AlwaysGoBack = 1,
|
||||
AlwaysDismiss = 2,
|
||||
AlwaysHide = 3,
|
||||
}
|
||||
|
||||
@@ -160,6 +160,16 @@ public partial class SettingsViewModel : INotifyPropertyChanged
|
||||
}
|
||||
}
|
||||
|
||||
public int EscapeKeyBehaviorIndex
|
||||
{
|
||||
get => (int)_settings.EscapeKeyBehaviorSetting;
|
||||
set
|
||||
{
|
||||
_settings.EscapeKeyBehaviorSetting = (EscapeKeyBehavior)value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ProviderSettingsViewModel> CommandProviders { get; } = [];
|
||||
|
||||
public SettingsExtensionsViewModel Extensions { get; }
|
||||
|
||||
@@ -142,9 +142,9 @@
|
||||
FontSize="16"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
x:Uid="SettingsButtonTextBlock"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="Settings" />
|
||||
Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<TextBlock
|
||||
|
||||
@@ -7,8 +7,10 @@ using CommunityToolkit.WinUI;
|
||||
using Microsoft.CmdPal.Core.ViewModels;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Commands;
|
||||
using Microsoft.CmdPal.Core.ViewModels.Messages;
|
||||
using Microsoft.CmdPal.UI.Messages;
|
||||
using Microsoft.CmdPal.Ext.ClipboardHistory.Messages;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.Views;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Input;
|
||||
using Microsoft.UI.Xaml;
|
||||
@@ -49,6 +51,8 @@ public sealed partial class SearchBar : UserControl,
|
||||
// 0.6+ suggestions
|
||||
private string? _textToSuggest;
|
||||
|
||||
private SettingsModel Settings => App.Current.Services.GetRequiredService<SettingsModel>();
|
||||
|
||||
public PageViewModel? CurrentPageViewModel
|
||||
{
|
||||
get => (PageViewModel?)GetValue(CurrentPageViewModelProperty);
|
||||
@@ -131,6 +135,22 @@ public sealed partial class SearchBar : UserControl,
|
||||
}
|
||||
else if (e.Key == VirtualKey.Escape)
|
||||
{
|
||||
switch (Settings.EscapeKeyBehaviorSetting)
|
||||
{
|
||||
case EscapeKeyBehavior.AlwaysGoBack:
|
||||
WeakReferenceMessenger.Default.Send<NavigateBackMessage>(new());
|
||||
break;
|
||||
|
||||
case EscapeKeyBehavior.AlwaysDismiss:
|
||||
WeakReferenceMessenger.Default.Send<DismissMessage>(new(ForceGoHome: true));
|
||||
break;
|
||||
|
||||
case EscapeKeyBehavior.AlwaysHide:
|
||||
WeakReferenceMessenger.Default.Send<HideWindowMessage>(new());
|
||||
break;
|
||||
|
||||
case EscapeKeyBehavior.ClearSearchFirstThenGoBack:
|
||||
default:
|
||||
if (string.IsNullOrEmpty(FilterBox.Text))
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigateBackMessage>(new());
|
||||
@@ -147,6 +167,9 @@ public sealed partial class SearchBar : UserControl,
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Back)
|
||||
|
||||
@@ -530,6 +530,11 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
public void Receive(DismissMessage message)
|
||||
{
|
||||
if (message.ForceGoHome)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send(new GoHomeMessage(false, false));
|
||||
}
|
||||
|
||||
// This might come in off the UI thread. Make sure to hop back.
|
||||
DispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
|
||||
@@ -133,7 +133,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
|
||||
if (!message.FromBackspace)
|
||||
{
|
||||
// If we can't go back then we must be at the top and thus escape again should quit.
|
||||
WeakReferenceMessenger.Default.Send<DismissMessage>();
|
||||
WeakReferenceMessenger.Default.Send(new DismissMessage());
|
||||
|
||||
PowerToysTelemetry.Log.WriteEvent(new CmdPalDismissedOnEsc());
|
||||
}
|
||||
|
||||
@@ -89,6 +89,15 @@
|
||||
<ToggleSwitch IsOn="{x:Bind viewModel.BackspaceGoesBack, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="Settings_GeneralPage_EscapeKeyBehavior_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<ComboBox MinWidth="{StaticResource SettingActionControlMinWidth}" SelectedIndex="{x:Bind viewModel.EscapeKeyBehaviorIndex, Mode=TwoWay}">
|
||||
<ComboBoxItem x:Uid="Settings_GeneralPage_EscapeKeyBehavior_Option_DismissEmptySearchOrGoBack" />
|
||||
<ComboBoxItem x:Uid="Settings_GeneralPage_EscapeKeyBehavior_Option_AlwaysGoBack" />
|
||||
<ComboBoxItem x:Uid="Settings_GeneralPage_EscapeKeyBehavior_Option_AlwaysDismiss" />
|
||||
<ComboBoxItem x:Uid="Settings_GeneralPage_EscapeKeyBehavior_Option_AlwaysHide" />
|
||||
</ComboBox>
|
||||
</controls:SettingsCard>
|
||||
|
||||
<controls:SettingsCard x:Uid="Settings_GeneralPage_SingleClickActivation_SettingsCard" HeaderIcon="{ui:FontIcon Glyph=}">
|
||||
<ToggleSwitch IsOn="{x:Bind viewModel.SingleClickActivates, Mode=TwoWay}" />
|
||||
</controls:SettingsCard>
|
||||
|
||||
@@ -556,4 +556,25 @@ Right-click to remove the key combination, thereby deactivating the shortcut.</v
|
||||
<data name="Settings_PageTitles_ExtensionsPage" xml:space="preserve">
|
||||
<value>Extensions</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_EscapeKeyBehavior_Option_DismissEmptySearchOrGoBack.Content" xml:space="preserve">
|
||||
<value>Clear search first, then go back</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_EscapeKeyBehavior_Option_AlwaysGoBack.Content" xml:space="preserve">
|
||||
<value>Go back</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_EscapeKeyBehavior_Option_AlwaysDismiss.Content" xml:space="preserve">
|
||||
<value>Hide window and go home</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_EscapeKeyBehavior_Option_AlwaysHide.Content" xml:space="preserve">
|
||||
<value>Hide window</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_EscapeKeyBehavior_SettingsCard.Header" xml:space="preserve">
|
||||
<value>Escape key behavior</value>
|
||||
</data>
|
||||
<data name="Settings_GeneralPage_EscapeKeyBehavior_SettingsCard.Description" xml:space="preserve">
|
||||
<value>Choose how Escape key behaves</value>
|
||||
</data>
|
||||
<data name="SettingsButtonTextBlock.Text" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -19,7 +19,7 @@ namespace Microsoft.CmdPal.Ext.RemoteDesktop.Properties {
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
@@ -88,7 +88,8 @@ namespace Microsoft.CmdPal.Ext.RemoteDesktop.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Unable to initialize Microsoft Terminal Service Client. Ensure it is enabled in Windows Settings.\r{0}.
|
||||
/// Looks up a localized string similar to Unable to initialize Microsoft Terminal Service Client. Ensure it is enabled in Windows Settings.
|
||||
///{0}.
|
||||
/// </summary>
|
||||
public static string remotedesktop_log_mstsc_error {
|
||||
get {
|
||||
|
||||
@@ -142,7 +142,8 @@
|
||||
<value>A list of connections to include in the query results by default</value>
|
||||
</data>
|
||||
<data name="remotedesktop_log_mstsc_error" xml:space="preserve">
|
||||
<value>Unable to initialize Microsoft Terminal Service Client. Ensure it is enabled in Windows Settings.\r{0}</value>
|
||||
<value>Unable to initialize Microsoft Terminal Service Client. Ensure it is enabled in Windows Settings.
|
||||
{0}</value>
|
||||
</data>
|
||||
<data name="remotedesktop_log_invalid_hostname" xml:space="preserve">
|
||||
<value>The hostname '{0}' was invalid. Ensure you're using a valid hostname or IP address.</value>
|
||||
|
||||
@@ -224,13 +224,13 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_X => new[] { "ẋ", "×" },
|
||||
LetterKey.VK_Y => new[] { "ẏ", "ꝡ" },
|
||||
LetterKey.VK_Z => new[] { "ʒ", "ǯ", "ℤ" },
|
||||
LetterKey.VK_COMMA => new[] { "∙", "₋", "⁻", "–", "√" }, // – is in VK_MINUS for other languages, but not VK_COMMA, so we add it here.
|
||||
LetterKey.VK_COMMA => new[] { "∙", "₋", "⁻", "–", "√", "‟", "《", "》", "‛", "〈", "〉", "″", "‴", "⁗" }, // – is in VK_MINUS for other languages, but not VK_COMMA, so we add it here.
|
||||
LetterKey.VK_PERIOD => new[] { "…", "⁝", "\u0300", "\u0301", "\u0302", "\u0303", "\u0304", "\u0308", "\u030B", "\u030C" },
|
||||
LetterKey.VK_MINUS => new[] { "~", "‐", "‑", "‒", "—", "―", "⁓", "−", "⸺", "⸻", "∓" },
|
||||
LetterKey.VK_SLASH_ => new[] { "÷", "√" },
|
||||
LetterKey.VK_DIVIDE_ => new[] { "÷", "√" },
|
||||
LetterKey.VK_MULTIPLY_ => new[] { "×", "⋅" },
|
||||
LetterKey.VK_PLUS => new[] { "≤", "≥", "≠", "≈", "≙", "⊕", "⊗", "∓", "≅", "≡" },
|
||||
LetterKey.VK_PLUS => new[] { "≤", "≥", "≠", "≈", "≙", "⊕", "⊗", "±", "≅", "≡", "₊", "⁺" },
|
||||
LetterKey.VK_BACKSLASH => new[] { "`", "~" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
@@ -302,6 +302,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_E => new[] { "€" },
|
||||
LetterKey.VK_S => new[] { "š" },
|
||||
LetterKey.VK_Z => new[] { "ž" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "»", "«" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -317,6 +318,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_U => new[] { "ü" },
|
||||
LetterKey.VK_Z => new[] { "ž" },
|
||||
LetterKey.VK_S => new[] { "š" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "«", "»" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -344,6 +346,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_A => new[] { "ä", "å" },
|
||||
LetterKey.VK_E => new[] { "€" },
|
||||
LetterKey.VK_O => new[] { "ö" },
|
||||
LetterKey.VK_COMMA => new[] { "”", "’", "»" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -360,6 +363,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_O => new[] { "ô", "ö", "ó", "ò", "õ", "œ" },
|
||||
LetterKey.VK_U => new[] { "û", "ù", "ü", "ú" },
|
||||
LetterKey.VK_Y => new[] { "ÿ", "ý" },
|
||||
LetterKey.VK_COMMA => new[] { "«", "»", "‹", "›", "“", "”", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -376,6 +380,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_U => new[] { "ú" },
|
||||
LetterKey.VK_Y => new[] { "ý" },
|
||||
LetterKey.VK_T => new[] { "þ" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "‚", "‘" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -393,7 +398,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_N => new[] { "ñ" },
|
||||
LetterKey.VK_O => new[] { "ó" },
|
||||
LetterKey.VK_U => new[] { "ú", "ü" },
|
||||
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!" },
|
||||
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!", "«", "»", "“", "”", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -411,7 +416,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_O => new[] { "ò", "ó" },
|
||||
LetterKey.VK_U => new[] { "ù", "ú", "ü" },
|
||||
LetterKey.VK_L => new[] { "·" },
|
||||
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!" },
|
||||
LetterKey.VK_COMMA => new[] { "¿", "?", "¡", "!", "«", "»", "“", "”", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -427,6 +432,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_O => new[] { "ō" },
|
||||
LetterKey.VK_S => new[] { "$" },
|
||||
LetterKey.VK_U => new[] { "ū" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "”", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -443,6 +449,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_N => new[] { "ñ" },
|
||||
LetterKey.VK_O => new[] { "ó", "ö", "ô" },
|
||||
LetterKey.VK_U => new[] { "ú", "ü", "û" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "„", "”", "‘", ",", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -469,6 +476,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_V => new[] { "ü", "ǖ", "ǘ", "ǚ", "ǜ" },
|
||||
LetterKey.VK_Y => new[] { "¥" },
|
||||
LetterKey.VK_Z => new[] { "ẑ" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "”", "‘", "’", "「", "」", "『", "』" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -505,6 +513,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_S => new[] { "ş" },
|
||||
LetterKey.VK_T => new[] { "₺" },
|
||||
LetterKey.VK_U => new[] { "ü", "û" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "”", "‘", "’", "«", "»", "‹", "›" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -522,6 +531,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_O => new[] { "ó" },
|
||||
LetterKey.VK_S => new[] { "ś" },
|
||||
LetterKey.VK_Z => new[] { "ż", "ź" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "”", "‘", "’", "»", "«" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -536,10 +546,9 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_E => new[] { "é", "ê", "€" },
|
||||
LetterKey.VK_I => new[] { "í" },
|
||||
LetterKey.VK_O => new[] { "ô", "ó", "õ", "º" },
|
||||
LetterKey.VK_P => new[] { "π" },
|
||||
LetterKey.VK_S => new[] { "$" },
|
||||
LetterKey.VK_U => new[] { "ú" },
|
||||
LetterKey.VK_COMMA => new[] { "≤", "≥", "≠", "≈", "≙", "±", "₊", "⁺" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "”", "‘", "’", "«", "»" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -594,6 +603,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_U => new[] { "ú" },
|
||||
LetterKey.VK_Y => new[] { "ý" },
|
||||
LetterKey.VK_Z => new[] { "ž" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "‚", "‘", "»", "«", "›", "‹" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -608,6 +618,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_I => new[] { "í" },
|
||||
LetterKey.VK_O => new[] { "ó" },
|
||||
LetterKey.VK_U => new[] { "ú" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "”", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -623,6 +634,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_O => new[] { "ò" },
|
||||
LetterKey.VK_P => new[] { "£" },
|
||||
LetterKey.VK_U => new[] { "ù" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "”", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -645,6 +657,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_U => new[] { "ů", "ú" },
|
||||
LetterKey.VK_Y => new[] { "ý" },
|
||||
LetterKey.VK_Z => new[] { "ž" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "‚", "‘", "»", "«", "›", "‹" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -659,6 +672,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_O => new[] { "ö" },
|
||||
LetterKey.VK_S => new[] { "ß" },
|
||||
LetterKey.VK_U => new[] { "ü" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "‚", "‘", "»", "«", "›", "‹" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -689,6 +703,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_X => new string[] { "ξ" },
|
||||
LetterKey.VK_Y => new string[] { "υ" },
|
||||
LetterKey.VK_Z => new string[] { "ζ" },
|
||||
LetterKey.VK_COMMA => new[] { "“", "”", "«", "»", },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -710,9 +725,9 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_U => new[] { "וֹ", "וּ", "װ", "\u05b9" },
|
||||
LetterKey.VK_X => new[] { "\u05b6", "\u05b1" },
|
||||
LetterKey.VK_Y => new[] { "ױ" },
|
||||
LetterKey.VK_COMMA => new[] { "”", "’", "״", "׳" },
|
||||
LetterKey.VK_COMMA => new[] { "”", "’", "'", "״", "׳" },
|
||||
LetterKey.VK_PERIOD => new[] { "\u05ab", "\u05bd", "\u05bf" },
|
||||
LetterKey.VK_MINUS => new[] { "–", "־" },
|
||||
LetterKey.VK_MINUS => new[] { "־" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -727,6 +742,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_I => new[] { "í" },
|
||||
LetterKey.VK_O => new[] { "ó", "ő", "ö" },
|
||||
LetterKey.VK_U => new[] { "ú", "ű", "ü" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "”", "»", "«" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -740,6 +756,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_I => new[] { "î" },
|
||||
LetterKey.VK_S => new[] { "ș" },
|
||||
LetterKey.VK_T => new[] { "ț" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "”", "«", "»" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -754,6 +771,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_I => new[] { "ì", "í" },
|
||||
LetterKey.VK_O => new[] { "ò", "ó" },
|
||||
LetterKey.VK_U => new[] { "ù", "ú" },
|
||||
LetterKey.VK_COMMA => new[] { "«", "»", "“", "”", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -772,6 +790,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_R => new[] { "ř" },
|
||||
LetterKey.VK_S => new[] { "ş" },
|
||||
LetterKey.VK_U => new[] { "û", "ü" },
|
||||
LetterKey.VK_COMMA => new[] { "«", "»", "“", "”" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -789,6 +808,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_U => new[] { "û", "ü", "ù", "ú" },
|
||||
LetterKey.VK_Y => new[] { "ŷ", "ÿ", "ỳ", "ý" },
|
||||
LetterKey.VK_W => new[] { "ŵ", "ẅ", "ẁ", "ẃ" },
|
||||
LetterKey.VK_COMMA => new[] { "‘", "’", "“", "“" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -801,6 +821,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_A => new[] { "å", "ä" },
|
||||
LetterKey.VK_E => new[] { "é" },
|
||||
LetterKey.VK_O => new[] { "ö" },
|
||||
LetterKey.VK_COMMA => new[] { "”", "’", "»", "«" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -814,6 +835,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_D => new[] { "đ" },
|
||||
LetterKey.VK_S => new[] { "š" },
|
||||
LetterKey.VK_Z => new[] { "ž" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "‚", "’", "»", "«", "›", "‹" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -838,6 +860,7 @@ namespace PowerAccent.Core
|
||||
{
|
||||
LetterKey.VK_E => new[] { "ѐ" },
|
||||
LetterKey.VK_I => new[] { "ѝ" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "’", "‘" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -869,6 +892,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_E => new[] { "€", "é" },
|
||||
LetterKey.VK_O => new[] { "ø" },
|
||||
LetterKey.VK_S => new[] { "$" },
|
||||
LetterKey.VK_COMMA => new[] { "«", "»", ",", "‘", "’", "„", "“" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -881,6 +905,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_A => new[] { "å", "æ" },
|
||||
LetterKey.VK_E => new[] { "€" },
|
||||
LetterKey.VK_O => new[] { "ø" },
|
||||
LetterKey.VK_COMMA => new[] { "»", "«", "“", "”", "›", "‹", "‘", "’" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -897,6 +922,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_S => new[] { "š" },
|
||||
LetterKey.VK_U => new[] { "ų", "ū" },
|
||||
LetterKey.VK_Z => new[] { "ž" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "‚", "‘" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
@@ -910,6 +936,7 @@ namespace PowerAccent.Core
|
||||
LetterKey.VK_E => new[] { "€" },
|
||||
LetterKey.VK_S => new[] { "š" },
|
||||
LetterKey.VK_Z => new[] { "ž" },
|
||||
LetterKey.VK_COMMA => new[] { "„", "“", "»", "«" },
|
||||
_ => Array.Empty<string>(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public partial class PowerAccent : IDisposable
|
||||
|
||||
// Keys that show a description (like dashes) when ShowCharacterInfoSetting is 1
|
||||
private readonly LetterKey[] _letterKeysShowingDescription = new LetterKey[] { LetterKey.VK_O };
|
||||
private const double ScreenMinPadding = 150;
|
||||
|
||||
private bool _visible;
|
||||
private string[] _characters = Array.Empty<string>();
|
||||
@@ -323,13 +324,17 @@ public partial class PowerAccent : IDisposable
|
||||
public Point GetDisplayCoordinates(Size window)
|
||||
{
|
||||
(Point Location, Size Size, double Dpi) activeDisplay = WindowsFunctions.GetActiveDisplay();
|
||||
double primaryDPI = Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth;
|
||||
Rect screen = new Rect(activeDisplay.Location, activeDisplay.Size) / primaryDPI;
|
||||
Rect screen = new(activeDisplay.Location, activeDisplay.Size);
|
||||
Position position = _settingService.Position;
|
||||
|
||||
/* Debug.WriteLine("Dpi: " + activeDisplay.Dpi); */
|
||||
|
||||
return Calculation.GetRawCoordinatesFromPosition(position, screen, window);
|
||||
return Calculation.GetRawCoordinatesFromPosition(position, screen, window, activeDisplay.Dpi) / activeDisplay.Dpi;
|
||||
}
|
||||
|
||||
public double GetDisplayMaxWidth()
|
||||
{
|
||||
return WindowsFunctions.GetActiveDisplay().Size.Width - ScreenMinPadding;
|
||||
}
|
||||
|
||||
public Position GetToolbarPosition()
|
||||
@@ -353,20 +358,21 @@ public partial class PowerAccent : IDisposable
|
||||
|
||||
public static string[] ToUpper(string[] array)
|
||||
{
|
||||
string[] result = new string[array.Length];
|
||||
List<string> result = new(array.Length);
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
switch (array[i])
|
||||
{
|
||||
case "ß": result[i] = "ẞ"; break;
|
||||
case "ǰ": result[i] = "J\u030c"; break;
|
||||
case "ı\u0307\u0304": result[i] = "İ\u0304"; break;
|
||||
case "ı": result[i] = "İ"; break;
|
||||
case "ᵛ": result[i] = "ⱽ"; break;
|
||||
default: result[i] = array[i].ToUpper(System.Globalization.CultureInfo.InvariantCulture); break;
|
||||
case "ß": result.Add("ẞ"); break;
|
||||
case "ǰ": result.Add("J\u030c"); break;
|
||||
case "ı\u0307\u0304": result.Add("İ\u0304"); break;
|
||||
case "ı": result.Add("İ"); break;
|
||||
case "ᵛ": result.Add("ⱽ"); break;
|
||||
case "ϑ": break;
|
||||
default: result.Add(array[i].ToUpper(CultureInfo.InvariantCulture)); break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return [..result];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,18 +18,18 @@ namespace PowerAccent.Core.Tools
|
||||
top < screen.Y ? caret.Y + 20 : top);
|
||||
}
|
||||
|
||||
public static Point GetRawCoordinatesFromPosition(Position position, Rect screen, Size window)
|
||||
public static Point GetRawCoordinatesFromPosition(Position position, Rect screen, Size window, double dpi)
|
||||
{
|
||||
int offset = 24;
|
||||
|
||||
double pointX = position switch
|
||||
{
|
||||
Position.Top or Position.Bottom or Position.Center
|
||||
=> screen.X + (screen.Width / 2) - (window.Width / 2),
|
||||
=> screen.X + (screen.Width / 2) - (window.Width * dpi / 2),
|
||||
Position.TopLeft or Position.Left or Position.BottomLeft
|
||||
=> screen.X + offset,
|
||||
Position.TopRight or Position.Right or Position.BottomRight
|
||||
=> screen.X + screen.Width - (window.Width + offset),
|
||||
=> screen.X + screen.Width - ((window.Width * dpi) + offset),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
@@ -38,9 +38,9 @@ namespace PowerAccent.Core.Tools
|
||||
Position.TopLeft or Position.Top or Position.TopRight
|
||||
=> screen.Y + offset,
|
||||
Position.Left or Position.Center or Position.Right
|
||||
=> screen.Y + (screen.Height / 2) - (window.Height / 2),
|
||||
=> screen.Y + (screen.Height / 2) - (window.Height * dpi / 2),
|
||||
Position.BottomLeft or Position.Bottom or Position.BottomRight
|
||||
=> screen.Y + screen.Height - (window.Height + offset),
|
||||
=> screen.Y + screen.Height - ((window.Height * dpi) + offset),
|
||||
_ => throw new NotImplementedException(),
|
||||
};
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
|
||||
_selectedIndex = index;
|
||||
characters.SelectedIndex = _selectedIndex;
|
||||
characterName.Text = _powerAccent.CharacterDescriptions[_selectedIndex];
|
||||
characters.ScrollIntoView(character);
|
||||
}
|
||||
|
||||
private void PowerAccent_OnChangeDisplay(bool isActive, string[] chars)
|
||||
@@ -73,6 +74,7 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
|
||||
characters.ItemsSource = chars;
|
||||
characters.SelectedIndex = _selectedIndex;
|
||||
this.UpdateLayout(); // Required for filling the actual width/height before positioning.
|
||||
SetWindowsSize();
|
||||
SetWindowPosition();
|
||||
Show();
|
||||
Microsoft.PowerToys.Telemetry.PowerToysTelemetry.Log.WriteEvent(new PowerAccent.Core.Telemetry.PowerAccentShowAccentMenuEvent());
|
||||
@@ -96,6 +98,11 @@ public partial class Selector : FluentWindow, IDisposable, INotifyPropertyChange
|
||||
this.Top = position.Y;
|
||||
}
|
||||
|
||||
private void SetWindowsSize()
|
||||
{
|
||||
this.characters.MaxWidth = _powerAccent.GetDisplayMaxWidth();
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
_powerAccent.SaveUsageInfo();
|
||||
|
||||
Binary file not shown.
@@ -33,6 +33,7 @@
|
||||
<TextBlock
|
||||
Margin="16,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.HeadingLevel="Level2"
|
||||
FontSize="16"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind Title, Mode=OneWay}" />
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
MaxWidth="{StaticResource PageMaxWidth}"
|
||||
Margin="1,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.HeadingLevel="1"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
|
||||
@@ -638,9 +638,6 @@ Please review the placeholder content that represents the final terms and usage
|
||||
<data name="AdvancedPaste_EnableAIDialogAcceptanceCheckBox.Content" xml:space="preserve">
|
||||
<value>I have read and accept the information above.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAdvancedAIModerationToggle.Content" xml:space="preserve">
|
||||
<value>Enable OpenAI content moderation</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnablePasteAIModerationToggle.Header" xml:space="preserve">
|
||||
<value>Enable OpenAI content moderation</value>
|
||||
</data>
|
||||
@@ -2092,9 +2089,6 @@ Made with 💗 by Microsoft and the PowerToys community.</value>
|
||||
<data name="TranscodeToMp4.Header" xml:space="preserve">
|
||||
<value>Transcode to .mp4 (H.264/AAC)</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialogOpenAIApiKey.Text" xml:space="preserve">
|
||||
<value>OpenAI API key:</value>
|
||||
</data>
|
||||
<data name="EnableAIDialog_SaveBtnText" xml:space="preserve">
|
||||
<value>Save</value>
|
||||
</data>
|
||||
@@ -4029,7 +4023,7 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<comment>Product name: Navigation view item name for Advanced Paste</comment>
|
||||
</data>
|
||||
<data name="AdvancedPaste.ModuleDescription" xml:space="preserve">
|
||||
<value>A tool to quickly format clipboard content into plain text, Markdown, JSON, and more. An AI-powered option requiring an OpenAI API key is available for advanced formatting.</value>
|
||||
<value>Formats clipboard content into plain text, Markdown, JSON, and more. Advanced formatting can use an online or local language model endpoint.</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste.ModuleTitle" xml:space="preserve">
|
||||
<value>Advanced Paste</value>
|
||||
@@ -4559,18 +4553,9 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<data name="TermsLink.Text" xml:space="preserve">
|
||||
<value>OpenAI Terms</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialog_Description.Text" xml:space="preserve">
|
||||
<value>Paste with AI allows you to format your clipboard content into any format you need. Learn more about the terms of conditions while using OpenAI and privacy at Microsoft:</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialog_LoginIntoText.Text" xml:space="preserve">
|
||||
<value>• Login into your</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialog_ConfigureOpenAIKey.Text" xml:space="preserve">
|
||||
<value>Configure OpenAI key</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialog_OpenAIApiKeysOverviewText.Text" xml:space="preserve">
|
||||
<value>OpenAI API keys overview</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialog_CreateNewKeyText.Text" xml:space="preserve">
|
||||
<value>• Create a new secret key and paste it in the field below</value>
|
||||
</data>
|
||||
@@ -4588,9 +4573,6 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<data name="Peek_SourceCode_FontSize.Header" xml:space="preserve">
|
||||
<value>Font size</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialog_NoteAICreditsText.Text" xml:space="preserve">
|
||||
<value>• NOTE: You need to have available paid credits in your OpenAI account to use this feature. If you do not have credits you will see an 'API key quota exceeded' error</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_EnableAIDialog_NoteAICreditsErrorText.Text" xml:space="preserve">
|
||||
<value>If you do not have credits you will see an 'API key quota exceeded' error</value>
|
||||
</data>
|
||||
|
||||
Reference in New Issue
Block a user