Compare commits

...

56 Commits

Author SHA1 Message Date
Jaime Bernardo
5259a1ba5e Update README.md
Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com>
2024-12-16 19:25:00 +00:00
Jaime Bernardo
e314fc75df Fix spellcheck 2024-12-13 16:04:29 +00:00
Jaime Bernardo
a38c4c8353 0.87 changelog 2024-12-13 15:57:09 +00:00
Stefan Markovic
f19a34d50c [FZEditor]Fix Create new layout dialog radio buttons IsChecked values (#36320) 2024-12-12 14:53:16 +00:00
Clint Rutkas
05e5e92199 Making the powertoys-code-owners team code owners (#36310)
* Update CODEOWNERS

* Update names.txt

* Update CODEOWNERS
2024-12-11 14:38:49 -08:00
Clint Rutkas
7aba6e8e75 Update CODEOWNERS to include gordon, jerry and kayla (#36308)
* Update CODEOWNERS

* Update names.txt
2024-12-11 13:47:45 -08:00
Jaime Bernardo
c5dc93f720 [ci]Sign OpenAI dll that's not signed (#36299) 2024-12-11 13:50:16 +00:00
Davide Giacometti
ce0e00fa3b [Settings][Dashboard] Accessibility fixes (#36280)
* make narrator announce buttons/toggles

* add toggles module name
2024-12-11 11:09:38 +00:00
Niels Laute
7a39f2fd46 [UX]Updating New+ and Settings icons (#36290)
* Updated icons

* Updating more icons and icos
2024-12-11 10:13:56 +00:00
Ionuț Manța
7c6af6580e [Launcher]Port from WPF-UI to .NET 9 WPF (#36215)
* Initial implementation

* Fix fluent style

* Fix no endline

* Update expect.txt

* Fix formatting

* Fix light theme looking bad on Windows 10

* fix formatting

* test change

* Now really fixed W10

* Add a comment

* Fix typos

* Fix spellcheck errors

* Fix spellcheck pattern for websites

* Change patterns for spellcheck in the right file

* Fix XAML styling

* Fix contrast colors on W11

* Fix formatting

* Removed emty line

* Fix formatting

* Added comment to fluentHC file

* fix comment

* Fix Windows10 again.
Adress feedback.

* W11 fix chaning from high contrast to normal not having correct background

* W10 Fix high contrast not working after switching from light/dark moed

* Address feedback

* Fix formatting

* Second W11 fix chaning from high contrast to normal not having correct background
2024-12-11 09:47:08 +00:00
Ani
bf3474b134 [AdvancedPaste]Add Semantic Kernel opt-in to allow chaining of paste actions (#35902)
* [AdvancedPaste] Semantic Kernel support

* Changed log-line with potentially sensitive info

* Spellcheck issues

* Various improvements for Semantic Kernel

* Spellcheck issue

* Refactored Clipboard routines

* Added integration tests for KernelService

* Extra telemetry for AdvancedPaste

* Added 'Hotkey' suffix to AdvancedPaste_Settings telemetry event

* Added IsSavedQuery

* Added KernelQueryCache

* Refactoring

* Added KernelQueryCache to BugReportTool delete list

* Added opt-n for Semantic Kernel

* Fixed bug with KernelQueryCache

* Ability to view last AI chat message on error

* Improved kernel query cache

* Used System.IO.Abstractions and improved tests

* Fixed under-count of token usage

* Used Semantic Kernel icon

* Cleanup

* Add missing EndProject line

* Fix dependency version conflicts

* Fix NOTICE.md

* Correct place of SemanticKernel in NOTICE.md

* Unlinked CustomPreview toggle from AI

* Added Microsoft.Bcl.AsyncInterfaces dependency to AdvancedPaste

* Fixed NOTICE.md order

* Moved Custom Preview to behaviour section

* Made Image to Text raise error on empty output

* Added AIServiceBatchIntegrationTests

* Updated AIServiceBatchIntegrationTests

* Added prompt moderation

* Moved GPO Infobar to better location
2024-12-11 09:28:44 +00:00
Clint Rutkas
474b0cfbdf Move the XamlStyler config to src/ (#36202)
my never ending goal to minimize files in the root dir
2024-12-10 17:06:30 -06:00
Laszlo Nemeth
9591950875 [Workspaces] detecting right app version (#36100)
* [Workspaces] Add move functionality

* spell checker

* [Workspaces] Modify Arranger to move apps without launch

* moved ipc helper

* removed callback

* use LauncherStatus in WindowArranger

* wait for launching next app

* launch in a separate thread and protect by mutexes

* update app version in advance

* changed canceling launch

* increased waiting time

* Fix optional parameter load from json

* changed arranger waiting time

* additional waiting time for Outlook

* added app id

* ensure ids before launch

* set id in editor

* minor updates

* [Workspaces] Move: Get the nearest window when moving a window

* [Workspaces] convert optional boolean to enum to avoid json problems

* Handle case when the new Application Property "moveIfExists" does not exist

* Re-implementing app-window pairing for moving feature.

* spell checker

* XAML formatting

* Fixing bug: IPC message not arriving

* spell checker

* Removing app-level-setting for move app. Also fixed compiler errors due styling.

* Updating editor window layout

* Re-implementing window positioning UI elements

* XAML formatting

* Code review findings

* Code cleanup

* Code cleanup

* Code cleanup

* code cleanup

* Code cleanup

* Code cleanup

* [Workspaces] fix detection of specific version of apps

---------

Co-authored-by: Seraphima <zykovas91@gmail.com>
Co-authored-by: donlaci <donlaci@yahoo.com>
2024-12-09 18:47:02 +01:00
Den Delimarsky 🔐
ef672b5564 Awake Updates - TILLSON_11272024 (#36049)
* Update with bug fixes for tray icon and support for parent process

* Process information enum

* Update the docs

* Fix spelling

* Make sure that PID is used in PT config flow

* Logic for checks based on #34148

* Update with link to PR

* Fixes #34717

* Small cleanup

* Proper task segmentation in a function

* Cleanup the code

* Fix synchronization context issue

* Update planning doc

* Test disabling caching to see if that manages to pass CI

* Cleanup to make sure that we're logging things properly.

* Update ci.yml

* Disable cache to pass CI

* Retry logic

* Cleanup

* Code cleanup

* Fixes #35848

* Update notes and codename

* After third attempt, log error instead of throwing exception

* More cleanup to avoid double execution

* Add expected word

* Safeguards for bad values for timed keep-awake

* More updates to make sure I am using uint

* Update error message

* Update packages

* Fix notice and revert CsWinRT upgrade

* Codename update

* Update expect.txt

* Update the struct

* Ensuring we're properly awaiting tray initialization

* Update to make sure tray reflects the bound process

* Cleanup, proper JSON serialization for logs.

* Not needed.

* Add command validation logic

* Moving the initialization logic earlier

* Make sure we show the display state in the tooltip

* Update tray string

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update src/modules/awake/Awake/Core/Manager.cs

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>

* Update logic for icon resets

* Update doc

* Simplify function for setting mode shell icon

* Issues should be properly linked

* Minor cleanup

* Update timed behavior

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Clint Rutkas <clint@rutkas.com>
2024-12-08 22:41:05 -08:00
leileizhang
3aec0a06ac [AOT compatible] Resolve AOT Build Error in Peek.UI (#36194)
* add partial for aot support

* add Microsoft.NET.ILLink.Tasks to packages.props

* format

* Revert "format"

This reverts commit 742d5e2214.

* add Microsoft.NET.ILLink.Tasks to notice.md

* add auto reference

* update script to remove the 'Auto-reference line'

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2024-12-09 10:17:38 +08:00
Clint Rutkas
51586b43f4 Adjust to community.md, shifting jerry's github user name (#36242)
Update COMMUNITY.md
2024-12-06 14:07:40 -08:00
Josh Soref
74a1a6eca2 Upgrade to check-spelling v0.0.24 (#36235)
This upgrades to [v0.0.24](https://github.com/check-spelling/check-spelling/releases/tag/v0.0.24).

A number of GitHub APIs are being turned off shortly, so you need to upgrade or various uncertain outcomes will occur.

There's a new accessibility forbidden pattern:

> Do not use `(click) here` links
> For more information, see:
> * https://www.w3.org/QA/Tips/noClickHere
> * https://webaim.org/techniques/hypertext/link_text
> * https://granicus.com/blog/why-click-here-links-are-bad/
> * https://heyoka.medium.com/dont-use-click-here-f32f445d1021
```pl
(?i)(?:>|\[)(?:(?:click |)here|link|(?:read |)more)(?:</|\]\()
```

There are some minor bugs that I'm aware of and which I've fixed since this release, but I don't expect to make another release this month.

I've added a pair of patterns for includes and pragmas. My argument is that the **compiler** will _generally_ tell you if you've misspelled an include and the **linker** will _generally_ tell you if you misspell a lib.

- There's a caveat here: If your include case-insensitively matches the referenced file (but doesn't properly match it), then unless you either use a case-sensitive file system (as opposed to case-preserving) or beg clang to warn, you won't notice when you make this specific mistake -- this matters in that a couple of Windows headers (e.g. Unknwn.h) have particular case and repositories don't tend to consistently/properly write them.
2024-12-06 10:33:08 -06:00
Laszlo Nemeth
1212ce2216 [Workspaces] Arranger: smart timer (#36096)
* [Workspaces] Add move functionality

* spell checker

* [Workspaces] Modify Arranger to move apps without launch

* moved ipc helper

* removed callback

* use LauncherStatus in WindowArranger

* wait for launching next app

* launch in a separate thread and protect by mutexes

* update app version in advance

* changed canceling launch

* increased waiting time

* Fix optional parameter load from json

* changed arranger waiting time

* additional waiting time for Outlook

* added app id

* ensure ids before launch

* set id in editor

* minor updates

* [Workspaces] Move: Get the nearest window when moving a window

* [Workspaces] convert optional boolean to enum to avoid json problems

* Handle case when the new Application Property "moveIfExists" does not exist

* Re-implementing app-window pairing for moving feature.

* spell checker

* XAML formatting

* Fixing bug: IPC message not arriving

* spell checker

* Removing app-level-setting for move app. Also fixed compiler errors due styling.

* Updating editor window layout

* Re-implementing window positioning UI elements

* XAML formatting

* Code review findings

* Code cleanup

* Code cleanup

* Code cleanup

* code cleanup

* Code cleanup

* Code cleanup

* [Workspaces] Arranger: Reset wait timer after each successful arrange action

* fix merge error

---------

Co-authored-by: Seraphima <zykovas91@gmail.com>
Co-authored-by: donlaci <donlaci@yahoo.com>
2024-12-06 15:09:09 +01:00
Clint Rutkas
4df8a97256 Adding Jerry to community.md (#36232)
Update COMMUNITY.md
2024-12-05 15:37:31 -08:00
Youssef Victor
a6c5420f2a [Deps]Update MSTest from 3.5.0 to 3.6.3 (#36115)
* Update MSTest from 3.5.0 to 3.6.3

* Use STA attributes that are now part of MSTest
2024-12-05 22:15:12 +00:00
Wenjian Chern
09ce610dbb [ScreenRuler]Add setting to show the measurement in an extra unit (#35887)
* display ruler: supporting millimeter and other units

* Measurement Tool: UI Setting for an extra unit

* Update images

* spelling

* spelling

* suit code style

* Fix for code review

* remove weird file

* rename field
2024-12-05 22:02:17 +00:00
Laszlo Nemeth
076461e460 [Workspaces] Implement store of app window's size and position (#36086)
* [Workspaces] Implement store of app window's size and position

* Modifying the default values to -1. The program will use the original default values for the first run.
2024-12-05 20:28:22 +01:00
Laszlo Nemeth
a9123bfc23 [Workspaces] Add encoder parameter to bitmap.save() (#36228)
* [Workspaces] Add encoder parameter to bitmap.save()

* 1 more call fixed

* Move repeated code to the csharp library
2024-12-05 20:27:28 +01:00
PesBandi
f7c9c80ab2 [PTRun][Calculator]Allow scientific notation with lowercase 'e' (#36187) 2024-12-05 15:38:59 +00:00
Seraphima Zykova
e19590f258 [Workspaces] PWA follow-up (#36217) 2024-12-05 16:24:05 +01:00
Jeremy Sinclair
eadcf4b120 [Analyzers] Update .editorconfig with rules to relax IDE errors (#36095)
* [Analyzers] Remove duplicate pascal case style from .editorconfig

* [Analyzers] Configured severity for individual IDE and CA rules showing as errors in VS

Set severity for IDE0005, IDE0008, IDE0016, IDE0018, IDE0019, IDE0021, IDE0022, IDE0023, IDE0025, IDE0027, IDE0028, IDE0029, IDE0031, IDE0032, IDE0034, IDE0036, IDE0039, IDE0042, IDE0044, IDE0045, IDE0046, IDE0047, IDE0057, IDE0051, IDE0052, IDE0054, IDE0055, IDE0056, IDE0057, IDE0059, IDE0060, IDE0061, IDE0063, IDE0071, IDE0073, IDE0074, IDE0075, IDE0077, IDE0078, IDE0083, IDE0090, IDE0100, IDE0130, IDE160, IDE180, IDE0200, IDE0240, IDE0250, IDE0251, IDE0260, IDE0270, IDE0290, IDE0300, IDE0301, IDE0305, IDE1005, IDE1006, CA1859, CA2022, CA2263

* [Analyzers] Fix mismatched analyzer descriptions

* [Analyzers] Fix misspelling

* Update .editorconfig

Made the following style rules `silent` instead of `suggestion`: 
- Use explicit type instead of 'var'
- Use expression body for ...
- Use block-scoped namespace

* [Analyzers] Set IDE0290 to silent

* [Analyzers] Remove IDE1006 configuration from .editorconfig in favor of making exclusions for the few entries

* [Analyzers][Indexer] Add IDE1006 suppressions

* [Analyzers][Peek] Add IDE1006 suppression

* [Analyzers][MWB] Add IDE1006 suppression.

* [Analyzers][Plugins] Add IDE1006 suppression

* [Analyzers][ImageResizer] Suppress IDE0073 to retain original copyright

* [Analyzers] Remove IDE0073 severity change in .editorconfig

---------

Co-authored-by: Ani <115020168+drawbyperpetual@users.noreply.github.com>
2024-12-04 12:58:36 -05:00
Ionuț Manța
e7f17495bb [KeyboardManager]Fix mapping shift to numpad (#35890)
* Keyboard Manger fix numpad as shift

Fixed shift not being released if a numpad key as shift.

* Added comments

* Fix typo

* Fix the numpad unlocked key not working if the locked version is overridden by shift

* Fix spelling check.

* Revert the VK_CLEAR change.

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2024-12-04 17:41:12 +00:00
Laszlo Nemeth
89be43e15d [Workspaces] implement the move feature (#35480)
* [Workspaces] Add move functionality

* spell checker

* [Workspaces] Modify Arranger to move apps without launch

* moved ipc helper

* removed callback

* use LauncherStatus in WindowArranger

* wait for launching next app

* launch in a separate thread and protect by mutexes

* update app version in advance

* changed canceling launch

* increased waiting time

* Fix optional parameter load from json

* changed arranger waiting time

* additional waiting time for Outlook

* added app id

* ensure ids before launch

* set id in editor

* minor updates

* [Workspaces] Move: Get the nearest window when moving a window

* [Workspaces] convert optional boolean to enum to avoid json problems

* Handle case when the new Application Property "moveIfExists" does not exist

* Re-implementing app-window pairing for moving feature.

* spell checker

* XAML formatting

* Fixing bug: IPC message not arriving

* spell checker

* Removing app-level-setting for move app. Also fixed compiler errors due styling.

* Updating editor window layout

* Re-implementing window positioning UI elements

* XAML formatting

* Code review findings

* Code cleanup

* Code cleanup

* Code cleanup

* code cleanup

* Code cleanup

* Code cleanup

* fix Move attribute after launch and snapshot

* Extend WindowArranger with PWA functionality to detect different PWA apps. PwaHelper moved to the common library

* fix repeat counter in the editor

* Code optimization

* code cleanup, optimization

* fix double-processing window

---------

Co-authored-by: Seraphima <zykovas91@gmail.com>
Co-authored-by: donlaci <donlaci@yahoo.com>
2024-12-04 18:17:54 +01:00
Clint Rutkas
e0949cb4ea adding in new friends and amazing community members (#36204)
* adding in new friends and amazing community members

* updating names

* Update names.txt

* Update COMMUNITY.md

* Update COMMUNITY.md

* Update names.txt

* Westernized Ionut name

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2024-12-04 07:29:57 -08:00
PesBandi
35b9bcacdb [Monaco] Use syntax highlighting for .srt (#35651)
* [FilePreview] Use syntax highlighting for .srt

* Change customTokenColors to customTokenThemeRules

* Ignore text on the same line as a timestamp

* Update tokenization rules
2024-12-03 17:25:35 +00:00
Davide Giacometti
50b1342234 [PreviewPane]Use PerMonitorV2 DPI mode (#36174)
use PerMonitorV2 DPI mode
2024-12-03 17:24:43 +00:00
KITAGAWA Yasutaka
866518e119 [KeyboardManager]Add IME On, IME Off (#34697) 2024-12-03 16:06:43 +00:00
leileizhang
fe610d4d0e [AOT compatible] Add Common MSBuild Props for AOT Compatibility Validation to Be Imported by Projects for Future AOT Support (#36176)
[AOT compatible] Add Common MSBuild Props for AOT compatibility validation to be imported by projects for future support
2024-12-03 09:00:28 +08:00
Sergey Chernyaev
54aab5d109 [QuickAccent]Multi-language selection (#35539)
* PowerAccent with multi-language selection

- Updated Language enum, method signatures, and settings to support multiple language selections.
- Remove ALL language and added special characters language instead.
- Modified UI to use ListView with checkboxes for language selection, including a "Select All" option.
- Adjusted ViewModel and code-behind to handle multi-selection logic.
- Updated Resources.resw and PowerAccentViewModel.cs to reflect these changes.

* Changes Language selection UI

- Groups languages into two groups
- Sort them by localized language name
- Remove unneeded looping when no languages selected
2024-12-02 15:21:56 +00:00
Christian Gaarden Gaardmark
db23992aac [New+]Fix for renaming when creating file (#36143) 2024-12-02 15:13:54 +00:00
Kayla Cinnamon
0ee1befe7b Update README with advent calendar (#36172)
* update readme

* fix spellcheck
2024-12-01 16:26:33 -05:00
moooyo
f565874fc8 [AOT Compatible] Add word "AOT" to spelling check allow list. (#36138)
* init

* Add new empty line in the end

* Add Aot to allow list

---------

Co-authored-by: Yu Leng (from Dev Box) <yuleng@microsoft.com>
2024-11-28 20:07:13 -08:00
Dave Rayment
438ee39252 [Peek]UserSettings load logging fix and refactor (#36111)
* UserSettings refactor to fix logging bug, properly initialise defaults etc.

* Apply lock only to code which changes shared data.
2024-11-28 17:36:32 +00:00
octastylos-pseudodipteros
28304838af [FilePreview]Add support for descript.ion files (#35599)
[FilePreview] Add support for descript.ion file
2024-11-28 17:11:28 +00:00
Frederik Höft
fc29fc7426 [PTRun][ValueGenerator]Add support for UUIDv7 (#35757)
* add Run support for UUIDv7 generation

* simplify comments and maybe satisfy spell check

* fix endianess

* prefer stack allocation for temporary fixed-size buffer

* perhaps the async test caused the pipeline to hang

* switch to .NET 9 BCL implementation of UUIDv7

* add UUIDv7 to input query suggestions + update exception messages to include v7

* simplify Guid description switch + update devdocs
2024-11-28 15:52:16 +00:00
Shuai Yuan
3dc491339a Upgrade Windows App SDK to the latest version (#36093)
Signed-off-by: Shawn Yuan <shuai.yuan.zju@gmail.com>
2024-11-27 13:57:20 -08:00
grzhan
271d64b8dc [PTRun][Docs] Add HttpStatusCodes to Third-Party plugins (#36055)
* [PTRun][Docs] Add HttpStatusCodes to Third-Party plugins

* adding grzhan to names.txt

---------

Co-authored-by: Clint Rutkas <clint@rutkas.com>
2024-11-27 11:44:56 -08:00
Andrea Bartolucci
a90b646e72 [PTRun][WindowWalker]Added score to the results (#35691)
Added Score to the results returned by WindowWalker

Co-authored-by: Andrea Bartolucci <8514491+andbartol@users.noreply.github.com>
2024-11-27 17:30:25 +00:00
Christian Gaarden Gaardmark
084402a2bd [New+]Windows 10 support (#35832) 2024-11-27 14:22:05 +00:00
Ani
08fe8089b8 [Logs]Fixed name of logging method in managed logging (#35952)
* Fixed name of method in managed logging

* Added hardening
2024-11-26 20:38:41 +00:00
Michael Clayton
53212188b7 [Mouse Jump] Customisable appearance - borders, margins, colours, etc - final part (#35521)
* [MouseJump] move Mouse Jump settings into separate control (#27511)

* [MouseJump] added Mouse Jump style controls to Settings UI (#27511)

* [MouseJump] added Mouse Jump style controls to Settings UI (#27511)

* [MouseJump] removing unused MouseJumpUI code (#27511)

* [MouseJump] whitespace (#27511)

* [MouseJump] fix spellcheck (#27511)

* [MouseJump] enabled "Copy to custom style" (#27511)

* [MouseJump] fixing build (internal members -> public) (#27511)

* [MouseJump] remove unused "using"s (#27511)

* [MouseJump] use custom styles in preview image (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] fixing failing test (#27511)

* [MouseJump] delinting to trigger a build (#27511)

* [MouseJump] updated settings preview image ("browser" header) (#27511)

* [MouseJump] upgrade default "custom" style settings in config (#27511)

* [MouseJump] fixed a glitch in settings upgrade (#27511)

* [MouseJump] fixed spell checker (#27511)

* [MouseJump] typo in resource strings (image -> images) (#27511)

* Remove unused include
2024-11-26 15:37:59 +00:00
Ishita Agarwal
7c9876582c Removed extra space from welcome page (#35778)
* Removed extra space from welcome page

* Added space between info bar and release notes
2024-11-25 21:13:44 -08:00
Dave Rayment
6cece12b85 [Peek]Add support for previewing .ahk files as plaintext (#35538) 2024-11-22 16:25:01 +00:00
Dave Rayment
863f7aa233 [Peek] Fix Monaco assets folder discovery (#35925) 2024-11-22 16:24:15 +00:00
Dave Rayment
b81478eb97 [Peek]Expand image format support for Image Previewer using local capabilities (#35622)
* Use BitmapDecoder to query compatible file extensions.

* Delete spellcheck exceptions for removed file types

* Remove unused usings.
2024-11-22 14:49:35 +00:00
Søren Kottal
40acdea356 Adds two new third party plugins for Run (#35453)
* Update thirdPartyRunPlugins.md

* Update expect.txt

---------

Co-authored-by: Clint Rutkas <clint@rutkas.com>
2024-11-21 07:35:18 -08:00
Jaime Bernardo
f1322d22c3 [ci]Fix signing new Workspaces dll in Dart (#36036) 2024-11-21 08:29:57 -06:00
Laszlo Nemeth
6b3bbf7e8f [Workspaces] Implement PWA recognition, launch. (#35913)
* [Workspaces] PWA: first steps: implement PWA app searcher, add basic controls to the editor

* spell checker

* Snapshot tool: adding command line args for edge

* PWA: add icon handling, add launch of PWA

* Impllement Aumid getters and comparison to connect PWA windows and processes. Update LauncherUI, Launcher

* Minor fixes, simplifications

* Spell checker

* Removing manual PWA selection, spell checker

* Fix merge conflict

* Trying to convince spell checker, that "PEB" is a correct word.

* XAML format fix

* Extending snapshot tool by logs for better testablility

* spell checker fix

* extending logs

* extending logs

* Removing some logs, modifying search criteria for pwa helper process search

* extending PWA detection for the case the directory with the app-id is missing

* Fix issue when pwaAppId is null

* fix missing pwa-app-id handling in the editor. Removed unused property (code cleaning) and updating json parser in Launcher

* Code cleaning: Moving duplicate code to a common project

* Fix issue: adding new Guid as app id if it is empty

* Code cleanup: moving Pwa related code from snapshotUtils to PwaHelper

* Code cleaning

* Code cleanup: Move common Application model to Csharp Library

* code cleanup

* modifying package name

* Ading project reference to Common.UI

* Code cleaning, fixing references

---------

Co-authored-by: donlaci <donlaci@yahoo.com>
2024-11-20 20:15:18 +01:00
桜井 ホタル
f66450f6a8 [PTRun][Docs] Add Bilibili to Third-Party plugins (#35926)
* Update thirdPartyRunPlugins.md

* Update names.txt
2024-11-20 09:25:35 -08:00
Ethan Fang
d31934b7de [DATA_AND_PRIVACY.md] updating the list of diagnostic data events (#35922)
* updating the list of diagnostic data events

* updating the list of events
2024-11-20 09:25:21 -08:00
Sanskar Gupta
4f3d7009a8 Simplify language and structure for clarity and ease of use. (#35943)
1. Clearer Section Headings: Renamed sections like "Indicating Interest in Issues" to improve readability.
2. Conciseness and Flow: Shortened sentences and rephrased for directness.
3. Improved Organization: Streamlined instructions in sections like “Filing an Issue” and “Contributing Fixes/Features.”
4. Reduced Redundancy: Simplified repetitive language, especially in localization and contribution details.
5. Enhanced Call to Action: Adjusted "Help Wanted" and "Becoming a Collaborator" to guide users clearly on next steps.
2024-11-20 09:24:04 -08:00
490 changed files with 17501 additions and 4774 deletions

14
.github/CODEOWNERS vendored
View File

@@ -1,16 +1,16 @@
# Protect `.github` folder except the spell-check rules inside it. (The exception happens by not defining any owner user or group for the path.) # Protect `.github` folder except the spell-check rules inside it. (The exception happens by not defining any owner user or group for the path.)
# Protection of the spell-check rules makes no sense as it needs to be changed in nearly every PR. # Protection of the spell-check rules makes no sense as it needs to be changed in nearly every PR.
/.github/ @crutkas @DHowett @ethanfangg /.github/ @microsoft/powertoys-code-owners
/.github/actions/spell-check/ /.github/actions/spell-check/
# locking down pipeline folder # locking down pipeline folder
/.pipelines/ @crutkas @DHowett @ethanfangg /.pipelines/ @microsoft/powertoys-code-owners
# locking down nuget config # locking down nuget config
nuget.config @crutkas @DHowett @ethanfangg nuget.config @microsoft/powertoys-code-owners
packages.config @crutkas @DHowett @ethanfangg packages.config @microsoft/powertoys-code-owners
# locking down files that should not change # locking down files that should not change
LICENSE @crutkas @DHowett @ethanfangg LICENSE @microsoft/powertoys-code-owners
SECURITY.md @crutkas @DHowett @ethanfangg SECURITY.md @microsoft/powertoys-code-owners
CODE_OF_CONDUCT.md @crutkas @DHowett @ethanfangg CODE_OF_CONDUCT.md @microsoft/powertoys-code-owners

View File

@@ -21,6 +21,9 @@ Pbgra
WHITEONBLACK WHITEONBLACK
# COUNTRIES
RUS
# FILES # FILES
AYUV AYUV
@@ -128,6 +131,9 @@ XBUTTONDOWN
XBUTTONUP XBUTTONUP
XDOWN XDOWN
# Prefix
pcs
# User32.SYSTEM_METRICS_INDEX.cs # User32.SYSTEM_METRICS_INDEX.cs
CLEANBOOT CLEANBOOT
@@ -220,9 +226,23 @@ TABLETPC
artanh artanh
arsinh arsinh
arcosh arcosh
roundf
# Linux # Linux
dbus dbus
anypass anypass
github
gpg gpg
https
ssh
ubuntu
workarounds
# For upgrade to check-spelling v0.0.24
pwa
# .NET
AOT
Aot

View File

@@ -44,6 +44,7 @@ Bartosz
betadele betadele
betsegaw betsegaw
bricelam bricelam
bsky
CCcat CCcat
Chinh Chinh
chrdavis chrdavis
@@ -53,6 +54,7 @@ Coplen
craigloewen craigloewen
crutkas crutkas
damienleroy damienleroy
daverayment
davidegiacometti davidegiacometti
debian debian
Deibisu Deibisu
@@ -64,6 +66,7 @@ Essey
ethanfangg ethanfangg
ferraridavide ferraridavide
frankychen frankychen
Gaarden
gaardmark gaardmark
gabime gabime
Galaxi Galaxi
@@ -71,6 +74,8 @@ Garside
Gershaft Gershaft
Giordani Giordani
Gokce Gokce
gordon
grzhan
Guo Guo
hanselman hanselman
Harmath Harmath
@@ -81,6 +86,8 @@ Horvalds
Howett Howett
htcfreek htcfreek
Huynh Huynh
Ionut
jamrobot
Jaswal Jaswal
jefflord jefflord
Jordi Jordi
@@ -89,11 +96,13 @@ Kairu
Kamra Kamra
Kantarci Kantarci
Karthick Karthick
kaylacinnamon
kevinguo kevinguo
Krigun Krigun
Lambson Lambson
Laute Laute
laviusmotileng laviusmotileng
Leilei
Luecking Luecking
Mahalingam Mahalingam
Markovic Markovic
@@ -109,6 +118,7 @@ nathancartlidge
Nemeth Nemeth
nielslaute nielslaute
oldnewthing oldnewthing
onegreatworld
palenshus palenshus
pedrolamas pedrolamas
peteblois peteblois
@@ -119,6 +129,7 @@ Pylyp
quachpas quachpas
Quriz Quriz
randyrants randyrants
rayment
ricardosantos ricardosantos
riri riri
ritchielawrence ritchielawrence
@@ -144,8 +155,10 @@ TBM
tilovell tilovell
Triet Triet
waaverecords waaverecords
Whuihuan
Xpg Xpg
ycv ycv
yeelam
Yuniardi Yuniardi
yuyoyuppe yuyoyuppe
Zeol Zeol
@@ -154,6 +167,8 @@ Zykova
# OTHERS # OTHERS
Bilibili
BVID
cmdow cmdow
Controlz Controlz
cortana cortana

View File

@@ -1,3 +1,6 @@
# D2D
#D?2D
# marker to ignore all code on line # marker to ignore all code on line
^.*/\* #no-spell-check-line \*/.*$ ^.*/\* #no-spell-check-line \*/.*$
# marker to ignore all code on line # marker to ignore all code on line
@@ -8,7 +11,7 @@
^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b ^.*\b[Cc][Ss][Pp][Ee][Ll]{2}:\s*[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-[Ll][Ii][Nn][Ee]\b
# patch hunk comments # patch hunk comments
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .* ^@@ -\d+(?:,\d+|) \+\d+(?:,\d+|) @@ .*
# git index header # git index header
index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40} index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
@@ -26,13 +29,13 @@ index (?:[0-9a-z]{7,40},|)[0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
# data url in quotes # data url in quotes
([`'"])data:(?:[^ `'"].*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1} ([`'"])data:(?:[^ `'"].*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
# data url # data url
data:[-a-zA-Z=;:/0-9+]*,\S* \bdata:[-a-zA-Z=;:/0-9+]*,\S*
# https/http/file urls # https/http/file urls
#(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|] #(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/*%?=~_|!:,.;]+[-A-Za-z0-9+&@#/*%=~_|]
# mailto urls # mailto urls
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,} #mailto:[-a-zA-Z=;:/?%&0-9+@._]{3,}
# magnet urls # magnet urls
magnet:[?=:\w]+ magnet:[?=:\w]+
@@ -152,6 +155,9 @@ themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
# GHSA # GHSA
GHSA(?:-[0-9a-z]{4}){3} GHSA(?:-[0-9a-z]{4}){3}
# GitHub actions
\buses:\s+[-\w.]+/[-\w./]+@[-\w.]+
# GitLab commit # GitLab commit
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b \bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
# GitLab merge requests # GitLab merge requests
@@ -210,7 +216,7 @@ accounts\.binance\.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
# medium link # medium link
\blink\.medium\.com/[a-zA-Z0-9]+ \blink\.medium\.com/[a-zA-Z0-9]+
# medium # medium
\bmedium\.com/\@?[^/\s"]+/[-\w]+ \bmedium\.com/@?[^/\s"]+/[-\w]+
# microsoft # microsoft
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]* \b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
@@ -275,7 +281,7 @@ slack://[a-zA-Z0-9?&=]+
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b [0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
# Twitter markdown # Twitter markdown
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\) \[@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
# Twitter hashtag # Twitter hashtag
\btwitter\.com/hashtag/[\w?_=&]* \btwitter\.com/hashtag/[\w?_=&]*
# Twitter status # Twitter status
@@ -330,7 +336,7 @@ ipfs://[0-9a-zA-Z]{3,}
[^"\s]+/gitweb/\S+;h=[0-9a-f]+ [^"\s]+/gitweb/\S+;h=[0-9a-f]+
# HyperKitty lists # HyperKitty lists
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/ /archives/list/[^@/]+@[^/\s"]*/message/[^/\s"]*/
# lists # lists
/thread\.html/[^"\s]+ /thread\.html/[^"\s]+
@@ -348,7 +354,7 @@ ipfs://[0-9a-zA-Z]{3,}
\bopen\.spotify\.com/embed/playlist/\w+ \bopen\.spotify\.com/embed/playlist/\w+
# Mastodon # Mastodon
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]* \bmastodon\.[-a-z.]*/(?:media/|@)[?&=0-9a-zA-Z_]*
# scastie # scastie
\bscastie\.scala-lang\.org/[^/]+/\w+ \bscastie\.scala-lang\.org/[^/]+/\w+
@@ -390,9 +396,9 @@ ipfs://[0-9a-zA-Z]{3,}
(?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m (?:\\(?:u00|x)1[Bb]|\x1b|\\u\{1[Bb]\})\[\d+(?:;\d+|)m
# URL escaped characters # URL escaped characters
\%[0-9A-F][A-F] %[0-9A-F][A-F](?=[A-Za-z])
# lower URL escaped characters # lower URL escaped characters
\%[0-9a-f][a-f](?=[a-z]{2,}) %[0-9a-f][a-f](?=[a-z]{2,})
# IPv6 # IPv6
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b \b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
# c99 hex digits (not the full format, just one I've seen) # c99 hex digits (not the full format, just one I've seen)
@@ -400,7 +406,7 @@ ipfs://[0-9a-zA-Z]{3,}
# Punycode # Punycode
\bxn--[-0-9a-z]+ \bxn--[-0-9a-z]+
# sha # sha
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]* sha\d+:[0-9a-f]*?[a-f]{3,}[0-9a-f]*
# sha-... -- uses a fancy capture # sha-... -- uses a fancy capture
(\\?['"]|&quot;)[0-9a-f]{40,}\g{-1} (\\?['"]|&quot;)[0-9a-f]{40,}\g{-1}
# hex runs # hex runs
@@ -420,10 +426,13 @@ sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
# pki # pki
-----BEGIN.*-----END -----BEGIN.*-----END
# pki (base64)
LS0tLS1CRUdJT.*
# uuid: # uuid:
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b \b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
# hex digits including css/html color classes: # hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b (?:[\\0][xX]|\\u|[uU]\+|#x?|%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
# integrity # integrity
integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1} integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1}
@@ -441,20 +450,47 @@ integrity=(['"])(?:\s*sha\d+-[-a-zA-Z=;:/0-9+]{40,})+\g{-1}
Name\[[^\]]+\]=.* Name\[[^\]]+\]=.*
# IServiceProvider / isAThing # IServiceProvider / isAThing
\b(?:I|isA)(?=(?:[A-Z][a-z]{2,})+\b) (?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
# crypt # crypt
(['"])\$2[ayb]\$.{56}\g{-1} (['"])\$2[ayb]\$.{56}\g{-1}
# apache/old crypt
(['"]|)\$+(?:apr|)1\$+.{8}\$+.{22}\g{-1}
# sha1 hash
\{SHA\}[-a-zA-Z=;:/0-9+]{3,}
# machine learning (?)
#\b(?i)ml(?=[a-z]{2,})
# python
#\b(?i)py(?!gments|gmy|lon|ramid|ro|th)(?=[a-z]{2,})
# scrypt / argon # scrypt / argon
\$(?:scrypt|argon\d+[di]*)\$\S+ \$(?:scrypt|argon\d+[di]*)\$\S+
# go.sum # go.sum
\bh1:\S+ \bh1:\S+
# scala imports
^import (?:[\w.]|\{\w*?(?:,\s*(?:\w*|\*))+\})+
# scala modules # scala modules
#("[^"]+"\s*%%?\s*){2,3}"[^"]+" #("[^"]+"\s*%%?\s*){2,3}"[^"]+"
# container images
image: [-\w./:@]+
# Docker images
^\s*FROM\s+\S+:\S+(?:\s+AS\s+\S+|)
# `docker images` REPOSITORY TAG IMAGE ID CREATED SIZE
\s*\S+/\S+\s+\S+\s+[0-9a-f]{8,}\s+\d+\s+(?:hour|day|week)s ago\s+[\d.]+[KMGT]B
# Intel intrinsics
_mm_(?!dd)\w+
# Input to GitHub JSON # Input to GitHub JSON
content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1} content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1}
@@ -462,34 +498,44 @@ content: (['"])[-a-zA-Z=;:/0-9+]*=\g{-1}
# you'll want to remove the `(?=.*?")` suffix. # you'll want to remove the `(?=.*?")` suffix.
# The `(?=.*?")` suffix should limit the false positives rate # The `(?=.*?")` suffix should limit the false positives rate
# printf # printf
#%(?:(?:(?:hh?|ll?|[jzt])?[diuoxn]|l?[cs]|L?[fega]|p)(?=[a-z]{2,})|(?:X|L?[FEGA]|p)(?=[a-zA-Z]{2,}))(?=[_a-zA-Z]+\b)(?!%)(?=.*?['"]) #%(?:(?:(?:hh?|ll?|[jzt])?[diuoxn]|l?[cs]|L?[fega]|p)(?=[a-z]{2,})|(?:X|L?[FEGA])(?=[a-zA-Z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%)\b)(?=.*?['"])
# Alternative printf
# %s
%(?:s(?=[a-z]{2,}))(?!%)(?=[_a-zA-Z]+(?!%[^s])\b)(?=.*?['"])
# Python string prefix / binary prefix # Python string prefix / binary prefix
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings # Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?=[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}) (?<!['"])\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)['"](?=[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
# Regular expressions for (P|p)assword # Regular expressions for (P|p)assword
\([A-Z]\|[a-z]\)[a-z]+ \([A-Z]\|[a-z]\)[a-z]+
# JavaScript regular expressions # JavaScript regular expressions
# javascript test regex # javascript test regex
/.*/[gim]*\.test\( /.{3,}/[gim]*\.test\(
# javascript match regex # javascript match regex
\.match\(/[^/\s"]*/[gim]*\s* \.match\(/[^/\s"]{3,}/[gim]*\s*
# javascript match regex # javascript match regex
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$) \.match\(/\\[b].{3,}?/[gim]*\s*\)(?:;|$)
# javascript regex # javascript regex
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$) ^\s*/\\[b].{3,}?/[gim]*\s*(?:\)(?:;|$)|,$)
# javascript replace regex # javascript replace regex
\.replace\(/[^/\s"]*/[gim]*\s*, \.replace\(/[^/\s"]{3,}/[gim]*\s*,
# assign regex # assign regex
= /[^*]*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/ = /[^*].*?(?:[a-z]{3,}|[A-Z]{3,}|[A-Z][a-z]{2,}).*/[gi]?(?=\W|$)
# perl regex test # perl regex test
[!=]~ (?:/.*/|m\{.*?\}|m<.*?>|m([|!/@#,;']).*?\g{-1}) [!=]~ (?:/.*/|m\{.*?\}|m<.*?>|m([|!/@#,;']).*?\g{-1})
# perl qr regex # perl qr regex
(?<!\$)\bqr(?:\{.*?\}|<.*?>|\(.*?\)|([|!/@#,;']).*?\g{-1}) (?<!\$)\bqr(?:\{.*?\}|<.*?>|\(.*?\)|([|!/@#,;']).*?\g{-1})
# perl run
perl(?:\s+-[a-zA-Z]\w*)+
# C network byte conversions
#(?:\d|\bh)to(?!ken)(?=[a-z])|to(?=[adhiklpun]\()
# Go regular expressions # Go regular expressions
regexp?\.MustCompile\(`[^`]*`\) regexp?\.MustCompile\(`[^`]*`\)
@@ -503,14 +549,20 @@ regexp?\.MustCompile\(`[^`]*`\)
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2} sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
# node packages # node packages
(["'])\@[^/'" ]+/[^/'" ]+\g{-1} (["'])@[^/'" ]+/[^/'" ]+\g{-1}
# go install # go install
go install(?:\s+[a-z]+\.[-@\w/.]+)+ go install(?:\s+[a-z]+\.[-@\w/.]+)+
# pom.xml
<(?:group|artifact)Id>.*?<
# jetbrains schema https://youtrack.jetbrains.com/issue/RSRP-489571 # jetbrains schema https://youtrack.jetbrains.com/issue/RSRP-489571
urn:shemas-jetbrains-com urn:shemas-jetbrains-com
# Debian changelog severity
[-\w]+ \(.*\) (?:\w+|baseline|unstable|experimental); urgency=(?:low|medium|high|emergency|critical)\b
# kubernetes pod status lists # kubernetes pod status lists
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase # https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+ \w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
@@ -518,9 +570,15 @@ urn:shemas-jetbrains-com
# kubectl - pods in CrashLoopBackOff # kubectl - pods in CrashLoopBackOff
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+ \w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
# kubernetes applications
\.apps/[-\w]+
# kubernetes object suffix # kubernetes object suffix
-[0-9a-f]{10}-\w{5}\s -[0-9a-f]{10}-\w{5}\s
# kubernetes crd patterns
^\s*pattern: .*$
# posthog secrets # posthog secrets
([`'"])phc_[^"',]+\g{-1} ([`'"])phc_[^"',]+\g{-1}
@@ -532,6 +590,9 @@ urn:shemas-jetbrains-com
# xcode api botches # xcode api botches
customObjectInstantitationMethod customObjectInstantitationMethod
# msvc api botches
PrependWithABINamepsace
# configure flags # configure flags
.* \| --\w{2,}.*?(?=\w+\s\w+) .* \| --\w{2,}.*?(?=\w+\s\w+)
@@ -539,21 +600,34 @@ customObjectInstantitationMethod
\.fa-[-a-z0-9]+ \.fa-[-a-z0-9]+
# bearer auth # bearer auth
(['"])Bear[e][r] .*?\g{-1} (['"])[Bb]ear[e][r] .*?\g{-1}
# bearer auth
\b[Bb]ear[e][r]:? [-a-zA-Z=;:/0-9+.]+
# basic auth # basic auth
(['"])Basic [-a-zA-Z=;:/0-9+]{3,}\g{-1} (['"])[Bb]asic [-a-zA-Z=;:/0-9+]{3,}\g{-1}
# base64 encoded content # base64 encoded content
([`'"])[-a-zA-Z=;:/0-9+]+=\g{-1} #([`'"])[-a-zA-Z=;:/0-9+]{3,}=\g{-1}
# base64 encoded content in xml/sgml # base64 encoded content in xml/sgml
>[-a-zA-Z=;:/0-9+]+=</ >[-a-zA-Z=;:/0-9+]{3,}=</
# base64 encoded content, possibly wrapped in mime # base64 encoded content, possibly wrapped in mime
(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$) (?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
# base64 encoded json
\beyJ[-a-zA-Z=;:/0-9+]+
# base64 encoded pkcs
\bMII[-a-zA-Z=;:/0-9+]+
# DNS rr data
(?:\d+\s+){3}(?:[-+/=.\w]{2,}\s*){1,2}
# encoded-word # encoded-word
=\?[-a-zA-Z0-9"*%]+\?[BQ]\?[^?]{0,75}\?= =\?[-a-zA-Z0-9"*%]+\?[BQ]\?[^?]{0,75}\?=
# numerator
\bnumer\b(?=.*denom)
# Time Zones # Time Zones
\b(?:Africa|Atlantic|America|Antarctica|Asia|Australia|Europe|Indian|Pacific)(?:/\w+)+ \b(?:Africa|Atlantic|America|Antarctica|Asia|Australia|Europe|Indian|Pacific)(?:/\w+)+
@@ -563,16 +637,22 @@ customObjectInstantitationMethod
# systemd mode # systemd mode
systemd.*?running in system mode \([-+].*\)$ systemd.*?running in system mode \([-+].*\)$
# Lorem
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally ) # Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review - # grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into: # Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary). # ... Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
## You could manually change `(?i)X...` to use `[Xx]...` # ... You could manually change `(?i)X...` to use `[Xx]...`
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path) # ... or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
# Lorem (?:(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*)
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
# Non-English # Non-English
# Even repositories expecting pure English content can unintentionally have Non-English content... People will occasionally mistakenly enter [homoglyphs](https://en.wikipedia.org/wiki/Homoglyph) which are essentially typos, and using this pattern will mean check-spelling will not complain about them.
#
# If the content to be checked should be written in English and the only Non-English items will be people's names, then you can consider adding this.
#
# Alternatively, if you're using check-spelling v0.0.25+, and you would like to _check_ the Non-English content for spelling errors, you can. For information on how to do so, see:
# https://docs.check-spelling.dev/Feature:-Configurable-word-characters.html#unicode
[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,} [a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,}
# highlighted letters # highlighted letters
@@ -585,27 +665,48 @@ systemd.*?running in system mode \([-+].*\)$
# latex (check-spelling >= 0.0.22) # latex (check-spelling >= 0.0.22)
\\\w{2,}\{ \\\w{2,}\{
# American Mathematical Society (AMS) / Doxygen
TeX/AMS
# File extensions
\*\.[+\w]+,
# eslint # eslint
"varsIgnorePattern": ".+" "varsIgnorePattern": ".+"
# nolint
nolint:\w+
# Windows short paths # Windows short paths
[/\\][^/\\]{5,6}~\d{1,2}[/\\] [/\\][^/\\]{5,6}~\d{1,2}(?=[/\\])
# Windows Resources with accelerators
\b[A-Z]&[a-z]+\b(?!;)
# cygwin paths
/cygdrive/[a-zA-Z]/(?:Program Files(?: \(.*?\)| ?)(?:/[-+.~\\/()\w ]+)*|[-+.~\\/()\w])+
# in check-spelling@v0.0.22+, printf markers aren't automatically consumed # in check-spelling@v0.0.22+, printf markers aren't automatically consumed
# printf markers # printf markers
#(?<!\\)\\[nrt](?=[a-z]{2,}) #(?<!\\)\\[nrt](?=[a-z]{2,})
# alternate markers if you run into latex and friends # alternate printf markers if you run into latex and friends
#(?<!\\)\\[nrt](?=[a-z]{2,})(?=.*['"`]) #(?<!\\)\\[nrt](?=[a-z]{2,})(?=.*['"`])
# Markdown anchor links
\(#\S*?[a-zA-Z]\S*?\)
# apache # apache
a2(?:en|dis) a2(?:en|dis)
# weak e-tag # weak e-tag
W/"[^"]+" W/"[^"]+"
# authors/credits
^\*(?: [A-Z](?:\w+|\.)){2,} (?=\[|$)
# the negative lookahead here is to allow catching 'templatesz' as a misspelling # the negative lookahead here is to allow catching 'templatesz' as a misspelling
# but to otherwise recognize a Windows path with \templates\foo.template or similar: # but to otherwise recognize a Windows path with \templates\foo.template or similar:
#\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z]) #\\(?:necessary|r(?:elease|eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
# ignore long runs of a single character: # ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b \b([A-Za-z])\g{-1}{3,}\b
@@ -624,13 +725,24 @@ W/"[^"]+"
# Compiler flags (linker) # Compiler flags (linker)
,-B ,-B
# libraries
(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
# WWNN/WWPN (NAA identifiers)
\b(?:0x)?10[0-9a-f]{14}\b|\b(?:0x|3)?[25][0-9a-f]{15}\b|\b(?:0x|3)?6[0-9a-f]{31}\b
# iSCSI iqn (approximate regex)
\biqn\.[0-9]{4}-[0-9]{2}(?:[\.-][a-z][a-z0-9]*)*\b
# curl arguments # curl arguments
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)* \b(?:\\n|)curl(?:\.exe|)(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
# set arguments # set arguments
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)* \b(?:bash|sh|set)(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
# tar arguments # tar arguments
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ \b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long... # tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b \btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
# macOS temp folders # macOS temp folders
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/ /var/folders/\w\w/[+\w]+/(?:T|-Caches-)/
# github runner temp folders
/home/runner/work/_temp/[-_/a-z0-9]+

View File

@@ -1,23 +1,19 @@
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes # See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
(?:^|/)(?i)COPYRIGHT (?:^|/)(?i)COPYRIGHT
(?:^|/)(?i)LICEN[CS]E (?:^|/)(?i)LICEN[CS]E
(?:^|/)(?i)third[-_]?party/
(?:^|/)3rdparty/ (?:^|/)3rdparty/
(?:^|/)src/Monaco/customLanguages/ (?:^|/)generated/
(?:^|/)src/Monaco/generateLanguagesJson.html
(?:^|/)src/Monaco/index.html
(?:^|/)src/Monaco/monaco_languages.json
(?:^|/)src/Monaco/monacoSpecialLanguages.js
(?:^|/)go\.sum$ (?:^|/)go\.sum$
(?:^|/)monacoSRC/
(?:^|/)package(?:-lock|)\.json$ (?:^|/)package(?:-lock|)\.json$
(?:^|/)Pipfile$ (?:^|/)Pipfile$
(?:^|/)power-rename-ui-flags$ (?:^|/)power-rename-ui-flags$
(?:^|/)pyproject.toml (?:^|/)pyproject.toml
(?:^|/)requirements(?:-dev|-doc|-test|)\.txt$
(?:^|/)sample\.qoi$ (?:^|/)sample\.qoi$
(?:^|/)timezones\.json$
(?:^|/)vendor/ (?:^|/)vendor/
(?:^|/)WindowsSettings\.json$ (?:^|/)WindowsSettings\.json$
(?:^|/|\b)requirements(?:-dev|-doc|-test|)\.txt$
(?:|$^ 92.31% - excluded 12/13)/editor/[^/]+$
/images/launcher/[^/]+$ /images/launcher/[^/]+$
/TestFiles/ /TestFiles/
[^/]\.gcode$ [^/]\.gcode$
@@ -28,11 +24,10 @@
\.avi$ \.avi$
\.bmp$ \.bmp$
\.bz2$ \.bz2$
\.cer$ \.cert?$|\.crt$
\.class$ \.class$
\.coveragerc$ \.coveragerc$
\.crl$ \.crl$
\.crt$
\.csr$ \.csr$
\.dll$ \.dll$
\.docx?$ \.docx?$
@@ -97,19 +92,19 @@
^\.gitmodules$ ^\.gitmodules$
^\Q.github/workflows/spelling2.yml\E$ ^\Q.github/workflows/spelling2.yml\E$
^\Q.pipelines/ESRPSigning_core.json\E$ ^\Q.pipelines/ESRPSigning_core.json\E$
^\Q.pipelines/sdl.gdnbaselines\E$ ^\Qdoc/devdocs/localization.md\E$
^\Qinstaller/PowerToysSetup/Settings.wxs\E$
^\Qsrc/Monaco/monaco_languages.json\E$
^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ ^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$
^\Qsrc/common/notifications/BackgroundActivatorDLL/cpp.hint\E$ ^\Qsrc/common/notifications/BackgroundActivatorDLL/cpp.hint\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Assets/ColorPicker/colorPicker.cur\E$ ^\Qsrc/modules/colorPicker/ColorPickerUI/Assets/ColorPicker/colorPicker.cur\E$
^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$ ^\Qsrc/modules/colorPicker/ColorPickerUI/Shaders/GridShader.cso\E$
^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$ ^\Qsrc/modules/MouseUtils/MouseJumpUI/MainForm.resx\E$
^\Qsrc/modules/MouseUtils/MouseJumpUI/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs\E$ ^\Qsrc/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmAbout.cs\E$ ^\Qsrc/modules/MouseWithoutBorders/App/Form/frmAbout.cs\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmInputCallback.resx\E$ ^\Qsrc/modules/MouseWithoutBorders/App/Form/frmInputCallback.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmLogon.resx\E$ ^\Qsrc/modules/MouseWithoutBorders/App/Form/frmLogon.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmMatrix.resx\E$ ^\Qsrc/modules/MouseWithoutBorders/App/Form/frmMatrix.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmMessage.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmMouseCursor.resx\E$
^\Qsrc/modules/MouseWithoutBorders/App/Form/frmScreen.resx\E$ ^\Qsrc/modules/MouseWithoutBorders/App/Form/frmScreen.resx\E$
^\Qsrc/modules/MouseWithoutBorders/ModuleInterface/generateSecurityDescriptor.h\E$ ^\Qsrc/modules/MouseWithoutBorders/ModuleInterface/generateSecurityDescriptor.h\E$
^\Qsrc/modules/peek/Peek.Common/NativeMethods.txt\E$ ^\Qsrc/modules/peek/Peek.Common/NativeMethods.txt\E$
@@ -122,6 +117,7 @@
^src/modules/MouseWithoutBorders/App/Form/.*\.Designer\.cs$ ^src/modules/MouseWithoutBorders/App/Form/.*\.Designer\.cs$
^src/modules/MouseWithoutBorders/App/Form/.*\.resx$ ^src/modules/MouseWithoutBorders/App/Form/.*\.resx$
^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$ ^src/modules/MouseWithoutBorders/App/Helper/.*\.resx$
^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag.txt$ ^src/modules/previewpane/UnitTests-MarkdownPreviewHandler/HelperFiles/MarkdownWithHTMLImageTag\.txt$
^src/Monaco/
^tools/Verification scripts/Check preview handler registration\.ps1$ ^tools/Verification scripts/Check preview handler registration\.ps1$
ignore$ ignore$

View File

@@ -1,6 +1,3 @@
# FALSE POSITIVES
## "PackagemanagerWrapper.cs" should be "PackageManagerWrapper.cs"
## NOTICE.MD > MOZILLA PUBLIC LICENSE v1.1
aaaa aaaa
abcdefghjkmnpqrstuvxyz abcdefghjkmnpqrstuvxyz
abgr abgr
@@ -22,12 +19,12 @@ ADDUNDORECORD
ADifferent ADifferent
adml adml
admx admx
advapi
advfirewall advfirewall
AFeature AFeature
AFFINETRANSFORM AFFINETRANSFORM
AFX AFX
AGGREGATABLE AGGREGATABLE
ahk
AHybrid AHybrid
akv akv
ALarger ALarger
@@ -56,9 +53,9 @@ APPBARDATA
appdata appdata
APPEXECLINK APPEXECLINK
Appium Appium
Applicationcan
APPLICATIONFRAMEHOST APPLICATIONFRAMEHOST
appmanifest appmanifest
appmodel
APPNAME APPNAME
appref appref
appsettings appsettings
@@ -69,11 +66,9 @@ AQS
ARandom ARandom
ARCHITEW ARCHITEW
ARemapped ARemapped
ari
ARPINSTALLLOCATION ARPINSTALLLOCATION
ARPPRODUCTICON ARPPRODUCTICON
ARRAYSIZE ARRAYSIZE
arw
asf asf
AShortcut AShortcut
ASingle ASingle
@@ -104,6 +99,7 @@ backtracer
bbwe bbwe
bck bck
BESTEFFORT BESTEFFORT
bezelled
bhid bhid
BIF BIF
bigbar bigbar
@@ -124,7 +120,6 @@ bmi
bms bms
BNumber BNumber
BODGY BODGY
BOKMAL
bootstrapper bootstrapper
BOOTSTRAPPERINSTALLFOLDER BOOTSTRAPPERINSTALLFOLDER
bostrot bostrot
@@ -133,6 +128,7 @@ boxmodel
BPBF BPBF
bpmf bpmf
bpp bpp
Breadcrumb
Browsable Browsable
BROWSEINFO BROWSEINFO
bsd bsd
@@ -151,6 +147,7 @@ CALG
callbackptr callbackptr
calpwstr calpwstr
Cangjie Cangjie
caniuse
CANRENAME CANRENAME
CAPTUREBLT CAPTUREBLT
CAPTURECHANGED CAPTURECHANGED
@@ -163,7 +160,6 @@ CCom
CContext CContext
CDeclaration CDeclaration
CDEF CDEF
cdpx
CElems CElems
CENTERALIGN CENTERALIGN
ceq ceq
@@ -179,7 +175,6 @@ cidl
cim cim
CImage CImage
cla cla
clangformat
CLASSDC CLASSDC
CLASSNOTAVAILABLE CLASSNOTAVAILABLE
clickable clickable
@@ -201,12 +196,10 @@ CMINVOKECOMMANDINFO
CMINVOKECOMMANDINFOEX CMINVOKECOMMANDINFOEX
CMock CMock
CMONITORS CMONITORS
cmpgt
cmph cmph
cne cne
CNF CNF
coclass coclass
codeofconduct
codereview codereview
Codespaces Codespaces
codicon codicon
@@ -216,7 +209,6 @@ colorformat
colorhistory colorhistory
colorhistorylimit colorhistorylimit
COLORKEY COLORKEY
comctl
comdef comdef
comdlg comdlg
comexp comexp
@@ -226,9 +218,6 @@ commctrl
commdlg commdlg
compmgmt compmgmt
COMPOSITIONFULL COMPOSITIONFULL
comsupp
comsuppw
comsuppwd
comutil comutil
CONFIGW CONFIGW
CONFLICTINGMODIFIERKEY CONFLICTINGMODIFIERKEY
@@ -248,7 +237,6 @@ COULDNOT
countof countof
cph cph
CPower CPower
cppblog
cppwinrt cppwinrt
createdump createdump
CREATESCHEDULEDTASK CREATESCHEDULEDTASK
@@ -259,13 +247,12 @@ CRH
critsec critsec
Crossdevice Crossdevice
CRSEL CRSEL
crw crx
CSearch CSearch
CSettings CSettings
cso cso
CSRW CSRW
CStyle CStyle
CSY
CTest CTest
currentculture currentculture
CURRENTDIR CURRENTDIR
@@ -276,7 +263,6 @@ CUSTOMACTIONTEST
CVal CVal
cvd cvd
CVirtual CVirtual
cvtepu
cxfksword cxfksword
CXSCREEN CXSCREEN
CXSMICON CXSMICON
@@ -292,7 +278,6 @@ datareader
datatracker datatracker
dataversion dataversion
Dayof Dayof
Dbghelp
DBLCLKS DBLCLKS
DBLEPSILON DBLEPSILON
DCapture DCapture
@@ -301,8 +286,8 @@ DCOM
dcommon dcommon
dcomp dcomp
DComposition DComposition
dcr DCR
dcs DCs
ddd ddd
DDEIf DDEIf
DDevice DDevice
@@ -340,10 +325,12 @@ devblogs
devdocs devdocs
devenum devenum
devmgmt devmgmt
DEVMODE
DEVMODEW DEVMODEW
DEVMON DEVMON
devpkey devpkey
DEVSOURCE DEVSOURCE
DGR
DIIRFLAG DIIRFLAG
dimm dimm
DISABLEASACTIONKEY DISABLEASACTIONKEY
@@ -372,12 +359,11 @@ DRAWCLIPBOARD
DRAWFRAME DRAWFRAME
drawingcolor drawingcolor
dreamsofameaningfullife dreamsofameaningfullife
drf
drivedetectionwarning drivedetectionwarning
dshow dshow
DSTINVERT DSTINVERT
DUMMYUNIONNAME DUMMYUNIONNAME
dutil Dutil
DVASPECT DVASPECT
DVASPECTINFO DVASPECTINFO
DVD DVD
@@ -406,22 +392,17 @@ dwrite
dxgi dxgi
dxgidebug dxgidebug
dxgiformat dxgiformat
dxguid
easeofaccess easeofaccess
ecount ecount
EData EData
Edid Edid
EDITKEYBOARD EDITKEYBOARD
editkeyboardwindow
EDITSHORTCUTS EDITSHORTCUTS
editshortcutswindow
EFile EFile
eip
ekus ekus
emmintrin emmintrin
Emoji Emoji
ENABLEDELAYEDEXPANSION ENABLEDELAYEDEXPANSION
enabledisable
ENABLEDPOPUP ENABLEDPOPUP
encodedlaunch encodedlaunch
encryptor encryptor
@@ -444,11 +425,9 @@ ESettings
esrp esrp
etl etl
etw etw
EUQ
eurochange eurochange
eventlog eventlog
eventvwr eventvwr
everytime
evntrace evntrace
evt evt
EWXFORCE EWXFORCE
@@ -521,6 +500,7 @@ FZE
gacutil gacutil
Gaeilge Gaeilge
Gaidhlig Gaidhlig
GC'ed
GCLP GCLP
gdi gdi
gdiplus gdiplus
@@ -549,12 +529,10 @@ GSM
gtm gtm
guiddata guiddata
guiddef guiddef
guidgenerator
GUITHREADINFO GUITHREADINFO
GValue GValue
gwl gwl
GWLP GWLP
handlekeyboardhookevent
hangeul hangeul
Hanzi Hanzi
Hardlines Hardlines
@@ -590,7 +568,7 @@ Hiberboot
HIBYTE HIBYTE
hicon hicon
HIDEWINDOW HIDEWINDOW
hif Hif
HIMAGELIST HIMAGELIST
himl himl
hinst hinst
@@ -608,6 +586,7 @@ hmenu
hmodule hmodule
hmonitor hmonitor
homljgmgpmcbpjbnjpfijnhipfkiclkd homljgmgpmcbpjbnjpfijnhipfkiclkd
HORZSIZE
Hostbackdropbrush Hostbackdropbrush
hotkeycontrol hotkeycontrol
hotkeys hotkeys
@@ -639,37 +618,26 @@ HWNDLAST
HWNDNEXT HWNDNEXT
HWNDPREV HWNDPREV
hyjiacan hyjiacan
IAI
IBeam IBeam
ICapture
IClass
ICONERROR ICONERROR
ICONLOCATION ICONLOCATION
IData
IDD IDD
IDesktop
IDirect
idl idl
idlist idlist
IDOn
IDR IDR
IDXGI IDXGI
IEnum
ietf ietf
IExec
IEXPLORE IEXPLORE
IFACEMETHOD IFACEMETHOD
IFACEMETHODIMP IFACEMETHODIMP
IFile IFile
IFilter
IGNOREUNKNOWN IGNOREUNKNOWN
IGraphics
iid iid
Iindex Iindex
iiq
IJson
Ijwhost Ijwhost
IKs IKs
ILogon iljxck
IMAGEHLP IMAGEHLP
IMAGERESIZERCONTEXTMENU IMAGERESIZERCONTEXTMENU
IMAGERESIZEREXT IMAGERESIZEREXT
@@ -681,7 +649,7 @@ inetcpl
Infobar Infobar
INFOEXAMPLE INFOEXAMPLE
Infotip Infotip
initguid INITGUID
inorder inorder
INPC INPC
inproc inproc
@@ -689,7 +657,6 @@ INPUTHARDWARE
INPUTKEYBOARD INPUTKEYBOARD
INPUTLANGCHANGED INPUTLANGCHANGED
INPUTMOUSE INPUTMOUSE
inputparser
INPUTSINK INPUTSINK
INPUTTYPE INPUTTYPE
INSTALLDESKTOPSHORTCUT INSTALLDESKTOPSHORTCUT
@@ -710,35 +677,26 @@ INTRESOURCE
INVALIDARG INVALIDARG
invalidoperatioexception invalidoperatioexception
ipcmanager ipcmanager
IPlugin
IPower
IPREVIEW IPREVIEW
ipreviewhandlervisualssetfont ipreviewhandlervisualssetfont
IProperty
IPublic
irprops irprops
isbi isbi
ISearch ISearch
ISettings ISettings
IShell
isocpp isocpp
iss iss
issecret issecret
ISSEPARATOR ISSEPARATOR
ITask
ith ith
ITHUMBNAIL ITHUMBNAIL
IUI IUI
IUnknown IUnknown
IWbem IUse
IWeb
IWIC IWIC
iwr iwr
IYUV IYUV
jfi
jfif jfif
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
jif
jjw jjw
jobject jobject
jpe jpe
@@ -747,7 +705,6 @@ Jsons
jsonval jsonval
junja junja
jxr jxr
kdc
keybd keybd
KEYBDDATA KEYBDDATA
KEYBDINPUT KEYBDINPUT
@@ -756,7 +713,6 @@ keyboardmanagercommon
KEYBOARDMANAGEREDITOR KEYBOARDMANAGEREDITOR
keyboardmanagerstate keyboardmanagerstate
keyboardmanagerui keyboardmanagerui
keydropdowncontrol
KEYEVENTF KEYEVENTF
KEYIMAGE KEYIMAGE
keynum keynum
@@ -768,24 +724,21 @@ kmph
Knownfolders Knownfolders
KSPROPERTY KSPROPERTY
Kybd Kybd
languagesjson
lastcodeanalysissucceeded lastcodeanalysissucceeded
Lastdevice Lastdevice
LASTEXITCODE LASTEXITCODE
LAYOUTRTL LAYOUTRTL
lcb
LCIDTo LCIDTo
lcl
Lclean Lclean
Ldone Ldone
ldx ldx
LEFTSCROLLBAR LEFTSCROLLBAR
lego
LError LError
LEVELID LEVELID
LExit LExit
lhwnd lhwnd
LIBID LIBID
licate
lindex lindex
LINKOVERLAY LINKOVERLAY
LINQTo LINQTo
@@ -862,7 +815,6 @@ MAKEINTRESOURCE
MAKEINTRESOURCEA MAKEINTRESOURCEA
MAKEINTRESOURCEW MAKEINTRESOURCEW
MAKELANGID MAKELANGID
makepri
manifestdependency manifestdependency
MAPPEDTOSAMEKEY MAPPEDTOSAMEKEY
MAPTOSAMESHORTCUT MAPTOSAMESHORTCUT
@@ -872,7 +824,6 @@ MAXIMIZEBOX
MAXSHORTCUTSIZE MAXSHORTCUTSIZE
maxversiontested maxversiontested
MBR MBR
mdc
MDICHILD MDICHILD
MDL MDL
mdtext mdtext
@@ -880,7 +831,6 @@ mdtxt
mdwn mdwn
MEDIASUBTYPE MEDIASUBTYPE
mediatype mediatype
mef
MENUITEMINFO MENUITEMINFO
MENUITEMINFOW MENUITEMINFOW
MERGECOPY MERGECOPY
@@ -892,13 +842,11 @@ mfc
mfidl mfidl
mfobjects mfobjects
mfplat mfplat
Mfsensorgroup
mftransform mftransform
Mgmt Mgmt
mic mic
midl midl
mii mii
MIIM
mindaro mindaro
Minimatch Minimatch
Minimizable Minimizable
@@ -938,7 +886,6 @@ mpmc
MRM MRM
MRT MRT
mru mru
mrw
msc msc
mscorlib mscorlib
msdata msdata
@@ -989,7 +936,6 @@ NCPAINT
NCRENDERING NCRENDERING
ndp ndp
NEEDDISPATCH NEEDDISPATCH
needinfo
netcoreapp netcoreapp
netcpl netcpl
netframework netframework
@@ -1003,10 +949,10 @@ newitem
newpath newpath
newplus newplus
NEWPLUSCONTEXTMENU NEWPLUSCONTEXTMENU
NEWPLUSSHELLEXTENSIONWIN
newrow newrow
newsgroups newsgroups
NIF NIF
NLD
NLog NLog
NLSTEXT NLSTEXT
NNN NNN
@@ -1061,7 +1007,6 @@ NOZORDER
NPH NPH
npmjs npmjs
NResize NResize
nrw
nsunt nsunt
NTAPI NTAPI
ntdll ntdll
@@ -1084,14 +1029,12 @@ oldpath
oldtheme oldtheme
oleaut oleaut
OLECHAR OLECHAR
onebranch
opencode opencode
OPENFILENAME OPENFILENAME
opensource opensource
openxmlformats openxmlformats
OPTIMIZEFORINVOKE OPTIMIZEFORINVOKE
ORAW ORAW
ori
ORPHANEDDIALOGTITLE ORPHANEDDIALOGTITLE
ORSCANS ORSCANS
oss oss
@@ -1133,6 +1076,7 @@ pcelt
pch pch
pchast pchast
PCIDLIST PCIDLIST
PCTSTR
PCWSTR PCWSTR
pdbs pdbs
pdisp pdisp
@@ -1141,7 +1085,6 @@ pdto
pdtobj pdtobj
pdw pdw
Peb Peb
pef
PElems PElems
Pels Pels
PERCEIVEDFLAG PERCEIVEDFLAG
@@ -1166,7 +1109,6 @@ pinvoke
pipename pipename
PKBDLLHOOKSTRUCT PKBDLLHOOKSTRUCT
plib plib
PLK
ploc ploc
ploca ploca
plocm plocm
@@ -1176,6 +1118,7 @@ pnid
Pnp Pnp
Popups Popups
POPUPWINDOW POPUPWINDOW
POSITIONITEM
POWERRENAMECONTEXTMENU POWERRENAMECONTEXTMENU
powerrenameinput powerrenameinput
POWERRENAMETEST POWERRENAMETEST
@@ -1212,7 +1155,6 @@ prgms
pri pri
PRINTCLIENT PRINTCLIENT
printmanagement printmanagement
privacystatement
prm prm
proactively proactively
PROCESSENTRY PROCESSENTRY
@@ -1227,7 +1169,6 @@ projectname
PROPBAG PROPBAG
PROPERTYKEY PROPERTYKEY
propkey propkey
propsys
PROPVARIANT PROPVARIANT
propvarutil propvarutil
prvpane prvpane
@@ -1237,7 +1178,6 @@ PSECURITY
psfgao psfgao
psfi psfi
PSMODULEPATH PSMODULEPATH
Psr
psrm psrm
psrree psrree
pstatstg pstatstg
@@ -1254,7 +1194,7 @@ PTOKEN
PToy PToy
ptstr ptstr
pui pui
pwa PWAs
pwcs pwcs
PWSTR PWSTR
pwsz pwsz
@@ -1270,12 +1210,12 @@ QUERYENDSESSION
QUERYOPEN QUERYOPEN
QUEUESYNC QUEUESYNC
QUNS QUNS
raf
RAII RAII
RAlt RAlt
Rasterize Rasterize
RAWINPUTDEVICE RAWINPUTDEVICE
RAWINPUTHEADER RAWINPUTHEADER
RAWMODE
RAWPATH RAWPATH
rbhid rbhid
rclsid rclsid
@@ -1320,7 +1260,6 @@ Renamer
reparented reparented
reparenting reparenting
reparse reparse
reportbug
reportfileaccesses reportfileaccesses
requery requery
requerying requerying
@@ -1332,7 +1271,6 @@ RESIZETOFIT
resmimetype resmimetype
RESOURCEID RESOURCEID
RESTORETOMAXIMIZED RESTORETOMAXIMIZED
restrictedcapabilities
restrictederrorinfo restrictederrorinfo
resultlist resultlist
RETURNONLYFSDIRS RETURNONLYFSDIRS
@@ -1359,7 +1297,6 @@ Rsp
rstringalnum rstringalnum
rstringalpha rstringalpha
rstringdigit rstringdigit
Rstrtmgr
RTB RTB
RTLREADING RTLREADING
ruleset ruleset
@@ -1368,14 +1305,11 @@ rundll
rungameid rungameid
RUNLEVEL RUNLEVEL
runtimeclass runtimeclass
runtimeobject
runtimepack runtimepack
runtimes runtimes
ruuid ruuid
rvm rvm
rwin rwin
rwl
rwz
sacl sacl
safeprojectname safeprojectname
SAMEKEYPREVIOUSLYMAPPED SAMEKEYPREVIOUSLYMAPPED
@@ -1390,14 +1324,13 @@ Scode
screensaver screensaver
screenshots screenshots
scrollviewer scrollviewer
sddl SDDL
SDKDDK SDKDDK
sdns sdns
searchterm searchterm
SEARCHUI SEARCHUI
secpol secpol
SENDCHANGE SENDCHANGE
sendinput
sendvirtualinput sendvirtualinput
serverside serverside
SETCONTEXT SETCONTEXT
@@ -1413,7 +1346,6 @@ settingsheader
settingshotkeycontrol settingshotkeycontrol
setvariable setvariable
SETWORKAREA SETWORKAREA
setzero
sfgao sfgao
SFGAOF SFGAOF
SHANDLE SHANDLE
@@ -1421,7 +1353,7 @@ sharpkeys
SHCNE SHCNE
SHCNF SHCNF
SHCONTF SHCONTF
shcore Shcore
shellapi shellapi
SHELLDETAILS SHELLDETAILS
SHELLDLL SHELLDLL
@@ -1442,7 +1374,6 @@ shmem
SHNAMEMAPPING SHNAMEMAPPING
shobjidl shobjidl
SHORTCUTATLEAST SHORTCUTATLEAST
shortcutcontrol
SHORTCUTMAXONEACTIONKEY SHORTCUTMAXONEACTIONKEY
SHORTCUTNOREPEATEDMODIFIER SHORTCUTNOREPEATEDMODIFIER
SHORTCUTONEACTIONKEY SHORTCUTONEACTIONKEY
@@ -1471,7 +1402,6 @@ sigdn
SIGNINGSCENARIO SIGNINGSCENARIO
Signtool Signtool
SINGLEKEY SINGLEKEY
singlekeyremapcontrol
sipolicy sipolicy
SIZEBOX SIZEBOX
Sizename Sizename
@@ -1480,6 +1410,7 @@ SIZENS
SIZENWSE SIZENWSE
sizeread sizeread
SIZEWE SIZEWE
SKEXP
SKIPOWNPROCESS SKIPOWNPROCESS
sku sku
SLGP SLGP
@@ -1509,7 +1440,6 @@ Srch
SRCINVERT SRCINVERT
SRCPAINT SRCPAINT
SResize SResize
srf
srme srme
srre srre
srw srw
@@ -1543,7 +1473,6 @@ STRINGIZE
stringtable stringtable
stringval stringval
Strm Strm
Strmiids
strret strret
strsafe strsafe
strutil strutil
@@ -1584,6 +1513,7 @@ SYSLIB
SYSMENU SYSMENU
SYSTEMAPPS SYSTEMAPPS
SYSTEMTIME SYSTEMTIME
tailwindcss
tapp tapp
TApplication TApplication
TApplied TApplied
@@ -1595,7 +1525,7 @@ TARGETHEADER
targetver targetver
taskkill taskkill
taskschd taskschd
tchar TCHAR
Tcollab Tcollab
tcs tcs
tcscpy tcscpy
@@ -1617,6 +1547,7 @@ THISCOMPONENT
THotkey THotkey
thumbcache thumbcache
TILEDWINDOW TILEDWINDOW
TILLSON
timedate timedate
timediff timediff
timeunion timeunion
@@ -1627,6 +1558,7 @@ tkconverters
TLayout TLayout
tlb tlb
tlbimp tlbimp
tlhelp
TMPVAR TMPVAR
TNP TNP
Toolhelp Toolhelp
@@ -1645,7 +1577,6 @@ traies
transicc transicc
TRAYMOUSEMESSAGE TRAYMOUSEMESSAGE
triaging triaging
TRK
trl trl
trx trx
tsa tsa
@@ -1680,7 +1611,7 @@ uniquifier
Uniquifies Uniquifies
unitconverter unitconverter
unittests unittests
unknwn Unknwn
UNLEN UNLEN
UNORM UNORM
unregistering unregistering
@@ -1698,14 +1629,13 @@ Usb
USEDEFAULT USEDEFAULT
USEFILEATTRIBUTES USEFILEATTRIBUTES
USERDATA USERDATA
Userenv
USESHOWWINDOW USESHOWWINDOW
USESTDHANDLES USESTDHANDLES
USRDLL USRDLL
UType UType
uuidv uuidv
uwp uwp
uxtheme Uxtheme
vabdq vabdq
validmodulename validmodulename
valuegenerator valuegenerator
@@ -1727,6 +1657,7 @@ VERBW
VERIFYCONTEXT VERIFYCONTEXT
verrsrc verrsrc
VERSIONINFO VERSIONINFO
VERTSIZE
VFT VFT
vget vget
vgetq vgetq
@@ -1768,8 +1699,6 @@ Vtbl
WANTMAPPINGHANDLE WANTMAPPINGHANDLE
WANTPALM WANTPALM
wbem wbem
Wbemidl
wbemuuid
WBounds WBounds
Wca Wca
wcautil wcautil
@@ -1787,9 +1716,9 @@ webcam
webpage webpage
websites websites
wekyb wekyb
Wevtapi
wgpocpl wgpocpl
WIC WIC
wifi
wil wil
winapi winapi
wincodec wincodec
@@ -1805,7 +1734,6 @@ WINDOWPOSCHANGED
WINDOWPOSCHANGING WINDOWPOSCHANGING
windowsapp windowsapp
WINDOWSBUILDNUMBER WINDOWSBUILDNUMBER
Windowscodecs
windowssearch windowssearch
windowssettings windowssettings
WINDOWSTYLES WINDOWSTYLES
@@ -1816,20 +1744,17 @@ WINEVENT
winevt winevt
winexe winexe
winforms winforms
winfx
winget winget
wingetcreate wingetcreate
Winhook Winhook
WINL WINL
winlogon winlogon
winmd winmd
winmm WINNT
winnt
winres winres
winrt winrt
winsdk winsdk
winsdkver winsdkver
winspool
winsta winsta
winternl winternl
WINTHRESHOLD WINTHRESHOLD
@@ -1856,7 +1781,6 @@ WNDCLASSEXW
WNDCLASSW WNDCLASSW
WNDPROC WNDPROC
wnode wnode
workarounds
WORKSPACESEDITOR WORKSPACESEDITOR
WORKSPACESLAUNCHER WORKSPACESLAUNCHER
WORKSPACESSNAPSHOTTOOL WORKSPACESSNAPSHOTTOOL
@@ -1896,7 +1820,6 @@ XElement
xfd xfd
XFile XFile
XIncrement XIncrement
XLoc
XNamespace XNamespace
Xoshiro Xoshiro
XPels XPels
@@ -1922,3 +1845,5 @@ zonable
zoneset zoneset
Zoneszonabletester Zoneszonabletester
zzz zzz

View File

@@ -1,119 +1,289 @@
# reject `m_data` as VxWorks defined it and that breaks things if it's used elsewhere # reject `m_data` as VxWorks defined it and that breaks things if it's used elsewhere
# see [fprime](https://github.com/nasa/fprime/commit/d589f0a25c59ea9a800d851ea84c2f5df02fb529) # see [fprime](https://github.com/nasa/fprime/commit/d589f0a25c59ea9a800d851ea84c2f5df02fb529)
# and [Qt](https://github.com/qtproject/qt-solutions/blame/fb7bc42bfcc578ff3fa3b9ca21a41e96eb37c1c7/qtscriptclassic/src/qscriptbuffer_p.h#L46) # and [Qt](https://github.com/qtproject/qt-solutions/blame/fb7bc42bfcc578ff3fa3b9ca21a41e96eb37c1c7/qtscriptclassic/src/qscriptbuffer_p.h#L46)
# \bm_data\b #\bm_data\b
# Were you debugging using a framework with `fit()`?
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test, # If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want # you might not want to check in code where you skip all the other tests.
# to use this:
#\bfit\( #\bfit\(
# s.b. anymore # Should be `HH:MM:SS`
\bHH:SS:MM\b
# Should be `86400` (seconds in a standard day)
\b84600\b(?:.*\bday\b)
# Should probably be `2006-01-02` (yyyy-mm-dd)
# Assuming that the time is being passed to https://go.dev/src/time/format.go
\b2006-02-01\b
# Should probably be `YYYYMMDD`
\b[Yy]{4}[Dd]{2}[Mm]{2}(?!.*[Yy]{4}[Dd]{2}[Mm]{2}).*$
# Should be `a priori` or `and prior`
(?i)(?<!posteriori)\sand priori\s
# Should only be one of `a`, `an`, or `the`
\b(?:(?:an?|the)\s+){2,}\b
# Should be `anymore`
\bany more[,.] \bany more[,.]
# s.b. GitHub # Should be `'`
(?<![&*.]|// |\btype )\bGithub\b(?![{)]) (?i)\b(?:(?:i|s?he|they|what|who|you)"ll|(?:are|ca|did|do|does|ha[ds]|have|is|should|were|wo|would)n"t|(?:s?he|let|that|there|what|where|who)"s|(?:i|they|we|what|who|you)"ve)\b
# s.b. GitLab # Should be `briefcase`
(?<![&*.]|// |\btype )\bGitlab\b(?![{)]) \bbrief-case\b
# s.b. JavaScript # Should be `can, not only ..., ... also...`
\bcan not only.*can also\b
# Should be `cannot` (or `can't`)
# See https://www.grammarly.com/blog/cannot-or-can-not/
# > Don't use `can not` when you mean `cannot`. The only time you're likely to see `can not` written as separate words is when the word `can` happens to precede some other phrase that happens to start with `not`.
# > `Can't` is a contraction of `cannot`, and it's best suited for informal writing.
# > In formal writing and where contractions are frowned upon, use `cannot`.
# > It is possible to write `can not`, but you generally find it only as part of some other construction, such as `not only . . . but also.`
# - if you encounter such a case, add a pattern for that case to patterns.txt.
\b[Cc]an not\b
# Do not use `(click) here` links
# For more information, see:
# * https://www.w3.org/QA/Tips/noClickHere
# * https://webaim.org/techniques/hypertext/link_text
# * https://granicus.com/blog/why-click-here-links-are-bad/
# * https://heyoka.medium.com/dont-use-click-here-f32f445d1021
(?i)(?:>|\[)(?:(?:click |)here|this(?=\]\([^\)]+:/)|link|(?:read |)more(?!</value))(?:</|\]\()
# Should be `equals` to `is equal to`
\bequals to\b
# Should be `GitHub`
(?<![&*.]|// |\b(?:from|import|type) )\bGithub\b(?![{()])
# Should be `GitLab`
(?<![&*.]|// |\b(?:from|import|type) )\bGitlab\b(?![{()])
# Should probably be `https://`...
# Markdown generally doesn't assume that links are to urls
\]\(www\.\w
# Should be `JavaScript`
\bJavascript\b \bJavascript\b
# s.b. macOS or Mac OS X or ... # Should be `macOS` or `Mac OS X` or ...
\bMacOS\b \bMacOS\b
# s.b. Microsoft # Should be `Microsoft`
\bMicroSoft\b \bMicroSoft\b
# s.b. TypeScript # Should be `OAuth`
(?:^|[^-/*$])[ '"]oAuth(?: [a-z]|\d+ |[^ a-zA-Z0-9:;_.()])
# Should be `RabbitMQ`
\bRabbitmq\b
# Should be `TypeScript`
\bTypescript\b \bTypescript\b
# s.b. another # Should be `another`
\ban[- ]other\b \ban[- ]other\b
# s.b. deprecation warning # Should be `case-(in)sensitive`
\bcase (?:in|)sensitive\b
# Should be `coinciding`
\bco-inciding\b
# Should be `deprecation warning(s)`
\b[Dd]epreciation [Ww]arnings?\b \b[Dd]epreciation [Ww]arnings?\b
# s.b. greater than # Should be `greater than`
\bgreater then\b \bgreater then\b
# s.b. in front of # Should be `has`
\b[Ii]t only have\b
# Should be `here-in`, `the`, `them`, `this`, `these` or reworded in some other way
\bthe here(?:\.|,| (?!and|defined))
# Should be `greater than`
\bhigher than\b
# Should be `ID`
#\bId\b
# Should be `in front of`
\bin from of\b \bin from of\b
# s.b. into # Should be `into`
# when not phrasal and when `in order to` would be wrong: # when not phrasal and when `in order to` would be wrong:
# https://thewritepractice.com/into-vs-in-to/ # https://thewritepractice.com/into-vs-in-to/
\sin to\s(?!if\b) \sin to\s(?!if\b)
# s.b. is obsolete # Should be `use`
\sin used by\b
# Should be `is obsolete`
\bis obsolescent\b \bis obsolescent\b
# s.b. it's or its # Should be `it's` or `its`
\bits['] \bits[']
# s.b. opt-in # Should be `its`
(?<!\sfor)\sopt in\s \bit's(?= own\b)
# s.b. less than # Should be `log in`
\blogin to the
# Should be `long-standing`
\blong standing\b
# Should probably be a person named `Nick` or the abbreviation `NIC`
\bNic\b
# Should be `not supposed`
\bsupposed not\b
# Should probably be `much more`
\bmore much\b
# Should be `perform its`
\bperform it's\b
# Should be `opt-in`
(?<!\scan|for)(?<!\sif)\sopt in\s
# Should be `less than`
\bless then\b \bless then\b
# s.b. one of # Should be `load balancer`
\bon of\b \b[Ll]oud balancer
# s.b. otherwise # Should be `moot`
\bmute point\b
# Should be `one of`
(?<!-)\bon of\b
# Should be `on the other hand`
\b(?i)on another hand\b
# Most people only have two hands. Reword.
\b(?i)on the third hand\b
# Should be `otherwise`
\bother[- ]wise\b \bother[- ]wise\b
# s.b. or (more|less) # Should be `or (more|less)`
\bore (?:more|less)\b \bore (?:more|less)\b
# s.b. nonexistent # Should be `rather than`
\brather then\b
# Should be `regardless, ...` or `regardless of (whether)`
\b[Rr]egardless if you\b
# Should be `no longer needed`
\bno more needed\b(?! than\b)
# Should be `did not exist`
\bwere not existent\b
# Should be `nonexistent`
\bnon existing\b \bnon existing\b
# Should be `nonexistent`
\b[Nn]o[nt][- ]existent\b \b[Nn]o[nt][- ]existent\b
# s.b. brief / details/ param / return / retval # Should be `@brief` / `@details` / `@param` / `@return` / `@retval`
(?:^\s*|(?:\*|//|/*)\s+`)[\\@](?:breif|(?:detail|detials)|(?:params(?!\.)|prama?)|ret(?:uns?)|retvl)\b (?:^\s*|(?:\*|//|/*)\s+`)[\\@](?:breif|(?:detail|detials)|(?:params(?!\.)|prama?)|ret(?:uns?)|retvl)\b
# s.b. preexisting # Should be `preexisting`
[Pp]re[- ]existing [Pp]re[- ]existing
# s.b. preempt # Should be `preempt`
[Pp]re[- ]empt\b [Pp]re[- ]empt\b
# s.b. preemptively # Should be `preemptively`
[Pp]re[- ]emptively [Pp]re[- ]emptively
# s.b. recently changed or recent changes # Should be `prepopulate`
[Pp]re[- ]populate
# Should be `prerequisite`
[Pp]re[- ]requisite
# Should be `recently changed` or `recent changes`
[Rr]ecent changed [Rr]ecent changed
# s.b. reentrancy # Should be `reentrancy`
[Rr]e[- ]entrancy [Rr]e[- ]entrancy
# s.b. reentrant # Should be `reentrant`
[Rr]e[- ]entrant [Rr]e[- ]entrant
# s.b. understand # Should be `strong suit`
\b(?:my|his|her|their) strong suite\b
# Should be `understand`
\bunder stand\b \bunder stand\b
# s.b. workarounds # Should be `URI` or `uri` unless it refers to a person named `Uri`
#(?<!\.)\bUri\b(?![(])
# Should be `it uses is`
/\bis uses is\b/
# Should be `uses it as`
(?:^|\. |and )uses is as (?!an?\b|follows|livestock|[^.]+\s+as\b)
# Should be `was`
\bhas been(?= removed in v?\d)
# Should be `where`
\bwere they are\b
# should be `vCenter`
\bV[Cc]enter\b
# Should be `VM`
\bVm\b
# Should be `workarounds`
#\bwork[- ]arounds\b #\bwork[- ]arounds\b
# s.b. workaround # Should be `workaround`
(?:(?:[Aa]|[Tt]he|ugly)\swork[- ]around\b|\swork[- ]around\s+for) (?:(?:[Aa]|[Tt]he|ugly)\swork[- ]around\b|\swork[- ]around\s+for)
# s.b. (coarse|fine)-grained # Should be `worst`
(?i)worse-case
# Should be `you are not` or reworded
\byour not\b
# Should be `(coarse|fine)-grained`
\b(?:coarse|fine) grained\b \b(?:coarse|fine) grained\b
# s.b. neither/nor -- or reword # Homoglyph (Cyrillic) should be `A`/`B`/`C`/`E`/`H`/`I`/`I`/`J`/`K`/`M`/`O`/`P`/`S`/`T`/`Y`
#\bnot\b[^.?!"/(]+\bnor\b # It's possible that your content is intentionally mixing Cyrllic and Latin scripts, but if it isn't, you definitely want to correct this.
(?<=[A-Z]{2})[АВСЕНІӀЈКМОРЅТУ]|[АВСЕНІӀЈКМОРЅТУ](?=[A-Z]+(?:\b|[a-z]+)|[a-z]+(?:[^a-z]|$))
# probably a double negative # Homoglyph (Cyrillic) should be `a`/`b`/`e`
# s.b. neither/nor (plus rewording the beginning) # It's possible that your content is intentionally mixing Cyrllic and Latin scripts, but if it isn't, you definitely want to correct this.
\bnot\b[^.?!"/]*\bneither\b[^.?!"/(]*\bnor\b [аве](?=[A-Za-z]{2,})|(?<=[A-Za-z]{2})[аве]|(?<=[A-Za-z])[аве](?=[A-Za-z])
# In English, it is generally wrong to have the same word twice in a row without punctuation. # Should be `neither/nor` -- or reword
# Duplicated words are generally mistakes. #(?!<do )\bnot\b([^.?!"/(](?!neither|,.*?,))+\bnor\b
# There are a few exceptions where it is acceptable (e.g. "that that").
# If the highlighted doubled word pair is in a code snippet, you can write a pattern to mask it. # Should be `neither/nor` (plus rewording the beginning)
# If the highlighted doubled word pair is in prose, have someone read the English before you dismiss this error. # This is probably a double negative...
\bnot\b[^.?!"/(]*\bneither\b[^.?!"/(]*\bnor\b
# In English, duplicated words are generally mistakes
# There are a few exceptions (e.g. "that that").
# If the highlighted doubled word pair is in:
# * code, write a pattern to mask it.
# * prose, have someone read the English before you dismiss this error.
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s \s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s

View File

@@ -1,41 +1,22 @@
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns # See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
# Automatically suggested patterns # #includes
# hit-count: 3011 file-count: 842 ^\s*#include\s*(?:<.*?>|".*?")
# IServiceProvider / isAThing
\b(?:I|isA)(?=(?:[A-Z][a-z]{2,})+\b)
# hit-count: 2239 file-count: 134 # #pragma lib
# hex runs ^\s*#pragma comment\(lib, ".*?"\)
\b[0-9a-fA-F]{16,}\b
# hit-count: 1868 file-count: 1 # languageHashTable
# sha-... -- uses a fancy capture "\w+(?:-\w+|)"\s+=\s+@\(".*"\)
(\\?['"]|&quot;)[0-9a-f]{40,}\g{-1}
# hit-count: 1100 file-count: 97 # wikipedia
# base64 encoded content, possibly wrapped in mime \b\w\w\.wikipedia\.org/wiki/[-\w%.#]+
(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
# hit-count: 426 file-count: 165 # css fonts
# GitHub SHAs (markdown) \bfont-family:[^;}]+
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
# hit-count: 331 file-count: 117 # .github/policies/resourceManagement.yml
# hex digits including css/html color classes: pattern: '.*'
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
# hit-count: 275 file-count: 45
# version suffix <word>v#
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
# hit-count: 209 file-count: 97
# w3
\bw3\.org/[-0-9a-zA-Z/#.]+
# hit-count: 137 file-count: 38
# alternate markers if you run into latex and friends
(?<!\\)\\[nrt](?=[a-z]{2,})(?=.*['"`](?:, "[^{]|\]))
# tabs in c# # tabs in c#
\$"\\t \$"\\t
@@ -46,81 +27,6 @@
# windows line breaks in strings # windows line breaks in strings
\\r\\n \\r\\n
# hit-count: 104 file-count: 43
# regex choice
\(\?:[^)]+\|[^)]+\)
# hit-count: 76 file-count: 28
# base64 encoded content
([`'"])[-a-zA-Z=;:/0-9+]+=\g{-1}
# hit-count: 70 file-count: 5
# Contributor
\[[^\]]+\]\(https://github\.com/[^/\s"]+/?\)
# hit-count: 28 file-count: 22
# stackexchange -- https://stackexchange.com/feeds/sites
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
# hit-count: 21 file-count: 2
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
## You could manually change `(?i)X...` to use `[Xx]...`
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
# Lorem
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
# hit-count: 18 file-count: 15
# microsoft
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
# hit-count: 14 file-count: 5
# githubusercontent
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
# hit-count: 14 file-count: 3
# node packages
(["'])\@[^/'" ]+/[^/'" ]+\g{-1}
# hit-count: 10 file-count: 4
# URL escaped characters
\%[0-9A-F][A-F]
# hit-count: 9 file-count: 5
# Wikipedia
\ben\.wikipedia\.org/wiki/[-\w%.#]+
# hit-count: 6 file-count: 3
# css url wrappings
\burl\([^)]+\)
# hit-count: 5 file-count: 3
# vs devops
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
# hit-count: 4 file-count: 3
# Non-English
[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,}
# hit-count: 4 file-count: 2
# data url in quotes
([`'"])data:(?:[^ `'"].*?|)(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
# hit-count: 2 file-count: 2
# mailto urls
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
# msdn
\b(?:download\.visualstudio|docs|msdn|learn)\.microsoft\.com/[-_a-zA-Z0-9()=./]*
aka\.ms/[a-zA-Z0-9]+
# hit-count: 2 file-count: 1
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
# YouTube url
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
# power shell gallery website # power shell gallery website
\bpowershellgallery.com/[-_a-zA-Z0-9()=./%]* \bpowershellgallery.com/[-_a-zA-Z0-9()=./%]*
@@ -136,48 +42,183 @@ L?(["']|[-<({>]|\b)[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{10,12}(?:\g{
# UnitTests # UnitTests
\[DataRow\(.*\)\] \[DataRow\(.*\)\]
# AdditionalDependencies
<AdditionalDependencies>.*<
# the last line of mimetype="application/x-microsoft.net.object.bytearray.base64" things in .resx files
^\s*[-a-zA-Z=;:/0-9+]*[-a-zA-Z;:/0-9+][-a-zA-Z=;:/0-9+]*=$
# Automatically suggested patterns
# hit-count: 3715 file-count: 992
# IServiceProvider / isAThing
(?:\b|_)(?:(?:ns|)I|isA)(?=(?:[A-Z][a-z]{2,})+(?:[A-Z\d]|\b))
# hit-count: 404 file-count: 42
# base64 encoded content, possibly wrapped in mime
(?:^|[\s=;:?])[-a-zA-Z=;:/0-9+]{50,}(?:[\s=;:?]|$)
# hit-count: 402 file-count: 160
# hex runs
\b[0-9a-fA-F]{16,}\b
# hit-count: 337 file-count: 110
# hex digits including css/html color classes:
(?:[\\0][xX]|\\u|[uU]\+|#x?|%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|[iu]\d+)\b
# hit-count: 311 file-count: 43
# D2D # D2D
D?2D D?2D(?!efault)
# hit-count: 272 file-count: 75
# GitHub SHAs (markdown)
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
# hit-count: 146 file-count: 27
# version suffix <word>v#
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
# hit-count: 105 file-count: 103
# w3
\bw3\.org/[-0-9a-zA-Z/#.]+
# hit-count: 94 file-count: 6
# Contributor
\[[^\]]+\]\(https://github\.com/[^/\s"]+/?\)
RegExp\(([`'"]).*?\g{-1}\)|(?:escapes|regEx):\s*(?:/.*/|([`'"]).*?\g{-1})|return/.*?/
# hit-count: 65 file-count: 38
# regex choice
\(\?:[^)]+\|[^)]+\)
# hit-count: 37 file-count: 14
# Markdown anchor links
\(#\S*?[a-zA-Z]\S*?\)
# hit-count: 33 file-count: 5
# base64 encoded pkcs
\bMII[-a-zA-Z=;:/0-9+]+
# hit-count: 28 file-count: 22
# stackexchange -- https://stackexchange.com/feeds/sites
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
# hit-count: 14 file-count: 3
# node packages
(["'])@[^/'" ]+/[^/'" ]+\g{-1}
# hit-count: 13 file-count: 1
# Intel intrinsics
_mm_(?!dd)\w+
# hit-count: 11 file-count: 5
# URL escaped characters
%[0-9A-F][A-F](?=[A-Za-z])
# hit-count: 9 file-count: 5
# Wikipedia
\ben\.wikipedia\.org/wiki/[-\w%.#]+
# hit-count: 5 file-count: 4
# vs devops
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
# hit-count: 5 file-count: 4
# Alternatively, if you're using check-spelling v0.0.25+, and you would like to _check_ the Non-English content for spelling errors, you can. For information on how to do so, see:
# https://docs.check-spelling.dev/Feature:-Configurable-word-characters.html#unicode
[a-zA-Z]*[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*|[a-zA-Z]{3,}[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]|[ÀÁÂÃÄÅÆČÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæčçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3,}
# hit-count: 4 file-count: 4
# microsoft
\b(?:https?://|)(?:(?:(?:blogs|download\.visualstudio|developer|docs|msdn2?|research)\.|)microsoft|blogs\.msdn)\.co(?:m|\.\w\w)/[-_a-zA-Z0-9()=./%]*
aka\.ms/[a-zA-Z0-9]+
# hit-count: 3 file-count: 3
# githubusercontent
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
# hit-count: 3 file-count: 2
# css url wrappings
\burl\([^)]+\)
# hit-count: 3 file-count: 1
# kubernetes crd patterns
^\s*pattern: .*$
# hit-count: 3 file-count: 1
# Lorem
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
# ... Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
# ... You could manually change `(?i)X...` to use `[Xx]...`
# ... or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
(?:(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*)
# hit-count: 3 file-count: 1
# libraries
(?:\b|_)lib(?:re(?=office)|)(?!era[lt]|ero|erty|rar(?:i(?:an|es)|y))(?=[a-z])
# hit-count: 2 file-count: 1
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
# YouTube url
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
# hit-count: 1 file-count: 1 # hit-count: 1 file-count: 1
# GHSA # GHSA
GHSA(?:-[0-9a-z]{4}){3} GHSA(?:-[0-9a-z]{4}){3}
# hit-count: 1 file-count: 1 # hit-count: 1 file-count: 1
# medium # GitHub actions
\bmedium\.com/\@?[^/\s"]+/[-\w]+ \buses:\s+[-\w.]+/[-\w./]+@[-\w.]+
# hit-count: 1 file-count: 1 # hit-count: 1 file-count: 1
# kubectl.kubernetes.io/last-applied-configuration # medium
"kubectl.kubernetes.io/last-applied-configuration": ".*" \bmedium\.com/@?[^/\s"]+/[-\w]+
# hit-count: 1 file-count: 1
# sha-... -- uses a fancy capture
(\\?['"]|&quot;)[0-9a-f]{40,}\g{-1}
# hit-count: 1 file-count: 1 # hit-count: 1 file-count: 1
# tar arguments # tar arguments
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+ \b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
\bSecur32
# Questionably acceptable forms of `in to` # Questionably acceptable forms of `in to`
# Personally, I prefer `log into`, but people object # Personally, I prefer `log into`, but people object
# https://www.tprteaching.com/log-into-log-in-to-login/ # https://www.tprteaching.com/log-into-log-in-to-login/
\b(?:[Ll]og|[Ss]ign) in to\b \b(?:(?:[Ll]og(?:g(?=[a-z])|)|[Ss]ign)(?:ed|ing)?) in to\b
# to opt in # to opt in
\bto opt in\b \bto opt in\b
# acceptable duplicates # acceptable duplicates
# ls directory listings # ls directory listings
[-bcdlpsw](?:[-r][-w][-Ssx]){3}\s+\d+\s+\S+\s+\S+\s+\d+\s+ [-bcdlpsw](?:[-r][-w][-SsTtx]){3}[\.+*]?\s+\d+\s+\S+\s+\S+\s+[.\d]+(?:[KMGT]|)\s+
# mount # mount
\bmount\s+-t\s+(\w+)\s+\g{-1}\b \bmount\s+-t\s+(\w+)\s+\g{-1}\b
# C types and repeated CSS values # C types and repeated CSS values
\s(auto|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?: \g{-1})+\s \s(auto|buffalo|center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?: \g{-1})+\s
# C struct # C enum and struct
\bstruct\s+(\w+)\s+\g{-1}\b \b(?:enum|struct)\s+(\w+)\s+\g{-1}\b
# go templates # go templates
\s(\w+)\s+\g{-1}\s+\`(?:graphql|inject|json|yaml): \s(\w+)\s+\g{-1}\s+\`(?:graphql|inject|json|yaml):
# doxygen / javadoc / .net # doxygen / javadoc / .net
(?:[\\@](?:brief|groupname|t?param|return|retval)|(?:public|private|\[Parameter(?:\(.+\)|)\])(?:\s+static|\s+override|\s+readonly)*)(?:\s+\{\w+\}|)\s+(\w+)\s+\g{-1}\s (?:[\\@](?:brief|defgroup|groupname|link|t?param|return|retval)|(?:public|private|\[Parameter(?:\(.+\)|)\])(?:\s+(?:static|override|readonly|required|virtual))*)(?:\s+\{\w+\}|)\s+(\w+)\s+\g{-1}\s
# macOS file path
(?:Contents\W+|(?!iOS)/)MacOS\b
# Python package registry has incorrect spelling for macOS / Mac OS X
"Operating System :: MacOS :: MacOS X"
# "company" in Germany
\bGmbH\b
# IntelliJ
\bIntelliJ\b
# Commit message -- Signed-off-by and friends # Commit message -- Signed-off-by and friends
^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$ ^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$

View File

@@ -3,9 +3,11 @@
benefitting benefitting
occurences? occurences?
^dependan.* ^dependan.*
^diables?$
^oer$ ^oer$
Sorce Sorce
^[Ss]pae.* ^[Ss]pae.*
^Teh$
^untill$ ^untill$
^untilling$ ^untilling$
^wether.* ^wether.*

View File

@@ -34,14 +34,14 @@ name: Spell checking
# #
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key # For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
# Sarif reporting # SARIF reporting
# #
# Access to Sarif reports is generally restricted (by GitHub) to members of the repository. # Access to SARIF reports is generally restricted (by GitHub) to members of the repository.
# #
# Requires enabling `security-events: write` # Requires enabling `security-events: write`
# and configuring the action with `use_sarif: 1` # and configuring the action with `use_sarif: 1`
# #
# For information on the feature, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Sarif-output # For information on the feature, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-SARIF-output
# Minimal workflow structure: # Minimal workflow structure:
# #
@@ -60,23 +60,23 @@ name: Spell checking
on: on:
push: push:
branches: branches:
- "**" - "**"
tags-ignore: tags-ignore:
- "**" - "**"
pull_request_target: pull_request_target:
branches: branches:
- "**" - "**"
types: types:
- 'opened' - "opened"
- 'reopened' - "reopened"
- 'synchronize' - "synchronize"
issue_comment: issue_comment:
types: types:
- 'created' - "created"
jobs: jobs:
spelling: spelling:
name: Spell checking name: Check Spelling
permissions: permissions:
contents: read contents: read
pull-requests: read pull-requests: read
@@ -91,52 +91,59 @@ jobs:
# note: If you use only_check_changed_files, you do not want cancel-in-progress # note: If you use only_check_changed_files, you do not want cancel-in-progress
cancel-in-progress: true cancel-in-progress: true
steps: steps:
- name: check-spelling - name: check-spelling
id: spelling id: spelling
uses: check-spelling/check-spelling@v0.0.22 uses: check-spelling/check-spelling@v0.0.24
with: with:
config: .github/actions/spell-check config: .github/actions/spell-check
suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }} suppress_push_for_open_pull_request: ${{ github.actor != 'dependabot[bot]' && 1 }}
checkout: true checkout: true
check_file_names: 1 check_file_names: 1
spell_check_this: microsoft/PowerToys@main spell_check_this: microsoft/PowerToys@main
post_comment: 0 post_comment: 0
use_magic_file: 1 use_magic_file: 1
warnings: bad-regex,binary-file,deprecated-feature,ignored-expect-variant,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check report-timing: 1
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} warnings: bad-regex,binary-file,deprecated-feature,ignored-expect-variant,large-file,limited-references,no-newline-at-eof,noisy-file,non-alpha-in-dictionary,token-is-substring,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,no-files-to-check,unclosed-block-ignore-begin,unclosed-block-ignore-end
use_sarif: ${{ (!github.event.pull_request || (github.event.pull_request.head.repo.full_name == github.repository)) && 1 }} experimental_apply_changes_via_bot: 1
extra_dictionary_limit: 20 use_sarif: ${{ (!github.event.pull_request || (github.event.pull_request.head.repo.full_name == github.repository)) && 1 }}
extra_dictionaries: check_extra_dictionaries: ""
cspell:aws/aws.txt dictionary_source_prefixes: >
cspell:cpp/src/compiler-clang-attributes.txt {
cspell:cpp/src/compiler-msvc.txt "cspell": "https://raw.githubusercontent.com/check-spelling/cspell-dicts/v20241114/dictionaries/"
cspell:cpp/src/lang-keywords.txt }
cspell:cpp/src/stdlib-c.txt extra_dictionaries: |
cspell:cpp/src/stdlib-cmath.txt cspell:software-terms/softwareTerms.txt
cspell:cpp/src/stdlib-cpp.txt cspell:cpp/stdlib-cpp.txt
cspell:csharp/csharp.txt cspell:filetypes/filetypes.txt
cspell:css/dict/css.txt cspell:cpp/stdlib-c.txt
cspell:django/dict/django.txt cspell:lorem-ipsum/dictionary.txt
cspell:dotnet/dict/dotnet.txt cspell:python/python/python-lib.txt
cspell:filetypes/filetypes.txt cspell:php/php.txt
cspell:fullstack/dict/fullstack.txt cspell:fullstack/fullstack.txt
cspell:golang/dict/go.txt cspell:dotnet/dotnet.txt
cspell:html/dict/html.txt cspell:swift/swift.txt
cspell:java/src/java.txt cspell:node/node.txt
cspell:java/src/java-terms.txt cspell:dart/dart.txt
cspell:k8s/dict/k8s.txt cspell:django/django.txt
cspell:lorem-ipsum/dictionary.txt cspell:python/python/python.txt
cspell:monkeyc/src/monkeyc_keywords.txt cspell:powershell/powershell.txt
cspell:node/dict/node.txt cspell:npm/npm.txt
cspell:php/dict/php.txt cspell:golang/go.txt
cspell:powershell/dict/powershell.txt cspell:cpp/compiler-msvc.txt
cspell:python/src/common/extra.txt cspell:csharp/csharp.txt
cspell:python/src/python/python.txt cspell:html/html.txt
cspell:python/src/python/python-lib.txt cspell:java/java.txt
cspell:scala/dict/scala.txt cspell:aws/aws.txt
cspell:software-terms/dict/softwareTerms.txt cspell:typescript/typescript.txt
cspell:swift/src/swift.txt cspell:cpp/lang-keywords.txt
cspell:typescript/dict/typescript.txt cspell:python/common/extra.txt
cspell:scala/scala.txt
cspell:shell/shell-all-words.txt
cspell:css/css.txt
cspell:r/r.txt
cspell:java/java-terms.txt
cspell:cpp/stdlib-cerrno.txt
cspell:k8s/k8s.txt
comment-push: comment-push:
name: Report (Push) name: Report (Push)
@@ -144,16 +151,17 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: spelling needs: spelling
permissions: permissions:
actions: read
contents: write contents: write
if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push' if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
steps: steps:
- name: comment - name: comment
uses: check-spelling/check-spelling@v0.0.22 uses: check-spelling/check-spelling@v0.0.24
with: with:
config: .github/actions/spell-check config: .github/actions/spell-check
checkout: true checkout: true
spell_check_this: microsoft/PowerToys@main spell_check_this: microsoft/PowerToys@main
task: ${{ needs.spelling.outputs.followup }} task: ${{ needs.spelling.outputs.followup }}
comment-pr: comment-pr:
name: Report (PR) name: Report (PR)
@@ -161,18 +169,19 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: spelling needs: spelling
permissions: permissions:
actions: read
contents: read contents: read
pull-requests: write pull-requests: write
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request') if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
steps: steps:
- name: comment - name: comment
uses: check-spelling/check-spelling@v0.0.22 uses: check-spelling/check-spelling@v0.0.24
with: with:
config: .github/actions/spell-check config: .github/actions/spell-check
checkout: true checkout: true
spell_check_this: check-spelling/spell-check-this@prerelease spell_check_this: check-spelling/spell-check-this@prerelease
task: ${{ needs.spelling.outputs.followup }} task: ${{ needs.spelling.outputs.followup }}
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
update: update:
name: Update PR name: Update PR
@@ -182,18 +191,19 @@ jobs:
actions: read actions: read
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ if: ${{
github.repository_owner != 'microsoft' && github.repository_owner != 'microsoft' &&
github.event_name == 'issue_comment' && github.event_name == 'issue_comment' &&
github.event.issue.pull_request && github.event.issue.pull_request &&
contains(github.event.comment.body, '@check-spelling-bot apply') contains(github.event.comment.body, '@check-spelling-bot apply') &&
contains(github.event.comment.body, 'https://')
}} }}
concurrency: concurrency:
group: spelling-update-${{ github.event.issue.number }} group: spelling-update-${{ github.event.issue.number }}
cancel-in-progress: false cancel-in-progress: false
steps: steps:
- name: apply spelling updates - name: apply spelling updates
uses: check-spelling/check-spelling@v0.0.22 uses: check-spelling/check-spelling@v0.0.24
with: with:
experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }} experimental_apply_changes_via_bot: ${{ github.repository_owner != 'microsoft' && 1 }}
checkout: true checkout: true
ssh_key: "${{ secrets.CHECK_SPELLING }}" ssh_key: "${{ secrets.CHECK_SPELLING }}"

View File

@@ -181,6 +181,7 @@
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll", "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll",
"WinUI3Apps\\NewPlusPackage.msix", "WinUI3Apps\\NewPlusPackage.msix",
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll",
"PowerAccent.Core.dll", "PowerAccent.Core.dll",
"PowerToys.PowerAccent.dll", "PowerToys.PowerAccent.dll",
@@ -201,6 +202,7 @@
"PowerToys.WorkspacesLauncherUI.exe", "PowerToys.WorkspacesLauncherUI.exe",
"PowerToys.WorkspacesLauncherUI.dll", "PowerToys.WorkspacesLauncherUI.dll",
"PowerToys.WorkspacesModuleInterface.dll", "PowerToys.WorkspacesModuleInterface.dll",
"PowerToys.WorkspacesCsharpLibrary.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewExt.dll", "WinUI3Apps\\PowerToys.RegistryPreviewExt.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll", "WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll",
@@ -325,6 +327,7 @@
"WinUI3Apps\\TestableIO.System.IO.Abstractions.dll", "WinUI3Apps\\TestableIO.System.IO.Abstractions.dll",
"TestableIO.System.IO.Abstractions.Wrappers.dll", "TestableIO.System.IO.Abstractions.Wrappers.dll",
"WinUI3Apps\\TestableIO.System.IO.Abstractions.Wrappers.dll", "WinUI3Apps\\TestableIO.System.IO.Abstractions.Wrappers.dll",
"WinUI3Apps\\OpenAI.dll",
"ColorCode.Core.dll", "ColorCode.Core.dll",
"ColorCode.UWP.dll", "ColorCode.UWP.dll",
"UnitsNet.dll", "UnitsNet.dll",

View File

@@ -97,7 +97,7 @@ if (-not $Passive)
if ($files.count -gt 0) if ($files.count -gt 0)
{ {
dotnet tool run xstyler -c "$PSScriptRoot\..\Settings.XamlStyler" -f $files dotnet tool run xstyler -c "$PSScriptRoot\..\src\Settings.XamlStyler" -f $files
} }
else else
{ {
@@ -111,7 +111,7 @@ else
if ($files.count -gt 0) if ($files.count -gt 0)
{ {
dotnet tool run xstyler -p -c "$PSScriptRoot\..\Settings.XamlStyler" -f $files dotnet tool run xstyler -p -c "$PSScriptRoot\..\src\Settings.XamlStyler" -f $files
if ($lastExitCode -eq 1) if ($lastExitCode -eq 1)
{ {

View File

@@ -46,8 +46,14 @@ $totalList = $projFiles | ForEach-Object -Parallel {
foreach($p in $temp) foreach($p in $temp)
{ {
# ignore "Auto-referenced" string in the output
if ($p -match "Auto-referenced") {
continue
}
# breaking item down to usable array and getting 1 and 2, see below of a sample output # breaking item down to usable array and getting 1 and 2, see below of a sample output
# > PACKAGE VERSION VERSION # > PACKAGE VERSION VERSION
# if a package is Auto-referenced, "(A)" will appear in position 1 instead of a version number.
$p = -split $p $p = -split $p
$p = $p[1, 2] $p = $p[1, 2]

View File

@@ -9,12 +9,18 @@ Names are in alphabetical order based on first name.
### [@Aaron-Junker](https://github.com/Aaron-Junker) - [Aaron Junker](https://aaron-junker.github.io) ### [@Aaron-Junker](https://github.com/Aaron-Junker) - [Aaron Junker](https://aaron-junker.github.io)
Aaron has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Aaron was the primary person for helping build the File Explorer preview pane handler for developer files. Aaron has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Aaron was the primary person for helping build the File Explorer preview pane handler for developer files.
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
Christian contributed New+ utility
### [@CleanCodeDeveloper](https://github.com/CleanCodeDeveloper) ### [@CleanCodeDeveloper](https://github.com/CleanCodeDeveloper)
CleanCodeDeveloper helped do massive amounts of code stability and image resizer work. CleanCodeDeveloper helped do massive amounts of code stability and image resizer work.
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/) ### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
Damien has helped out by developing and contributing the Quick Accent utility. Damien has helped out by developing and contributing the Quick Accent utility.
### [@daverayment ](https://github.com/daverayment ) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/)
Dave has helped improve the experience inside of Peek by adding in new features and fixing bugs.
### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/) ### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/)
Davide has helped fix multiple bugs, added new utilities, features, as well as help us with the ARM64 effort by porting applications to .NET Core. Davide has helped fix multiple bugs, added new utilities, features, as well as help us with the ARM64 effort by porting applications to .NET Core.
@@ -42,6 +48,9 @@ Color Picker is from Martin.
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com) ### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility. Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
### [@PesBandi](https://github.com/PesBandi/) - PesBandi
PesBandi has helped do massive amounts of Quick Accent and bug fixes.
### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/) ### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/)
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well. Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
@@ -154,18 +163,24 @@ Other contributors:
## PowerToys core team ## PowerToys core team
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead - [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Lead - [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Product Manager - [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
- [@plante-msft](https://github.com/plante-msft) - Connor Plante - Product Manager - [@plante-msft](https://github.com/plante-msft) - Connor Plante - Product Manager
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager - [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
- [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie - Product Manager - [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie - Product Manager
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead - [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead - [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev - [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev - [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev - [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev - [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
# Former PowerToys core team members # Former PowerToys core team members
@@ -176,3 +191,4 @@ Other contributors:
- [@mykhailopylyp](https://github.com/mykhailopylyp) - Mykhailo Pylyp - Dev - [@mykhailopylyp](https://github.com/mykhailopylyp) - Mykhailo Pylyp - Dev
- [@taras-janea](https://github.com/taras-janea) - Taras Sich - Dev - [@taras-janea](https://github.com/taras-janea) - Taras Sich - Dev
- [@yuyoyuppe](https://github.com/yuyoyuppe) - Andrey Nekrasov - Dev - [@yuyoyuppe](https://github.com/yuyoyuppe) - Andrey Nekrasov - Dev
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev

View File

@@ -1,53 +1,53 @@
# PowerToys Contributor's Guide # PowerToys Contributor's Guide
Below is our guidance for how to report issues, propose new features, and submit contributions via Pull Requests (PRs). Our philosophy is heavily based around understanding the problem and scenarios first, this is why we follow this pattern before work has started. Below is our guidance for reporting issues, proposing new features, and submitting contributions via Pull Requests (PRs). Our philosophy is to understand the problem and scenarios first, which is why we follow this pattern before work starts.
1. There is an issue 1. There is an issue.
2. There has been a conversation 2. There has been a conversation.
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation) 3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation).
## Filing an issue ## Filing an Issue
Please follow this simple rule to help us eliminate any unnecessary wasted effort & frustration, and ensure an efficient and effective use of everyone's time - yours, ours, and other community members': **Importance of Filing an Issue First**
> 👉 If you have a question, think you've discovered an issue, would like to propose a new feature, etc., then find/file an issue **BEFORE** starting work to fix/implement it. Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyones time:
When requesting new features / enhancements, understanding problem and scenario around it is extremely important. Having additional evidence, data, tweets, blog posts, research, ... anything is extremely helpful. This information provides context to the scenario that may otherwise be lost. > 👉 If you have a question, think you've discovered an issue, or would like to propose a new feature, please find/file an issue **BEFORE** starting work to fix/implement it.
* Don't know whether you're reporting an issue or requesting a feature? File an issue When requesting new features or enhancements, providing additional evidence, data, tweets, blog posts, or research is extremely helpful. This information gives context to the scenario that may otherwise be lost.
* Have a question that you don't see answered in docs, videos, etc.? File an issue
* Want to know if we're planning on building a particular feature? File an issue
* Got a great idea for a new utility or feature? File an issue/request/idea
* Don't understand how to do something? File an issue/Community Guidance Request
* Found an existing issue that describes yours? Great - upvote and add additional commentary / info / repro-steps / etc.
A quick search before filing an issue also could be helpful. It is likely someone else has found the problem you're seeing, and someone may be working on or have already contributed a fix! * Unsure whether its an issue or feature request? File an issue.
* Have a question that isn't answered in the docs, videos, etc.? File an issue.
* Want to know if were planning a particular feature? File an issue.
* Got a great idea for a new utility or feature? File an issue/request/idea.
* Dont understand how to do something? File an issue/Community Guidance Request.
* Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
### How to tell the PowerToys team this is an interesting thing to focus on A quick search before filing an issue could be helpful. Its likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
Upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon or a different one. This way allows us to measure how impactful different issues are compared to others. The issue with comments like "+1", "me too", or similar is they actually make it harder to have a conversation and harder to quickly determine trending important requests. ### Indicating Interest in Issues
To let the team know which issues are important, upvote by clicking the [+😊] button and the 👍 icon on the original issue post. Avoid comments like "+1" or "me too" as they clutter the discussion and make it harder to prioritize requests.
--- ---
## Contributing fixes / features ## Contributing Fixes/Features
Please comment on [our "Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769) to let us know you're interested in working on something before you start the work. Not only does this avoid multiple people unexpectedly working on the same thing at the same time but it enables us to make sure everyone is clear on what should be done to implement any new functionality. It's less work for everyone, in the long run, to establish this up front. Please comment on our ["Would you like to contribute to PowerToys?"](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
### Localization issues ### Localization Issues
Please file localization issues, so our internal localization team can identify and fix them. However we currently don't accept community Pull Requests fixing localization issues. Localization is handled by the internal Microsoft team only. For localization issues, please file an issue to notify our internal localization team, as community PRs for localization aren't accepted. Localization is handled exclusively by the internal Microsoft team.
### To Spec or not to Spec ### To Spec or Not to Spec
A key point is for everyone to understand the approach that will be taken. We want to be sure if anyone does work, we will accept it in. Items that are larger in scope we'll want some type of spec to understand what is being planned and have a discussion. Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run. A key point is for everyone to understand the approach that will be taken. We want to be sure that any work done will be accepted. Larger-scope items will require a spec to outline the approach and allow for discussion. Specs help collaborators consider different solutions, describe feature behavior, and plan for errors. Achieving agreement in a spec before writing code often results in simpler code and less wasted effort.
For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Development" section below. Once a team member has agreed with your approach, proceed to the "Development" section below. Team members are happy to help review specs and guide them to completion.
Team members will be happy to help review specs and guide them to completion.
### Help Wanted ### Help Wanted
Once the team has approved an issue/spec approach to solving, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/PowerToys/labels/Help%20Wanted). Once the team has approved an issue/spec approach, development can proceed. If no developers are immediately available, the spec may be parked and labeled "Help Wanted," ready for a developer to get started. For development opportunities, visit [Issues labeled Help Wanted](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
--- ---
@@ -55,18 +55,18 @@ Once the team has approved an issue/spec approach to solving, development can pr
Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md). Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md).
### Naming of features and functionality ### Naming Features and Functionality
Naming should be descriptive and straight forward. We want names to be clear about functionality and usefulness moving forward. Names should be descriptive and straightforward, clearly reflecting functionality and usefulness.
### How can I become a collaborator on the PowerToys team ### Becoming a Collaborator on the PowerToys Team
Be a great community member. Just help out a lot and make useful additions, filing bugs/suggestions, help develop fixes and features, code reviews, and always, docs. Lets continue to make the PowerToys repository a great spot to learn and make a great set of utilities. Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
When the time comes, Microsoft will reach out and help make you a formal team member. Just make sure they can reach out to you :) When the time comes, Microsoft will reach out to you about becoming a formal team member. Just make sure they have a way to contact you. 😊
--- ---
## Thank you ## Thank You
Thank you in advance for your contribution! Thank you in advance for your contribution! We appreciate your help in making PowerToys a better tool for everyone.

View File

@@ -67,6 +67,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.TrayFlyoutModuleRunEvent</td> <td>Microsoft.PowerToys.TrayFlyoutModuleRunEvent</td>
<td>Logs when a utility from the tray flyout menu is run.</td> <td>Logs when a utility from the tray flyout menu is run.</td>
</tr> </tr>
<tr>
<td>Microsoft.PowerToys.Uninstall_Success</td>
<td>Logs when PowerToys is successfully uninstalled (who would do such a thing!).</td>
</tr>
</table> </table>
### OOBE (Out-of-box experience) ### OOBE (Out-of-box experience)
@@ -139,6 +143,10 @@ _If you want to find diagnostic data events in the source code, these two links
<td>Microsoft.PowerToys.AdvancedPasteInAppKeyboardShortcutEvent</td> <td>Microsoft.PowerToys.AdvancedPasteInAppKeyboardShortcutEvent</td>
<td>Triggered when a keyboard shortcut is used within the Advanced Paste interface.</td> <td>Triggered when a keyboard shortcut is used within the Advanced Paste interface.</td>
</tr> </tr>
<tr>
<td>Microsoft.PowerToys.AdvancedPasteSemanticKernelFormatEvent</td>
<td>Triggered when Advanced Paste leverages the Semantic Kernel.</td>
</tr>
</table> </table>
### Always on Top ### Always on Top

View File

@@ -4,7 +4,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Appium.WebDriver" Version="4.4.5" /> <PackageVersion Include="Appium.WebDriver" Version="4.4.5" />
<PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.12" /> <PackageVersion Include="Azure.AI.OpenAI" Version="1.0.0-beta.17" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" /> <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.0.240109" /> <PackageVersion Include="CommunityToolkit.WinUI.Animations" Version="8.0.240109" />
<PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.0.240109" /> <PackageVersion Include="CommunityToolkit.WinUI.Collections" Version="8.0.240109" />
@@ -26,14 +26,17 @@
<PackageVersion Include="Markdig.Signed" Version="0.34.0" /> <PackageVersion Include="Markdig.Signed" Version="0.34.0" />
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. --> <!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageVersion Include="MessagePack" Version="2.5.187" /> <PackageVersion Include="MessagePack" Version="2.5.187" />
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0-preview.24508.2" /> <PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.0" /> <PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.0" />
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" /> <PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" /> <PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" /> <PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2739.15" /> <PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2739.15" />
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. --> <!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
@@ -47,16 +50,17 @@
--> -->
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" /> <PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" /> <PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" /> <PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" /> <PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" /> <PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageVersion Include="ModernWpfUI" Version="0.9.4" /> <PackageVersion Include="ModernWpfUI" Version="0.9.4" />
<!-- Moq to stay below v4.20 due to behavior change. need to be sure fixed --> <!-- Moq to stay below v4.20 due to behavior change. need to be sure fixed -->
<PackageVersion Include="Moq" Version="4.18.4" /> <PackageVersion Include="Moq" Version="4.18.4" />
<PackageVersion Include="MSTest" Version="3.5.0" /> <PackageVersion Include="MSTest" Version="3.6.3" />
<PackageVersion Include="NLog" Version="5.0.4" /> <PackageVersion Include="NLog" Version="5.0.4" />
<PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" /> <PackageVersion Include="NLog.Extensions.Logging" Version="5.3.8" />
<PackageVersion Include="NLog.Schema" Version="5.2.8" /> <PackageVersion Include="NLog.Schema" Version="5.2.8" />
<PackageVersion Include="OpenAI" Version="2.0.0" />
<PackageVersion Include="ReverseMarkdown" Version="4.1.0" /> <PackageVersion Include="ReverseMarkdown" Version="4.1.0" />
<PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" /> <PackageVersion Include="ScipBe.Common.Office.OneNote" Version="3.0.1" />
<PackageVersion Include="SharpCompress" Version="0.37.2" /> <PackageVersion Include="SharpCompress" Version="0.37.2" />
@@ -94,4 +98,4 @@
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" /> <PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" /> <PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1297,7 +1297,7 @@ EXHIBIT A -Mozilla Public License.
## NuGet Packages used by PowerToys ## NuGet Packages used by PowerToys
- Appium.WebDriver 4.4.5 - Appium.WebDriver 4.4.5
- Azure.AI.OpenAI 1.0.0-beta.12 - Azure.AI.OpenAI 1.0.0-beta.17
- CommunityToolkit.Mvvm 8.2.2 - CommunityToolkit.Mvvm 8.2.2
- CommunityToolkit.WinUI.Animations 8.0.240109 - CommunityToolkit.WinUI.Animations 8.0.240109
- CommunityToolkit.WinUI.Collections 8.0.240109 - CommunityToolkit.WinUI.Collections 8.0.240109
@@ -1318,7 +1318,8 @@ EXHIBIT A -Mozilla Public License.
- Mages 2.0.2 - Mages 2.0.2
- Markdig.Signed 0.34.0 - Markdig.Signed 0.34.0
- MessagePack 2.5.187 - MessagePack 2.5.187
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0-preview.24508.2 - Microsoft.Bcl.AsyncInterfaces 9.0.0
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
- Microsoft.Data.Sqlite 9.0.0 - Microsoft.Data.Sqlite 9.0.0
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16 - Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
- Microsoft.Extensions.DependencyInjection 9.0.0 - Microsoft.Extensions.DependencyInjection 9.0.0
@@ -1326,6 +1327,8 @@ EXHIBIT A -Mozilla Public License.
- Microsoft.Extensions.Hosting.WindowsServices 9.0.0 - Microsoft.Extensions.Hosting.WindowsServices 9.0.0
- Microsoft.Extensions.Logging 9.0.0 - Microsoft.Extensions.Logging 9.0.0
- Microsoft.Extensions.Logging.Abstractions 9.0.0 - Microsoft.Extensions.Logging.Abstractions 9.0.0
- Microsoft.NET.ILLink.Tasks (A)
- Microsoft.SemanticKernel 1.15.0
- Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Toolkit.Uwp.Notifications 7.1.2
- Microsoft.Web.WebView2 1.0.2739.15 - Microsoft.Web.WebView2 1.0.2739.15
- Microsoft.Win32.SystemEvents 9.0.0 - Microsoft.Win32.SystemEvents 9.0.0
@@ -1333,14 +1336,15 @@ EXHIBIT A -Mozilla Public License.
- Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWin32 0.2.46-beta
- Microsoft.Windows.CsWinRT 2.1.5 - Microsoft.Windows.CsWinRT 2.1.5
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
- Microsoft.WindowsAppSDK 1.6.240923002 - Microsoft.WindowsAppSDK 1.6.241114003
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
- Microsoft.Xaml.Behaviors.Wpf 1.1.39 - Microsoft.Xaml.Behaviors.Wpf 1.1.39
- ModernWpfUI 0.9.4 - ModernWpfUI 0.9.4
- Moq 4.18.4 - Moq 4.18.4
- MSTest 3.5.0 - MSTest 3.6.3
- NLog.Extensions.Logging 5.3.8 - NLog.Extensions.Logging 5.3.8
- NLog.Schema 5.2.8 - NLog.Schema 5.2.8
- OpenAI 2.0.0
- ReverseMarkdown 4.1.0 - ReverseMarkdown 4.1.0
- ScipBe.Common.Office.OneNote 3.0.1 - ScipBe.Common.Office.OneNote 3.0.1
- SharpCompress 0.37.2 - SharpCompress 0.37.2

View File

@@ -171,6 +171,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
src\.editorconfig = src\.editorconfig src\.editorconfig = src\.editorconfig
.vsconfig = .vsconfig .vsconfig = .vsconfig
src\Common.Dotnet.AotCompatibility.props = src\Common.Dotnet.AotCompatibility.props
src\Common.Dotnet.CsWinRT.props = src\Common.Dotnet.CsWinRT.props src\Common.Dotnet.CsWinRT.props = src\Common.Dotnet.CsWinRT.props
src\Common.SelfContained.props = src\Common.SelfContained.props src\Common.SelfContained.props = src\Common.SelfContained.props
Cpp.Build.props = Cpp.Build.props Cpp.Build.props = Cpp.Build.props
@@ -630,6 +631,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "src\common\Tele
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedPaste.UnitTests", "src\modules\AdvancedPaste\AdvancedPaste.UnitTests\AdvancedPaste.UnitTests.csproj", "{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64 Debug|ARM64 = Debug|ARM64
@@ -2246,6 +2253,30 @@ Global
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x64.Build.0 = Release|x64 {8A08D663-4995-40E3-B42C-3F910625F284}.Release|x64.Build.0 = Release|x64
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.ActiveCfg = Release|x64 {8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.ActiveCfg = Release|x64
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.Build.0 = Release|x64 {8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.ActiveCfg = Debug|ARM64 {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.Build.0 = Debug|ARM64 {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.Build.0 = Debug|ARM64
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x64.ActiveCfg = Debug|x64 {D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x64.ActiveCfg = Debug|x64
@@ -2646,30 +2677,6 @@ Global
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64 {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64 {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64 {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64
@@ -2778,6 +2785,42 @@ Global
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64 {66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.ActiveCfg = Release|x64 {66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.ActiveCfg = Release|x64
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.Build.0 = Release|x64 {66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.ActiveCfg = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.Build.0 = Debug|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.ActiveCfg = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.Build.0 = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.ActiveCfg = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.Build.0 = Debug|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.ActiveCfg = Release|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.Build.0 = Release|ARM64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.ActiveCfg = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.Build.0 = Debug|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.ActiveCfg = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.Build.0 = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.ActiveCfg = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.Build.0 = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.ActiveCfg = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.Build.0 = Release|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|ARM64.Build.0 = Debug|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|x64.ActiveCfg = Debug|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|x64.Build.0 = Debug|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|x86.ActiveCfg = Debug|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Debug|x86.Build.0 = Debug|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|ARM64.ActiveCfg = Release|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|ARM64.Build.0 = Release|ARM64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|x64.ActiveCfg = Release|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|x64.Build.0 = Release|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|x86.ActiveCfg = Release|x64
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE}.Release|x86.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -2956,6 +2999,8 @@ Global
{B5EB9FE9-37EF-47C3-B8B8-81AD3C2972C2} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} {B5EB9FE9-37EF-47C3-B8B8-81AD3C2972C2} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{A663E672-B26D-4EC0-BEAB-FE2E424AC46F} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} {A663E672-B26D-4EC0-BEAB-FE2E424AC46F} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A} {8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A} {D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B} {FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B}
@@ -2995,8 +3040,6 @@ Global
{8ACB33D9-C95B-47D4-8363-9731EE0930A0} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC} {8ACB33D9-C95B-47D4-8363-9731EE0930A0} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
{CA716AE6-FE5C-40AC-BB8F-2C87912687AC} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {CA716AE6-FE5C-40AC-BB8F-2C87912687AC} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{F055103B-F80B-4D0C-BF48-057C55620033} = {5A7818A8-109C-4E1C-850D-1A654E234B0E} {F055103B-F80B-4D0C-BF48-057C55620033} = {5A7818A8-109C-4E1C-850D-1A654E234B0E}
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
{A2221D7E-55E7-4BEA-90D1-4F162D670BBF} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
@@ -3009,6 +3052,9 @@ Global
{37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216} {8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} {66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
{D5E5F5EA-1B6C-4A73-88BE-304F36C9E4EE} = {9873BA05-4C41-4819-9283-CF45D795431B}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

156
README.md
View File

@@ -1,6 +1,6 @@
# Microsoft PowerToys # Microsoft PowerToys
![Hero image for Microsoft PowerToys](doc/images/overview/PT_hero_image.png) ![Hero image for Microsoft PowerToys](doc/images/overview/PT_holiday_hero_image.png)
[How to use PowerToys][usingPowerToys-docs-link] | [Downloads & Release notes][github-release-link] | [Contributing to PowerToys](#contributing) | [What's Happening](#whats-happening) | [Roadmap](#powertoys-roadmap) [How to use PowerToys][usingPowerToys-docs-link] | [Downloads & Release notes][github-release-link] | [Contributing to PowerToys](#contributing) | [What's Happening](#whats-happening) | [Roadmap](#powertoys-roadmap)
@@ -20,6 +20,12 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
| [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) |
| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
## 🎁⭐ PowerToys Advent calendar ⭐🎁
We will be highlighting a cool utility each day for 24 days in December! To follow along, check out these threads:
- https://bsky.app/profile/kaylacinnamon.bsky.social/post/3lcb7iljxck2o
- https://x.com/cinnamon_msft/status/1863284610773246257
## Installing and running Microsoft PowerToys ## Installing and running Microsoft PowerToys
### Requirements ### Requirements
@@ -34,19 +40,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user. Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
<!-- items that need to be updated release to release --> <!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.87%22 [github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.88%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.86%22 [github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.87%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysUserSetup-0.86.0-x64.exe [ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.0/PowerToysUserSetup-0.87.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysUserSetup-0.86.0-arm64.exe [ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.0/PowerToysUserSetup-0.87.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysSetup-0.86.0-x64.exe [ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.0/PowerToysSetup-0.87.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.86.0/PowerToysSetup-0.86.0-arm64.exe [ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.87.0/PowerToysSetup-0.87.0-arm64.exe
| Description | Filename | sha256 hash | | Description | Filename | sha256 hash |
|----------------|----------|-------------| |----------------|----------|-------------|
| Per user - x64 | [PowerToysUserSetup-0.86.0-x64.exe][ptUserX64] | CFB9608B28B8FF12C9A7C9814A6EF981636EB5AB261DC278C28EC93FD959CCE2 | | Per user - x64 | [PowerToysUserSetup-0.87.0-x64.exe][ptUserX64] | A6549B8D78985CC995F091624D1A2B70907CAC8954334C1CAF61D26EBCF8A449 |
| Per user - ARM64 | [PowerToysUserSetup-0.86.0-arm64.exe][ptUserArm64] | 861CEDBFDCDA993D1D1056E3280319D5EA45D142CA3C737AB1FB4FABD651A5F5 | | Per user - ARM64 | [PowerToysUserSetup-0.87.0-arm64.exe][ptUserArm64] | 3557D4F35AA52571334712A48F51D116F389FA8C43C6B27FE321A7525067E7AE |
| Machine wide - x64 | [PowerToysSetup-0.86.0-x64.exe][ptMachineX64] | 857DE9DC5938D9602F82DFD6183DB5E6823B875A412AEC59B4BE93617E27E9CD | | Machine wide - x64 | [PowerToysSetup-0.87.0-x64.exe][ptMachineX64] | 600CDC7F9AC296AA8B554CA34A0C7EA2D9B1E7E8E41BD096840851B416E63A3C |
| Machine wide - ARM64 | [PowerToysSetup-0.86.0-arm64.exe][ptMachineArm64] | 6F37192534C195A02A80AAE1E449DF61C894C50763096A06195581801943FA31 | | Machine wide - ARM64 | [PowerToysSetup-0.87.0-arm64.exe][ptMachineArm64] | 387B5BF1BD923BDA215D7DF1D82A197AE12CD91A71A73267768E26757F7A5FE6 |
This is our preferred method. This is our preferred method.
@@ -92,103 +98,115 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on. Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
### 0.86 - October 2024 Update ### 0.87 - December 2024 Update
In this release, we focused on new features, stability, and improvements. In this release, we focused on new features, stability, and improvements.
**Highlights** **Highlights**
- Advanced Paste has new abilities: Image to text, and paste to file (text / png / html).
- In settings, we've adjusted the left navigation to group the utilities. As the number of utilities shipped with PowerToys keeps growing, we felt this was a needed adjustment. Thanks everyone for your feedback!
- Workspaces received many bug fixes, including the proper launching of many instances of the same application in the same workspace. Note, we are still actively looking at how to properly handle PWA detection.
- We've added a diagnostic data (telemetry) opt-in option in the Settings General tab. As it is off-by-default, we encourage users to turn it on as that helps direct our development efforts and their journeys. More information about the data we collect can be found in the [PowerToys Data and Privacy documentation](https://aka.ms/powertoys-data-and-privacy-documentation) and what each event does.
### General - Advanced Paste has a new feature called "Advanced AI" that uses Semantic Kernel to allow setting up the orchestration of sequential clipboard transformations.
- Workspaces supports Progressive Web Applications.
- Added a setting for diagnostic data (telemetry) opt-in (off by default, however, see above for why we encourage you to opt-in!) and user controls to view data. - Workspaces has a new feature to move existing windows instead of creating new ones.
- Improved exception logging by adding the type of Exception and InnerException. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Mouse Jump added new settings to allow customization of screens pop-up. Thanks [@mikeclayton](https://github.com/mikeclayton)!
- New+ now works on Windows 10. Thanks [@cgaarden](https://github.com/cgaarden)!
- Quick Accent allows selecting the character sets that should appear on the UI. Thanks [@Sirozha1337](https://github.com/Sirozha1337)!
### Advanced Paste ### Advanced Paste
- Added new built-in actions: Image to text, and paste txt, png or html as a file. - Added a new optional feature allowing using AI to set up the orchestration of sequential clipboard transformations.
### Awake
- Initialization, logging and tray icon setup improvements. Thanks [@dend](https://github.com/dend)!
### File Explorer add-ons
- Preview Pane extensions now use the PerMonitorV2 DPI mode to fix errors on different scales. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Keyboard Manager.
- Added labels to the IME On, IME Off keys. Thanks [@kit494way](https://github.com/kit494way)!
- Fixed an issue that caused the Shift key to remain stuck if a numpad key was mapped to the Shift key.
### Monaco Preview
- Added support for .ahk files to be shown as a plaintext file in Peek and File Explorer add-ons. Thanks [@daverayment](https://github.com/daverayment)!
- Added support for .ion files to be shown as a plaintext file in Peek and File Explorer add-ons. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
- Added support for syntax highlighting for .srt files in Peek and File Explorer add-ons. Thanks [@PesBandi](https://github.com/PesBandi)!
### Mouse Jump ### Mouse Jump
- Refactored the common classes into a separate project. Thanks [@mikeclayton](https://github.com/mikeclayton)! - Allow customizing the appearance of the UI of the Mouse Jump pop-up. Thanks [@mikeclayton](https://github.com/mikeclayton)!
- Brought back the telemetry events that were deleted across previous refactoring efforts.
### Mouse Without Borders
- Refactored the Logger common classes. Thanks [@mikeclayton](https://github.com/mikeclayton)!
### New+ ### New+
- Fixed the telemetry event for when the modules is enabled or disabled. (This was a hotfix for 0.85) - Added support for Windows 10. Thanks [@cgaarden](https://github.com/cgaarden)!
- Fixed bug when creating folders or files that contain Unicode characters. Thanks [@cgaarden](https://github.com/cgaarden)! - Fixed an issue causing the renaming of new files to not trigger some times. Thanks [@cgaarden](https://github.com/cgaarden)!
- Fixed bug when the name of a new folder collided with an already existing folder. Thanks [@cgaarden](https://github.com/cgaarden)! - Updated the New+ icons. Thanks [@niels9001](https://github.com/niels9001)!
- Updated the New+ icons to the fluent style.
### Peek ### Peek
- Folder preview enumeration of size and number of files is now more responsive and faster. Thanks [@daverayment](https://github.com/daverayment)! - Peek now checks local capabilities to decide what image formats Image Previewer is able to support. Thanks [@daverayment](https://github.com/daverayment)!
- Fixed an issue causing the Code Files Previewer to not load correctly under certain conditions. Thanks [@daverayment](https://github.com/daverayment)!
- Refactored, improved and fixed logging when loading the user settings file. Thanks [@daverayment](https://github.com/daverayment)!
### PowerToys Run ### PowerToys Run
- Handled a culture not found error when checking for right-to-left languages. - Added a scoring function for proper ordering of the WindowWalker plugin results. Thanks [@andbartol](https://github.com/andbartol)!
- Fixed the WebSearch plugin results title being trimmed in the UI. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)! - Added UUIDv7 support to the ValueGenerator plugin. Thanks [@frederik-hoeft](https://github.com/frederik-hoeft)!
- The Unit Converter plugin will now show more significant digits. Thanks [@PesBandi](https://github.com/PesBandi)! - The calculator plugin now allows scientific notation numbers with a lowercase 'e'. Thanks [@PesBandi](https://github.com/PesBandi)!
- Improved error handling when copying to the clipboard results in an error. Thanks [@PesBandi](https://github.com/PesBandi)! - Ported the UI from WPF-UI to .NET 9 WPF, to fix "Desktop composition is disabled" crashes.
### Quick Accent ### Quick Accent
- Added support for the Serbian Cyrillic character set. Thanks [@Sirozha1337](https://github.com/Sirozha1337)! - Added a setting to allow selecting which character sets to show. Thanks [@Sirozha1337](https://github.com/Sirozha1337)!
### Registry Preview ### Screen Ruler
- Adopted the Monaco Editor as the UI text editor. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Added a Setting to also allow showing measurements in inches, centimeters or millimeters. Thanks [@Sophanatprime](https://github.com/Sophanatprime)!
### Settings ### Settings
- Fixed a crash when trying to access a non-existing templates folder from the New+ page. (This was a hotfix for 0.85) - Fixed an issue causing all the links to milestones in the "What's new?" OOBE page to point to the same milestone.
- Added a navigation tree to group utilities in the left navigation menu. - Removed extra space from the Welcome page. Thanks [@agarwalishita](https://github.com/agarwalishita)!
- Sorted the list of languages in the language selection combo box in the General tab. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - Updated left navigation bar icons. Thanks [@niels9001](https://github.com/niels9001)!
- Fixed the state of the info bar about templates not being backed up to not close and react to the module's enabled state in the New+ page. Thanks [@htcfreek](https://github.com/htcfreek)! - Fixed accessibility issues in the dashboard page. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed a crash caused by a dangling thread.
- Clicking a notification about there being an update available should now correctly open the Settings application in the General tab.
- Fixed a UI freeze when trying to access the Diagnostic Data Viewer files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Workspaces ### Workspaces
- Fixed launching the incorrect workspace when launching many workspaces quickly through shortcuts. (This was a hotfix for 0.85) - Added support for Progressive Web Applications to Workspaces.
- Fixed launching many instances of the same application in a workspace. - Implemented a feature to move existing windows instead of creating new ones.
- Fixed a crash when a previously captured monitor ID no longer existed. - Fixed a crash when opening the workspaces editor that was caused by passing incorrect encoder parameters when saving Bitmap files.
- Fixed an issue causing the wrong coordinates to be saved for minimized applications. - Workspaces editor position is now saved so that we can start it at the same position when we open it again.
- Fixed an issue causing a crash when stress testing workspace launching. - Fixed an issue causing many instances of the same application to be put in the same position instead of the intended position due to timer issues.
- Fixed application launching when UAC is off and every application always runs elevated. - Fixed detection of exact application version when many versions of the same application are installed.
### Documentation ### Documentation
- Added HackMD plugin mention to thirdPartyRunPlugins.md. Thanks [@8LWXpg](https://github.com/8LWXpg)! - Improved language in CONTRIBUTE.md. Thanks [@sanskaarz](https://github.com/sanskaarz)!
- Added SSH plugin mention to thirdPartyRunPlugins.md. Thanks [@8LWXpg](https://github.com/8LWXpg)! - Added Bilibili plugin mention to thirdPartyRunPlugins.md. Thanks [@Whuihuan](https://github.com/Whuihuan)!
- Added the [Data and Privacy documentation](https://github.com/microsoft/PowerToys/blob/main/DATA_AND_PRIVACY.md) to the repo. - Added CanIUse and TailwindCSS plugins mention to thirdPartyRunPlugins.md. Thanks [@skttl](https://github.com/skttl)!
- Added HttpStatusCodes plugin mention to thirdPartyRunPlugins.md. Thanks [@grzhan](https://github.com/grzhan)!
- Updated COMMUNITY.md with more contributors.
### Development ### Development
- Fixed the CI precheck action to take into account the recent changes in CI actions. - Upgraded to .NET 9. Thanks [@snickler](https://github.com/snickler)!
- Added the new Microsoft org issue types to the issue templates. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)! - Fixed building on Visual Studio 17.12.
- Updated System.Text.Json to 8.0.5 and System.Runtime.Caching to 8.0.1 and related dependencies to the latest to address security reports. Thanks [@snickler](https://github.com/snickler)! - Upgraded the System.IO.Abstractions dependency to 21.0.29. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Updated WinAppSDK to 1.6.1 and CsWinRT to 2.1.5. Thanks [@snickler](https://github.com/snickler)! - Upgraded the WindowsAppSDK dependency to 1.6.241114003. Thanks [@shuaiyuanxx](https://github.com/shuaiyuanxx)!
- Upgraded the WpfUI dependency to 3.0.5. - Upgraded the MSTest dependency to 3.6.3. Thanks [@Youssef1313](https://github.com/Youssef1313)!
- Updated MessagePack to 2.5.187 and StreamJsonRpc to 2.19.27 to address security reports. - Upgraded the check-spelling CI dependency to 0.0.24 and fixed related spell checking issues. Thanks [@jsoref](https://github.com/jsoref)!
- Removed some of the hacks that are no longer needed that tried to force same dependency versions in .csproj files. - Removed duplicate names from the spellcheck allowed names file. Thanks [@htcfreek](https://github.com/htcfreek)!
- Removed the Markdown file exclusions from the conditions that trigger a full CI test. - Improved logging of asynchronous methods call stacks when logging an error.
- CI fails again when there are XAML style errors in a PR. - Created a MSBuild props file to be imported by other projects to enable AOT support.
- Fixed CI actions that were not failing when one of the powershell scripts they tried to run was failing. - Made the Peek utility source code AOT compatible.
- Fixed analyzer violations to allow fully building PowerToys on Visual Studio 17.12. Thanks [@snickler](https://github.com/snickler)! - Updated .editorconfig rules to relax squiggly IDE errors in Visual Studio 17.12. Thanks [@snickler](https://github.com/snickler)!
- Moved Xaml.Styler from the root to the src folder.
#### What is being planned for version 0.87 #### What is being planned for version 0.88
For [v0.87][github-next-release-work], we'll work on the items below: For [v0.88][github-next-release-work], we'll work on the items below:
- Stability / bug fixes - Stability / bug fixes
- New module: File Actions Menu - New module: File Actions Menu

View File

@@ -47,7 +47,7 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco)
* The id can be anything. Recommended is one of the file extensions. For example "php" or "reg". * The id can be anything. Recommended is one of the file extensions. For example "php" or "reg".
4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenColors.js`](/src/Monaco/customTokenColors.js): 4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenThemeRules.js`](/src/Monaco/customTokenThemeRules.js):
```javascript ```javascript
{token: 'token-name', foreground: 'ff0000'} {token: 'token-name', foreground: 'ff0000'}
``` ```

View File

@@ -97,6 +97,6 @@ namespace
``` ```
Note: since _background activation_ implies that your toast handler will be invoked in a separate process, you can't share data directly from within a handler and your PT process. Also, since PT is currently a Desktop Bridge app, _foreground activation_ is [handled the same as background](https://learn.microsoft.com/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop-cpp-wrl#foreground-vs-background-activation), therefore we don't make a dedicated API for it. You can read more on the rationale of the current design [here](https://github.com/microsoft/PowerToys/pull/1178#issue-368768337). Note: since _background activation_ implies that your toast handler will be invoked in a separate process, you can't share data directly from within a handler and your PT process. Also, since PT is currently a Desktop Bridge app, _foreground activation_ is [handled the same as background](https://learn.microsoft.com/windows/uwp/design/shell/tiles-and-notifications/send-local-toast-desktop-cpp-wrl#foreground-vs-background-activation), therefore we don't make a dedicated API for it. You can read more on the [rationale of the current design](https://github.com/microsoft/PowerToys/pull/1178#issue-368768337).

View File

@@ -17,13 +17,13 @@
## Localization on the pipeline (CDPX) ## 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 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 [here](#lcl-files). Once the `.resx` files are generated, they will be used during the `Build PowerToys` step for localized versions of the modules. 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-Pipelinhttps://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. 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 [here](https://onebranch.visualstudio.com/Pipeline/_wiki/wikis/Pipeline.wiki/290/Localization). 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 [here](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. 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 ## 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. To enable localization on a new project, the first step is to create a file `LocProject.json` in the project root.
@@ -45,7 +45,7 @@ For example, for a project in the folder `src\path` where the resx file is prese
] ]
} }
``` ```
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 [here](#Enabling-localized-MSI-for-a-new-project). 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++
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. 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.
@@ -59,7 +59,7 @@ After generating the resx file, rename the existing rc and h files to ProjName.b
</Target> </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 can be found [here](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: 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) #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
@@ -71,7 +71,7 @@ END
#endif #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 [here](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: 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" /> <None Include="Resources.resx" />
``` ```
@@ -86,7 +86,7 @@ Since C# projects natively support `resx` files, the only step required here is
<EmbeddedResource Include="Properties\Resources.*.resx" /> <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 [here](https://github.com/microsoft/PowerToys/issues/7269). **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. **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.
``` ```
@@ -153,7 +153,7 @@ For C++ and UWP projects no additional files are generated with localization tha
``` ```
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). 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 [here](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/installer/PowerToysSetup/Product.wxs#L806) and a resource component for the project can be created [here](https://github.com/microsoft/PowerToys/blob/f92bd6ffd38014c228544bb8d68d0937ce4c2b6d/installer/PowerToysSetup/Product.wxs#L845-L847) in this format: 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"> <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" /> <File Id="ProjName_$(var.IdSafeLanguage)_File" Source="$(var.BinX64Dir)modules\ProjName\$(var.Language)\ProjName.resources.dll" />

View File

@@ -25,13 +25,13 @@ This file contains documentation for all the methods involved in key/shortcut re
- Check if any shortcut remap is currently invoked. This is required to ensure that two remaps don't occur simultaneously at a time, and we send key up events for the shortcuts only if they are actually invoked and not for artificial key up events. In addition to that, while a remap is in the middle of execution, the keyboard state will not match the physical keys, so we do not want a remap <kbd>Ctrl+A</kbd> to <kbd>Ctrl+V</kbd> to also trigger the remap from <kbd>Ctrl+V</kbd> to <kbd>Alt+V</kbd> on pressing <kbd>Ctrl+A</kbd> on the keyboard. - Check if any shortcut remap is currently invoked. This is required to ensure that two remaps don't occur simultaneously at a time, and we send key up events for the shortcuts only if they are actually invoked and not for artificial key up events. In addition to that, while a remap is in the middle of execution, the keyboard state will not match the physical keys, so we do not want a remap <kbd>Ctrl+A</kbd> to <kbd>Ctrl+V</kbd> to also trigger the remap from <kbd>Ctrl+V</kbd> to <kbd>Alt+V</kbd> on pressing <kbd>Ctrl+A</kbd> on the keyboard.
- Get the remap table as per the `activatedApp` argument (i.e. if it is empty, we get the global shortcut remap table and otherwise we get the corresponding app-specific shortcut remap table). - Get the remap table as per the `activatedApp` argument (i.e. if it is empty, we get the global shortcut remap table and otherwise we get the corresponding app-specific shortcut remap table).
- Iterate over the list of remaps in descending order of number of keys in the shortcut. This is required **for shortcut to key remaps** to ensure that if a user has both <kbd>Ctrl+A</kbd> and <kbd>Ctrl+Shift+A</kbd> remapped to some keys, and the user presses <kbd>Ctrl+Shift+A</kbd>, then we prefer the <kbd>Ctrl+Shift+A</kbd> remap. This logic would not be required if there were only shortcut to shortcut remaps, as they are invoked only on exact match. - Iterate over the list of remaps in descending order of number of keys in the shortcut. This is required **for shortcut to key remaps** to ensure that if a user has both <kbd>Ctrl+A</kbd> and <kbd>Ctrl+Shift+A</kbd> remapped to some keys, and the user presses <kbd>Ctrl+Shift+A</kbd>, then we prefer the <kbd>Ctrl+Shift+A</kbd> remap. This logic would not be required if there were only shortcut to shortcut remaps, as they are invoked only on exact match.
- If any shortcut was found to be invoked (from the first step), then we skip till we find the matching shortcut remap. If not we check if the modifiers of the original shortcut are pressed down. If they are, we check if the current key event is a key down event and it matches the action key of the original shortcut. For shortcut to shortcut and for disabling a shortcut [we have an additional step](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L208-L212) where we check if any other key is pressed apart from the original shortcut. This is required because for these two features we allow the remaps only if those exact keys are pressed. The method used for this is described in detail [here](keyboardmanagercommon.md#IsKeyboardStateClearExceptShortcut). If a win key was pressed, we store whether it was the left or the right one, in order to determine which key to set for remaps from/to the common Win key code which we added. This is so that pressing and releasing Left Win key results in that Win key getting modified and not the Right Win key. - If any shortcut was found to be invoked (from the first step), then we skip till we find the matching shortcut remap. If not we check if the modifiers of the original shortcut are pressed down. If they are, we check if the current key event is a key down event and it matches the action key of the original shortcut. For shortcut to shortcut and for disabling a shortcut [we have an additional step](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L208-L212) where we check if any other key is pressed apart from the original shortcut. This is required because for these two features we allow the remaps only if those exact keys are pressed. The method used for this is [described in detail](keyboardmanagercommon.md#IsKeyboardStateClearExceptShortcut). If a win key was pressed, we store whether it was the left or the right one, in order to determine which key to set for remaps from/to the common Win key code which we added. This is so that pressing and releasing Left Win key results in that Win key getting modified and not the Right Win key.
- If the remap is to a key, we send a dummy key event followed by releasing the original shortcut's modifiers and setting the target key (or doing nothing if it is remapped to disable) and we suppress the event. - If the remap is to a key, we send a dummy key event followed by releasing the original shortcut's modifiers and setting the target key (or doing nothing if it is remapped to disable) and we suppress the event.
- If the remap is to a shortcut, if the modifiers in the original shortcut are present in the target, we only set the additional modifiers and the action key of the target. If it isn't, we send a dummy key event followed by releasing the modifiers which are not common, and setting the remaining ones in the target along with the action key. - If the remap is to a shortcut, if the modifiers in the original shortcut are present in the target, we only set the additional modifiers and the action key of the target. If it isn't, we send a dummy key event followed by releasing the modifiers which are not common, and setting the remaining ones in the target along with the action key.
- For both cases, we set the `isShortcutInvoked` flag to true, and set the `KeyboardManagerState.activatedApp` if it is an app-specific shortcut remap. - For both cases, we set the `isShortcutInvoked` flag to true, and set the `KeyboardManagerState.activatedApp` if it is an app-specific shortcut remap.
- For the `isShortcutInvoked` is true scenario (i.e. the initial remap keydown section is done) there are several cases depending on the key pressed or released: - For the `isShortcutInvoked` is true scenario (i.e. the initial remap keydown section is done) there are several cases depending on the key pressed or released:
- [**Case 1:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L339-L430) If a modifier in the original shortcut is released, we need to reset back to the physical keys pressed. - [**Case 1:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L339-L430) If a modifier in the original shortcut is released, we need to reset back to the physical keys pressed.
- For remap to shortcut, we release the target action key if it is currently pressed, and depending on whether all the modifiers of the original shortcut are present in the target, we release the target modifiers that are not common, and set the remaining original shortcut modifiers except the one that was released. We do not need to send the original action key as that will get generate it's own key event if it is held down. - For remap to shortcut, we release the target action key if it is currently pressed, and depending on whether all the modifiers of the original shortcut are present in the target, we release the target modifiers that are not common, and set the remaining original shortcut modifiers except the one that was released. We do not need to send the original action key as that will get generate its own key event if it is held down.
- For remap to key, we release the target key if it is pressed (and it is not remapped to Disable), and we set the original shortcut modifiers. - For remap to key, we release the target key if it is pressed (and it is not remapped to Disable), and we set the original shortcut modifiers.
- For both the cases we send a dummy key event at the end, since we are setting modifiers without any other key after that, and we reset all the remap variables. - For both the cases we send a dummy key event at the end, since we are setting modifiers without any other key after that, and we reset all the remap variables.
- [**Case 2:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L435-L461) If the original shortcut's action key is pressed again, we send the target shortcut's action key or the target key again (or for disable we just suppress the event). - [**Case 2:**](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L435-L461) If the original shortcut's action key is pressed again, we send the target shortcut's action key or the target key again (or for disable we just suppress the event).
@@ -57,9 +57,9 @@ This file contains documentation for all the methods involved in key/shortcut re
## HandleAppSpecificShortcutRemapEvent ## HandleAppSpecificShortcutRemapEvent
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L754-L809) is used for handling app-specific shortcut to shortcut and shortcut to key remaps. The general logic is as follows: [This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L754-L809) is used for handling app-specific shortcut to shortcut and shortcut to key remaps. The general logic is as follows:
- Check if the `dwExtraInfo` field is set to `KEYBOARDMANAGER_SHORTCUT_FLAG`. This indicates that the key event was generated by the KBM shortcut remap method using `SendInput`. This ensures that we don't read events generated by the shortcut remap method, but we still read events which are generated by the key remap method. - Check if the `dwExtraInfo` field is set to `KEYBOARDMANAGER_SHORTCUT_FLAG`. This indicates that the key event was generated by the KBM shortcut remap method using `SendInput`. This ensures that we don't read events generated by the shortcut remap method, but we still read events which are generated by the key remap method.
- Get the name of the process in the foreground. This is done using `GetCurrentApplication` which uses `GetForegroundWindow` to get the window handle and `get_process_path` from the common lib. This approach can fail for UWP apps in full screen, so for that scenario we use the `GetGUIThreadInfo` approach to find the correct window handle, and hence the correct process name. This method is described in more detail [here](keyboardmanagercommon.md#Foreground-app-detection) - Get the name of the process in the foreground. This is done using `GetCurrentApplication` which uses `GetForegroundWindow` to get the window handle and `get_process_path` from the common lib. This approach can fail for UWP apps in full screen, so for that scenario we use the `GetGUIThreadInfo` approach to find the correct window handle, and hence the correct process name. This method is [described in more detail](keyboardmanagercommon.md#Foreground-app-detection)
- By checking `KeyboardManagerState.GetActivatedApp` we check if an app-specific shortcut is currently invoked. If so, we consider this application to be the activated app. This is required because some shortcut remaps could cause the current app to lose focus and hence until the shortcut is completely released we should allow that remap to continue, otherwise the user could end up in a state where some keys do not get released. For example: remap <kbd>Ctrl+A</kbd> to <kbd>Alt+Tab</kbd> for Edge, when a user presses <kbd>Ctrl+A</kbd> the window loses focus as <kbd>Alt+Tab</kbd> gets executed. - By checking `KeyboardManagerState.GetActivatedApp` we check if an app-specific shortcut is currently invoked. If so, we consider this application to be the activated app. This is required because some shortcut remaps could cause the current app to lose focus and hence until the shortcut is completely released we should allow that remap to continue, otherwise the user could end up in a state where some keys do not get released. For example: remap <kbd>Ctrl+A</kbd> to <kbd>Alt+Tab</kbd> for Edge, when a user presses <kbd>Ctrl+A</kbd> the window loses focus as <kbd>Alt+Tab</kbd> gets executed.
- If there is no app-specific shortcut currently invoked, we check if the foreground process is present in the list of app-specific remaps, either with or without the file extension and case insensitive. If it is, this is considered to be the activated app. - If there is no app-specific shortcut currently invoked, we check if the foreground process is present in the list of app-specific remaps, either with or without the file extension and case-insensitive. If it is, this is considered to be the activated app.
- Call `HandleShortcutRemapEvent` with the `activatedApp` argument so that app-specific shortcut remapping takes place if it applies for the current key event. - Call `HandleShortcutRemapEvent` with the `activatedApp` argument so that app-specific shortcut remapping takes place if it applies for the current key event.
## HandleSingleKeyToggleToModEvent (Obsolete - Code from PoC which is commented out) ## HandleSingleKeyToggleToModEvent (Obsolete - Code from PoC which is commented out)
@@ -81,4 +81,4 @@ The [`MockedInput`](https://github.com/microsoft/PowerToys/blob/main/src/modules
- For modifiers the behavior is slightly different as if the key state of the L/R version is modified, it should also modify the common version, and if a common version is released, it should release both the L and R versions. - For modifiers the behavior is slightly different as if the key state of the L/R version is modified, it should also modify the common version, and if a common version is released, it should release both the L and R versions.
### Tests for single key remaps and shortcut remaps ### Tests for single key remaps and shortcut remaps
Using the MockedInput handler, all the expected (and known) key scenarios that can occur for while pressing a [remapped key](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/SingleKeyRemappingTests.cpp) or [remapped shortcut](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/OSLevelShortcutRemappingTests.cpp) are tested. The foreground app behavior which is specific to app-specific shortcuts is tested [here](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/AppSpecificShortcutRemappingTests.cpp). Using the MockedInput handler, all the expected (and known) key scenarios that can occur for while pressing a [remapped key](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/SingleKeyRemappingTests.cpp) or [remapped shortcut](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/OSLevelShortcutRemappingTests.cpp) are tested. The foreground app behavior which is specific to app-specific shortcuts is tested in [AppSpecificShortcutRemappingTests.cpp](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/AppSpecificShortcutRemappingTests.cpp).

View File

@@ -27,14 +27,14 @@ This file contains the documentation for the KeyboardManager PowerToy module whi
The `KeyboardManager` module has [3 main class members](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L54-L61): The `KeyboardManager` module has [3 main class members](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L54-L61):
- A static pointer to the current object of `KeyboardManager`. This is required for using the `KeyboardManager` object in the low level keyboard hook handler as that method must be static. This is described in more detail in [this section](#Low-level-keyboard-hook-handler). - A static pointer to the current object of `KeyboardManager`. This is required for using the `KeyboardManager` object in the low level keyboard hook handler as that method must be static. This is described in more detail in [this section](#Low-level-keyboard-hook-handler).
- An object of type `Input`, which is used for all the operations that involving getting or setting keyboard states. This is wrapped in an object to allow testing the remapping methods. - An object of type `Input`, which is used for all the operations that involving getting or setting keyboard states. This is wrapped in an object to allow testing the remapping methods.
- An object of type `KeyboardManagerState`. This object contains all the data related to remappings and is also used in the sense of a View Model as it used to communicate common data that is shared between the KBM UI and the backend. This class is described in more detail [here](keyboardmanagercommon.md#keyboardmanagerstate). - An object of type `KeyboardManagerState`. This object contains all the data related to remappings and is also used in the sense of a [View Model as it used to communicate common data that is shared between the KBM UI and the backend](keyboardmanagercommon.md#keyboardmanagerstate).
## Enable/Disable ## Enable/Disable
On enabling KBM, the low level keyboard hook is started, and it is unhooked on disable. This is done to allow users to manually restart KBM if some other application which registers a keyboard hook was launched after PowerToys, so that it can be brought back to the highest priority hook (as the last hook to be registered receives the input first as mentioned [here](https://learn.microsoft.com/windows/win32/winmsg/about-hooks#hook-procedures)). On enabling KBM, the low level keyboard hook is started, and it is unhooked on disable. This is done to allow users to manually restart KBM if some other application which registers a keyboard hook was launched after PowerToys, so that it can be brought back to the highest priority hook (as the [last hook to be registered receives the input first](https://learn.microsoft.com/windows/win32/winmsg/about-hooks#hook-procedures)).
In addition to stopping the hook, any active KBM UI windows are also closed on disabling. This is done because the KBM UI uses the same keyboard hook for the Type button where you can type a key/shortcut, so if KBM is disabled the windows would not be completely functional. In addition to stopping the hook, any active KBM UI windows are also closed on disabling. This is done because the KBM UI uses the same keyboard hook for the Type button where you can type a key/shortcut, so if KBM is disabled the windows would not be completely functional.
The enable/disable code can be found [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L301-L322) The [enable/disable code can be found in dllmain.cpp](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L301-L322)
## Settings format ## Settings format
KBM uses two sets of settings files. KBM uses two sets of settings files.
@@ -101,10 +101,10 @@ KBM uses two sets of settings files.
- `originalKeys` stores the key/shortcut which is to be pressed for the remap, and `newKeys` stores the key/shortcut which is to be executed. - `originalKeys` stores the key/shortcut which is to be pressed for the remap, and `newKeys` stores the key/shortcut which is to be executed.
- Both contain semi-colon separated virtual key codes. For `remapKeys`, `originalKeys` must have only one key code, whereas for `remapShortcuts` it must have at least two key codes. - Both contain semi-colon separated virtual key codes. For `remapKeys`, `originalKeys` must have only one key code, whereas for `remapShortcuts` it must have at least two key codes.
- `inProcess` sub-key was added in `remapKeys` because there was a possibility of adding the registry based remapping approach (used by [SharpKeys](https://github.com/randyrants/sharpkeys)), so that would be under a separate sub-key while `inProcess` would be for keyboard hook based remaps. This was deprioritized as there weren't enough requests for it. - `inProcess` sub-key was added in `remapKeys` because there was a possibility of adding the registry based remapping approach (used by [SharpKeys](https://github.com/randyrants/sharpkeys)), so that would be under a separate sub-key while `inProcess` would be for keyboard hook based remaps. This was deprioritized as there weren't enough requests for it.
- `remapShortcuts` is split into `global` and `appSpecific`, where `global` remaps would apply to all applications, whereas `appSpecific` would apply on when the `targetApp` is in focus. `targetApp` must be the process name of the app (with or without it's extension), e.g. `msedge` or `msedge.exe` for Microsoft Edge. - `remapShortcuts` is split into `global` and `appSpecific`, where `global` remaps would apply to all applications, whereas `appSpecific` would apply on when the `targetApp` is in focus. `targetApp` must be the process name of the app (with or without its extension), e.g. `msedge` or `msedge.exe` for Microsoft Edge.
## Loading settings ## Loading settings
KBM settings are loaded only on the C++ side only at start up, in the [constructor](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L67-L68). The settings file may get modified from the KBM UI on applying new remappings, but the file is not read again. The files are read from the PowerToys Settings process whenever a change is made to the file (using a FileWatcher) or whenever the KBM page is opened. The settings are updated only when the user presses the OK button from either of the Remap Keys or Remap Shortcuts windows. This is described in more detail [here](keyboardmanagerui.md#ok-and-cancel-button). KBM settings are loaded only on the C++ side only at start up, in the [constructor](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L67-L68). The settings file may get modified from the KBM UI on applying new remappings, but the file is not read again. The files are read from the PowerToys Settings process whenever a change is made to the file (using a FileWatcher) or whenever the KBM page is opened. The settings are updated only when the user presses the OK button from either of the Remap Keys or Remap Shortcuts windows. This is described in more detail [keyboardmanagerui: OK and Cancel button](keyboardmanagerui.md#ok-and-cancel-button).
## Low level keyboard hook handler ## Low level keyboard hook handler
Since the [`hook_proc`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L330-L349) cannot be a member function in the class, this is declared `static` and a `static pointer` to the `KeyboardManager` project is used ([`keyboardmanager_object_ptr`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L54-L55)). Since the [`hook_proc`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L330-L349) cannot be a member function in the class, this is declared `static` and a `static pointer` to the `KeyboardManager` project is used ([`keyboardmanager_object_ptr`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L54-L55)).
@@ -117,22 +117,23 @@ As seen in the code for `hook_proc`, similar to other keyboard hooks in PowerToy
The [`HandleKeyboardHookEvent`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L384-L458) is the method which calls the corresponding remapping methods in the required order. The following checks are executed in order: The [`HandleKeyboardHookEvent`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L384-L458) is the method which calls the corresponding remapping methods in the required order. The following checks are executed in order:
- **`KeyboardManagerState.AreRemappingsEnabled`:** This returns false while the KBM remap tables are getting updated. If it is in this state, `HandleKeyboardHookEvent` returns `0`, i.e. the key event is not suppressed and is forwarded normally. - **`KeyboardManagerState.AreRemappingsEnabled`:** This returns false while the KBM remap tables are getting updated. If it is in this state, `HandleKeyboardHookEvent` returns `0`, i.e. the key event is not suppressed and is forwarded normally.
- **Check for `KEYBOARDMANAGER_SUPPRESS_FLAG`:** If the key event has the suppress flag, the method returns 1 to suppress the key event. - **Check for `KEYBOARDMANAGER_SUPPRESS_FLAG`:** If the key event has the suppress flag, the method returns 1 to suppress the key event.
- **[`KeyboardManagerState.DetectSingleRemapKeyUIBackend`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L399-L408):** This method is used for handling hook operations for the single key Type UI in the Remap keys window. If the Remap keys window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the left column Type button is clicked on the Remap keys window and the window is in focus, then the key event is suppressed and the UI is updated with the latest key from the recent key events. This method is described in more detail [here](keyboardmanagercommon.md#DetectSingleRemapKeyUIBackend-and-DetectShortcutUIBackend). - **[`KeyboardManagerState.DetectSingleRemapKeyUIBackend`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L399-L408):** This method is used for handling hook operations for the single key Type UI in the Remap keys window. If the Remap keys window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the left column Type button is clicked on the Remap keys window and the window is in focus, then the key event is suppressed and the UI is updated with the latest key from the recent key events. This method is described in more detail in [DetectSingleRemapKeyUIBackend and DetectShortcutUIBackend](keyboardmanagercommon.md#DetectSingleRemapKeyUIBackend-and-DetectShortcutUIBackend).
- **[`KeyboardManagerState.DetectShortcutUIBackend(data, true)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L410-L419):** This method is used for handling hook operations for the shortcut Type UI in the Remap keys window (when `isRemapKey` arg is `true`). If the Remap keys window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the right column Type button is clicked on the Remap keys window and the window is in focus, then the key event is suppressed and the UI is updated with the shortcut from the recent key events. This method is described in more detail [here](keyboardmanagercommon.md#DetectSingleRemapKeyUIBackend-and-DetectShortcutUIBackend). - **[`KeyboardManagerState.DetectShortcutUIBackend(data, true)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L410-L419):** This method is used for handling hook operations for the shortcut Type UI in the Remap keys window (when `isRemapKey` arg is `true`). If the Remap keys window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the right column Type button is clicked on the Remap keys window and the window is in focus, then the key event is suppressed and the UI is updated with the shortcut from the recent key events. This method is also described in more detail in [DetectSingleRemapKeyUIBackend and DetectShortcutUIBackend](keyboardmanagercommon.md#DetectSingleRemapKeyUIBackend-and-DetectShortcutUIBackend).
- **`HandleSingleKeyRemapEvent`:** This method handles the single key remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail [here](keyboardeventhandlers.md#HandleSingleKeyRemapEvent). - **`HandleSingleKeyRemapEvent`:** This method handles the single key remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail in [HandleSingleKeyRemapEvent](keyboardeventhandlers.md#HandleSingleKeyRemapEvent).
- **[`KeyboardManagerState.DetectShortcutUIBackend(data, false)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L430-L439):** This method is used for handling hook operations for the shortcut Type UI in the Remap shortcuts window (when `isRemapKey` arg is `false`). If the Remap shortcuts window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the Type button is clicked on the Remap shortcuts window and the window is in focus, then the key event is suppressed and the UI is updated with the shortcut from the recent key events. **Since this is executed after the single key remap method, all single key remappings are applied when the user is on the Remap shortcuts window.** - **[`KeyboardManagerState.DetectShortcutUIBackend(data, false)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L430-L439):** This method is used for handling hook operations for the shortcut Type UI in the Remap shortcuts window (when `isRemapKey` arg is `false`). If the Remap shortcuts window is open, then `HandleKeyboardHookEvent` returns `0` and the key event is forwarded normally. If the Type button is clicked on the Remap shortcuts window and the window is in focus, then the key event is suppressed and the UI is updated with the shortcut from the recent key events. **Since this is executed after the single key remap method, all single key remappings are applied when the user is on the Remap shortcuts window.**
- **`HandleAppSpecificShortcutRemapEvent`:** This method handles the app-specific shortcut remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail [here](keyboardeventhandlers.md#HandleAppSpecificShortcutRemapEvent). **Since this is executed after the single key remap method, single key remappings have precedence over shortcut remaps and are correspondingly reflected in shortcut remaps.** - **`HandleAppSpecificShortcutRemapEvent`:** This method handles the app-specific shortcut remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail in [HandleAppSpecificShortcutRemapEvent](keyboardeventhandlers.md#HandleAppSpecificShortcutRemapEvent). **Since this is executed after the single key remap method, single key remappings have precedence over shortcut remaps and are correspondingly reflected in shortcut remaps.**
- **`HandleOSLevelShortcutRemapEvent`:** This method handles the global shortcut remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail [here](keyboardeventhandlers.md#HandleOSLevelShortcutRemapEvent). The app-specific remap method is executed before this because if a shortcut is remapped to different keys/shortcuts for a particular app and globally, the app-specific variant should be preferred if that app is in focus. **Since this is executed after the single key remap method, single key remappings have precedence over shortcut remaps and are correspondingly reflected in shortcut remaps.** - **`HandleOSLevelShortcutRemapEvent`:** This method handles the global shortcut remap logic. If a remapping takes place, the key event is suppressed. This method is described in more detail under [HandleOSLevelShortcutRemapEvent](keyboardeventhandlers.md#HandleOSLevelShortcutRemapEvent). The app-specific remap method is executed before this because if a shortcut is remapped to different keys/shortcuts for a particular app and globally, the app-specific variant should be preferred if that app is in focus. **Since this is executed after the single key remap method, single key remappings have precedence over shortcut remaps and are correspondingly reflected in shortcut remaps.**
**Note:** Single key remaps need to be executed before shortcut remaps, because otherwise there can be several logical issues. For example if a user has Ctrl remapped to X and Ctrl+A remapped to Y, we can't detect Ctrl+A because the moment Ctrl is pressed it would be remapped to X before the system ever sees Ctrl+A. This is why the design decision was made to separate Remap keys and Remap shortcuts, and all key remaps are reflected in the shortcut remaps. **Note:** Single key remaps need to be executed before shortcut remaps, because otherwise there can be several logical issues. For example if a user has Ctrl remapped to X and Ctrl+A remapped to Y, we can't detect Ctrl+A because the moment Ctrl is pressed it would be remapped to X before the system ever sees Ctrl+A. This is why the design decision was made to separate Remap keys and Remap shortcuts, and all key remaps are reflected in the shortcut remaps.
## Custom Action to launch KBM UI ## Custom Action to launch KBM UI
KBM uses the [`call_custom_action`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L249-L280) method from the `PowertoyModuleIface` in order to launch the KBM UI when the user clicks the Remap a key or Remap a shortcut button from the KBM settings page. On clicking the button, we check if there is already any active KBM UI window, and if there is it is brought to the foreground. If not, the corresponding KBM UI window is launched on a separate detached thread. The UI is described in more detail [here](keyboardmanagerui.md). KBM uses the [`call_custom_action`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L249-L280) method from the `PowertoyModuleIface` in order to launch the KBM UI when the user clicks the Remap a key or Remap a shortcut button from the KBM settings page. On clicking the button, we check if there is already any active KBM UI window, and if there is it is brought to the foreground. If not, the corresponding KBM UI window is launched on a separate detached thread. The UI is described in more detail in [Keyboard Manager UI](keyboardmanagerui.md).
## SendInput Special Scenarios ## SendInput Special Scenarios
### Extended keys ### Extended keys
Certain keys such as the arrow keys, <kbd>right Ctrl/Alt</kbd>, and <kbd>Del/Home/Ins</kbd>, etc need to be sent with the `KEYEVENTF_EXTENDEDKEY` flag because otherwise the NumPad versions get sent, which can cause weird behavior when NumLock is on. The code can be found [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L190-L194) and the list of extended keys in code can be found [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L73-L98). Docs about extended keys can be found [here](https://learn.microsoft.com/windows/win32/inputdev/about-keyboard-input#extended-key-flag). Certain keys such as the arrow keys, <kbd>right Ctrl/Alt</kbd>, and <kbd>Del/Home/Ins</kbd>, etc need to be sent with the `KEYEVENTF_EXTENDEDKEY` flag because otherwise the NumPad versions get sent, which can cause weird behavior when NumLock is on. The code can be found where [`SetKeyEvent` checks `IsExtendedKey(keyCode)`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L190-L194) and the list of extended keys in code can be found in [`IsExtendedKey`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L73-L98). Docs about extended keys can be found in [Keyboard Input Overview: Extended-Key Flag
](https://learn.microsoft.com/windows/win32/inputdev/about-keyboard-input#extended-key-flag).
The weird behavior that is caused by this can be found at these issues: The weird behavior that is caused by this can be found at these issues:
- https://github.com/microsoft/PowerToys/issues/3478 - https://github.com/microsoft/PowerToys/issues/3478
@@ -140,7 +141,7 @@ The weird behavior that is caused by this can be found at these issues:
- https://github.com/microsoft/PowerToys/issues/3981 - https://github.com/microsoft/PowerToys/issues/3981
### Scan code ### Scan code
Certain applications (such as Windows Terminal) may filter out key events which are set to scan code 0. Even though the `KEYEVENTF_SCANCODE` flag is not set, the `wScan` field is still sent, which defaults to 0. To avoid this issue we use the `MapVirtualKey` API to find the scan code from the virtual key code. Code can be found [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L196-L198). Certain applications (such as Windows Terminal) may filter out key events which are set to scan code 0. Even though the `KEYEVENTF_SCANCODE` flag is not set, the `wScan` field is still sent, which defaults to 0. To avoid this issue we use the `MapVirtualKey` API to find the scan code from the virtual key code. Code can be found in [`SetKeyEvent`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L196-L198).
## Special Scenarios ## Special Scenarios
Since we are using low level keyboard hooks and not actual OS level input handling certain scenarios with input require workarounds as do they not interact well with the OS input logic directly. These are covered in the sub-sections below. Since we are using low level keyboard hooks and not actual OS level input handling certain scenarios with input require workarounds as do they not interact well with the OS input logic directly. These are covered in the sub-sections below.
@@ -156,7 +157,7 @@ The dummy key event is currently used in the following places (the linked code s
- https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L509-L510 - https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L509-L510
### Suppressing Num Lock in a keyboard hook ### Suppressing Num Lock in a keyboard hook
The <kbd>Num Lock</kbd> key state is updated by the OS before it is intercepted by low level hooks. This causes the issue that even if you suppress a <kbd>Num Lock</kbd> key event, <kbd>Num Lock</kbd> will still get toggled. In order to work around this, in the [`hook_proc`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L340-L344) whenever we suppress a <kbd>Num Lock</kbd> key down event, we send an additional <kbd>Num Lock</kbd> key up followed by key down so that the <kbd>Num Lock</kbd> state is reverted to it's previous value before the suppressed event. These are sent with a `KEYBOARDMANAGER_SUPPRESS_FLAG` in the `dwExtraInfo` field, so that we suppress them at the start of the hook (see code [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L811-L825)). Since these events will update the <kbd>Num Lock</kbd> state before the low level hooks, by suppressing them we ensure that these are not sent to any other hooks/applications and hence are only processed by the OS. The <kbd>Num Lock</kbd> key state is updated by the OS before it is intercepted by low level hooks. This causes the issue that even if you suppress a <kbd>Num Lock</kbd> key event, <kbd>Num Lock</kbd> will still get toggled. In order to work around this, in the [`hook_proc`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L340-L344) whenever we suppress a <kbd>Num Lock</kbd> key down event, we send an additional <kbd>Num Lock</kbd> key up followed by key down so that the <kbd>Num Lock</kbd> state is reverted to its previous value before the suppressed event. These are sent with a `KEYBOARDMANAGER_SUPPRESS_FLAG` in the `dwExtraInfo` field, so that we suppress them at the start of the hook (see code in [`SetNumLockToPreviousState`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L811-L825)). Since these events will update the <kbd>Num Lock</kbd> state before the low level hooks, by suppressing them we ensure that these are not sent to any other hooks/applications and hence are only processed by the OS.
This assumes that KBM is the last hook to be registered (since another hook-based app like AutoHotkey could remap NumLock to some other key which could mess up this logic). This assumes that KBM is the last hook to be registered (since another hook-based app like AutoHotkey could remap NumLock to some other key which could mess up this logic).
@@ -167,7 +168,7 @@ While using Japanese IME on Windows, shortcuts like <kbd>Shift/Alt/Ctrl</kbd> +
These shortcuts are detected before low level hooks, and hence cause issues while remapping <kbd>Caps Lock</kbd> to <kbd>Shift/Alt/Ctrl</kbd> or vice-versa, as there could be an intermediate state where the system detects both the keys as being pressed. This results in a state where the modifier key does not get released since the OS suppresses the key up messages before they reach the low level hooks. These shortcuts are detected before low level hooks, and hence cause issues while remapping <kbd>Caps Lock</kbd> to <kbd>Shift/Alt/Ctrl</kbd> or vice-versa, as there could be an intermediate state where the system detects both the keys as being pressed. This results in a state where the modifier key does not get released since the OS suppresses the key up messages before they reach the low level hooks.
In order to work around this when a key down for the modifier is being processed, we send a key up for the modifier key with the `KEYBOARDMANAGER_SUPPRESS_FLAG` in the `dwExtraInfo` field, so that we suppress them at the start of the hook, and this key event would only be processed by the OS, without getting forwarded to other hooks/apps. The approach is described in more detail at [this comment](https://github.com/microsoft/PowerToys/issues/3397#issuecomment-640136416), as discussed with the AutoHotkey team. The code for the workaround can be found [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L827-L846). Tests for these scenarios have also been added at: In order to work around this when a key down for the modifier is being processed, we send a key up for the modifier key with the `KEYBOARDMANAGER_SUPPRESS_FLAG` in the `dwExtraInfo` field, so that we suppress them at the start of the hook, and this key event would only be processed by the OS, without getting forwarded to other hooks/apps. The approach is described in more detail at [this comment](https://github.com/microsoft/PowerToys/issues/3397#issuecomment-640136416), as discussed with the AutoHotkey team. The code for the workaround can be found in [`ResetIfModifierKeyForLowerLevelKeyHandlers`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/KeyboardEventHandlers.cpp#L827-L846). Tests for these scenarios have also been added at:
- [Tests for workaround on single key remaps](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/SingleKeyRemappingTests.cpp#L110-L219) - [Tests for workaround on single key remaps](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/SingleKeyRemappingTests.cpp#L110-L219)
- [Tests for workaround on shortcut remaps](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/OSLevelShortcutRemappingTests.cpp#L1935-L2144) - [Tests for workaround on shortcut remaps](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/OSLevelShortcutRemappingTests.cpp#L1935-L2144)
@@ -189,8 +190,8 @@ Using a driver approach has the benefit of not depending on precedence orders as
## Telemetry ## Telemetry
Keyboard Manager emits the following telemetry events (implemented in [trace.h](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.h) and [trace.cpp](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.cpp)): Keyboard Manager emits the following telemetry events (implemented in [trace.h](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.h) and [trace.cpp](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/trace.cpp)):
- **`KeyboardManager_EnableKeyboardManager`:** Logs a `boolean` value storing the KBM toggle state. It is logged whenever KBM is enabled or disabled (emitted [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L305-L316)). - **`KeyboardManager_EnableKeyboardManager`:** Logs a `boolean` value storing the KBM toggle state. It is logged whenever KBM is enabled or disabled (emitted in [`enable`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L305-L306) and [`disable`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L315-L316)).
- **`KeyboardManager_KeyRemapCount`:** Logs the number of key to key and key to shortcut remaps (i.e. all the remaps on the Remap a key window). This gets logged on saving new settings in the Remap a key window (emitted [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L159-L163)). - **`KeyboardManager_KeyRemapCount`:** Logs the number of key to key and key to shortcut remaps (i.e. all the remaps on the Remap a key window). This gets logged on saving new settings in the Remap a key window (emitted at [the end of `ApplySingleKeyRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L159-L163)).
- **`KeyboardManager_OSLevelShortcutRemapCount`:** Logs the number of global shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the Remap a shortcut window (emitted [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L220)). - **`KeyboardManager_OSLevelShortcutRemapCount`:** Logs the number of global shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the Remap a shortcut window (emitted at [the end of `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L220)).
- **`KeyboardManager_AppSpecificShortcutRemapCount`:** Logs the number of app-specific shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the Remap a shortcut window (emitted [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L221)). - **`KeyboardManager_AppSpecificShortcutRemapCount`:** Logs the number of app-specific shortcut to shortcut and shortcut to key remaps. This gets logged on saving new settings in the Remap a shortcut window (emitted [after calling `OSLevelShortcutRemapCount` in `ApplyShortcutRemappings`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L221)).
- **`KeyboardManager_Error`:** Logs the occurrence of an error in KBM with the name of the method, error code and the corresponding error message. This is currently used only for logging `SetWindowsHookEx` failures (emitted [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L364-L369)). - **`KeyboardManager_Error`:** Logs the occurrence of an error in KBM with the name of the method, error code and the corresponding error message. This is currently used only for logging `SetWindowsHookEx` failures (emitted [at the end of `start_lowlevel_keyboard_hook`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/dll/dllmain.cpp#L364-L369)).

View File

@@ -37,7 +37,7 @@ The [`SaveConfigToFile`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a
To prevent the UI thread and low level hook thread from concurrently accessing the remap tables we use an [`atomic bool` variable](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.h#L91-L92), which is set to `true` while the tables are getting updated. When this is `true` the hook will skip all remappings. Use of mutexes in the hook were removed to prevent reentrant mutex bugs. To prevent the UI thread and low level hook thread from concurrently accessing the remap tables we use an [`atomic bool` variable](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.h#L91-L92), which is set to `true` while the tables are getting updated. When this is `true` the hook will skip all remappings. Use of mutexes in the hook were removed to prevent reentrant mutex bugs.
## KeyDelay ## KeyDelay
[This class](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/KeyDelay.cpp) implements a queue based approach for processing key events and based on the time difference between key down and key up events [executes separate methods for `ShortPress`, `LongPress` or `LongPressReleased`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.h#L69-L72). The class is used for the hold Enter/Esc functionality required for making the Type window accessible and prevent keyboard traps (see [this](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L273-L292) for an example of it's usage). The `KeyEvents` are added to the queue from the hook thread of KBM, and a separate [`DelayThread`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.cpp#L142-L166) is used to process the key events by checking the `time` member in the key event. The thresholds for short vs long press and hold wait timeouts are `static` constants, but if the module is extended for other purposes these could be made into arguments. The [KeyDelay class](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/KeyDelay.cpp) implements a queue based approach for processing key events and based on the time difference between key down and key up events [executes separate methods for `ShortPress`, `LongPress` or `LongPressReleased`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.h#L69-L72). The class is used for the hold Enter/Esc functionality required for making the Type window accessible and prevent keyboard traps (see [this call to `keyboardManagerState.RegisterKeyDelay`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/SingleKeyRemapControl.cpp#L273-L292) for an example of its usage). The `KeyEvents` are added to the queue from the hook thread of KBM, and a separate [`DelayThread`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.cpp#L142-L166) is used to process the key events by checking the `time` member in the key event. The thresholds for short vs long press and hold wait timeouts are `static` constants, but if the module is extended for other purposes these could be made into arguments.
**Note:** [Deletion of the `KeyDelay`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.cpp#L4-L12) object should never be called from the `DelayThread` i.e. from within one of the 3 handlers, as it can re-enter the mutex and would lead to a deadlock. This can be avoided by either deleting it on a separate thread or as done in the KBM UI, on the dispatcher thread. See [this PR](https://github.com/microsoft/PowerToys/pull/6959#issue-496583547) for more details on this issue. **Note:** [Deletion of the `KeyDelay`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyDelay.cpp#L4-L12) object should never be called from the `DelayThread` i.e. from within one of the 3 handlers, as it can re-enter the mutex and would lead to a deadlock. This can be avoided by either deleting it on a separate thread or as done in the KBM UI, on the dispatcher thread. See [this PR](https://github.com/microsoft/PowerToys/pull/6959#issue-496583547) for more details on this issue.
@@ -45,16 +45,16 @@ To prevent the UI thread and low level hook thread from concurrently accessing t
The [`Shortcut` class](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/Shortcut.h) is a data structure for storing key combinations which are valid shortcuts and it contains several methods which are used for shortcut specific operations. [`RemapShortcut`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/RemapShortcut.h) consists of a shortcut/key union (`std::variant`), along with other boolean flags which are required on the hook side for storing any relevant keyboard states mid-execution. The [`Shortcut` class](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/Shortcut.h) is a data structure for storing key combinations which are valid shortcuts and it contains several methods which are used for shortcut specific operations. [`RemapShortcut`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/RemapShortcut.h) consists of a shortcut/key union (`std::variant`), along with other boolean flags which are required on the hook side for storing any relevant keyboard states mid-execution.
### IsKeyboardStateClearExceptShortcut ### IsKeyboardStateClearExceptShortcut
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L665-L813) is used by the `HandleShortcutRemapEvent` to check if any other keys on the keyboard have been pressed apart from the keys in the shortcut. This is required because shortcut to shortcut remaps should not be applied if the shortcut is pressed with other keys. The method iterates over all the possible key codes, except any keys that are considered reserved, unassigned, OEM-specific or undefined, as well as mouse buttons (see list [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L628-L663)). [This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L665-L813) is used by the `HandleShortcutRemapEvent` to check if any other keys on the keyboard have been pressed apart from the keys in the shortcut. This is required because shortcut to shortcut remaps should not be applied if the shortcut is pressed with other keys. The method iterates over all the possible key codes, except any keys that are considered reserved, unassigned, OEM-specific or undefined, as well as mouse buttons (see [list in `IgnoreKeyCode`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L628-L663)).
### CheckModifiersKeyboardState ### CheckModifiersKeyboardState
[This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L517-L614) uses `GetVirtualKeyState` (internally calls `GetAsyncKeyState` in production code), to check if all the modifiers of the current shortcut are being pressed. Since Win doesn't have a non-L/R key code we check this by checking both LWIN and RWIN. [This method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Shortcut.cpp#L517-L614) uses `GetVirtualKeyState` (internally calls `GetAsyncKeyState` in production code), to check if all the modifiers of the current shortcut are being pressed. Since Win doesn't have a non-L/R key code we check this by checking both LWIN and RWIN.
### Tests ### Tests
Tests for some methods in the `Shortcut` class can be found [here](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/ShortcutTests.cpp). Tests for some methods in the `Shortcut` class can be found in [`OSLevelShortcutRemappingTests.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEngineTest/OSLevelShortcutRemappingTests.cpp) and [`AppSpecificShortcutRemappingTests.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEngineTest/AppSpecificShortcutRemappingTests.cpp).
## Helpers ## Helpers
[This namespace](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/Helpers.cpp) has any methods which are used across either UI or the backend which aren't specific to either. Some of these methods have tests [here](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/SetKeyEventTests.cpp). [This namespace](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/common/Helpers.cpp) has any methods which are used across either UI or the backend which aren't specific to either. Some of these methods have tests in [`SetKeyEventTests.cpp`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/KeyboardManagerEngineTest/SetKeyEventTests.cpp).
### Foreground App Detection ### Foreground App Detection
[`GetCurrentApplication`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L226-L268) is used for detecting the foreground process for App-specific shortcuts. The logic is very similar to that used for FZ's app exception feature, involving `GetForegroundWindow` and `get_process_path`. The one additional case which has been added is for full-screen UWP apps, where the above method fails and returns `ApplicationFrameHost.exe`. The [`GetFullscreenUWPWindowHandle`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L210-L224) uses `GetGUIThreadInfo` API to find the window linked to the GUI thread. This logic is based on [this stackoverflow answer](https://stackoverflow.com/questions/39702704/connecting-uwp-apps-hosted-by-applicationframehost-to-their-real-processes/55353165#55353165). [`GetCurrentApplication`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L226-L268) is used for detecting the foreground process for App-specific shortcuts. The logic is very similar to that used for FZ's app exception feature, involving `GetForegroundWindow` and `get_process_path`. The one additional case which has been added is for full-screen UWP apps, where the above method fails and returns `ApplicationFrameHost.exe`. The [`GetFullscreenUWPWindowHandle`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/Helpers.cpp#L210-L224) uses `GetGUIThreadInfo` API to find the window linked to the GUI thread. This logic is based on [this stackoverflow answer](https://stackoverflow.com/questions/39702704/connecting-uwp-apps-hosted-by-applicationframehost-to-their-real-processes/55353165#55353165).

View File

@@ -23,7 +23,7 @@ The KBM UI was originally implemented as a XAML Island, but in order to easily s
Mica is then achieved by calling [`BackdropMaterial::SetApplyToRootOrPageBackground()`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp#L388-L400) in both of the editor windows, or falls back to the `ApplicationPageBackgroundThemeBrush` background if Mica isn't available. Mica is then achieved by calling [`BackdropMaterial::SetApplyToRootOrPageBackground()`](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp#L388-L400) in both of the editor windows, or falls back to the `ApplicationPageBackgroundThemeBrush` background if Mica isn't available.
The UI was also updated to use WinUI 2.8 to match the look and feel of the Fluent design language of Windows 11 and the rest of PowerToys. There has been talk about [migrating the implementation to XAML files instead of code-behind](https://github.com/microsoft/PowerToys/issues/2027) and [utilizing WinUI 3 going forward](https://github.com/microsoft/PowerToys/issues/15870). More about the update can be read in [here](https://github.com/microsoft/PowerToys/pull/28473). The UI was also updated to use WinUI 2.8 to match the look and feel of the Fluent design language of Windows 11 and the rest of PowerToys. There has been talk about [migrating the implementation to XAML files instead of code-behind](https://github.com/microsoft/PowerToys/issues/2027) and [utilizing WinUI 3 going forward](https://github.com/microsoft/PowerToys/issues/15870). More about the update can be read in [[Keyboard Manager] Modernize the editor UI - PR#28473](https://github.com/microsoft/PowerToys/pull/28473).
[**Link to the original documentation**](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/doc/devdocs/modules/keyboardmanager/keyboardmanagerui.md#c-xaml-islands) [**Link to the original documentation**](https://github.com/microsoft/PowerToys/blob/b3f27057d43445abc59aa04405f7c24bb895a61c/doc/devdocs/modules/keyboardmanager/keyboardmanagerui.md#c-xaml-islands)
@@ -43,7 +43,7 @@ When the `EditKeyboardWindow`/`EditShortcutsWindow` is created, [we iterate thro
### OK and Cancel button ### OK and Cancel button
[On pressing the OK button](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L66-L89) in `EditKeyboardWindow`, first the [`CheckIfRemappingsAreValid` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L10-L44) is executed which performs basic validity checks on the current remappings in the remap buffer (`static SingleKeyRemapControl::singleKeyRemapBuffer`), such as if there are no NULL columns and none of the source keys are repeated. All other validity checks are assumed to happen while the user adds the remapping. If this is found to be invalid a ContentDialog is displayed which shows that some remappings are invalid and if the user proceeds only the valid ones will be applied. If it is valid [`GetOrphanedKeys`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L46-L75) is executed which checks if any keys are orphaned (i.e. the key has been remapped and no other key has been remapped to it, so there is no way to send that key code), and a dialog is shown for notifying the user with a list of orphaned keys. After this the settings are [applied by adding it to the `KeyboardManagerState.singleKeyReMap` member](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L102-L164) and they are saved to the JSON file. `EditShortcutsWindow` differs slightly from this, as there is no orphaned keys check, and [on pressing OK](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp#L32-L47) both the global and app-specific shortcuts are validated and [updated](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L166-L223). [On pressing the OK button](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp#L66-L89) in `EditKeyboardWindow`, first the [`CheckIfRemappingsAreValid` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L10-L44) is executed which performs basic validity checks on the current remappings in the remap buffer (`static SingleKeyRemapControl::singleKeyRemapBuffer`), such as if there are no NULL columns and none of the source keys are repeated. All other validity checks are assumed to happen while the user adds the remapping. If this is found to be invalid a ContentDialog is displayed which shows that some remappings are invalid and if the user proceeds only the valid ones will be applied. If it is valid [`GetOrphanedKeys`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L46-L75) is executed which checks if any keys are orphaned (i.e. the key has been remapped and no other key has been remapped to it, so there is no way to send that key code), and a dialog is shown for notifying the user with a list of orphaned keys. After this the settings are [applied by adding it to the `KeyboardManagerState.singleKeyReMap` member](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L102-L164) and they are saved to the JSON file. `EditShortcutsWindow` differs slightly from this, as there is no orphaned keys check, and [on pressing OK](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp#L32-L47) both the global and app-specific shortcuts are validated and [updated](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/LoadingAndSavingRemappingHelper.cpp#L166-L223).
The code used for updating the remapping tables in `KeyboardManagerState` can be found [here](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L104-L183). For shortcut remaps, the `sortedKeys` vectors are updated and re-sorted whenever an element is added to them (like [this](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L135-L136)). The code used for updating the remapping tables in `KeyboardManagerState` can be found in [KeyboardManagerState.cpp lines 104-183](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L104-L183). For shortcut remaps, the `sortedKeys` vectors are updated and re-sorted whenever an element is added to them (like [this code in `KeyboardManagerState::AddOSLevelShortcut`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/common/KeyboardManagerState.cpp#L135-L136)).
On pressing OK (after confirmation dialogs) or Cancel, the window is closed and UI states are reset. On pressing OK (after confirmation dialogs) or Cancel, the window is closed and UI states are reset.
@@ -106,4 +106,9 @@ Unlike the Single Key handler, there is a different set of errors that can occur
**Note:** After updating the buffer we have [code to handle a special case](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L269-L279), which was required to prevent scenarios where a drop down can get deleted but the corresponding `KeyDropDownControl` object isn't deleted. The code checks if the drop down is still linked to the parent and accordingly deletes the `KeyDropDownControl` object from the vector. **Note:** After updating the buffer we have [code to handle a special case](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L269-L279), which was required to prevent scenarios where a drop down can get deleted but the corresponding `KeyDropDownControl` object isn't deleted. The code checks if the drop down is still linked to the parent and accordingly deletes the `KeyDropDownControl` object from the vector.
**IgnoreKeyToShortcutWarning special case:** [An additional](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L177-L181) check was added to ignore the Map to Same key error when an existing remapping is loaded. This was because a remapping like Ctrl->Ctrl+A has an intermediate step of Ctrl->Ctrl, which could lead to an error of invalid input, even though Ctrl+A is valid. The only way to actually add this is from the Type button or by adding them in a different order (like typing Shift+A and then changing Shift to Ctrl). Since the intermediate check could fail, this was causing the app to crash since the Xaml Island wouldn't be completely loaded at that point and the Flyout can't be displayed. [This](https://github.com/microsoft/PowerToys/issues/6695) is the linked issue which describes the repro scenario. **IgnoreKeyToShortcutWarning special case:**
[An additional](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/ui/KeyDropDownControl.cpp#L177-L181) check was added to ignore the Map to Same key error when an existing remapping is loaded.
This was because a remapping like Ctrl->Ctrl+A has an intermediate step of Ctrl->Ctrl, which could lead to an error of invalid input, even though Ctrl+A is valid.
The only way to actually add this is from the Type button or by adding them in a different order (like typing Shift+A and then changing Shift to Ctrl).
Since the intermediate check could fail, this was causing the app to crash since the Xaml Island wouldn't be completely loaded at that point and the Flyout can't be displayed.
[Issue #6695](https://github.com/microsoft/PowerToys/issues/6695) is the linked issue which describes the repro scenario.

View File

@@ -16,7 +16,7 @@ PowerToys Run UI is written in the WPF framework. The UI code is present in the
3. **[`ResultList.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml)**: This control implements the UI component for displaying results (marked in green in Fig 1). It consists of a `ListView` WPF control with a custom `ItemTemplate` to display application logo, name, tooltip text, and context menu. 3. **[`ResultList.xaml`](/src/modules/launcher/PowerLauncher/LauncherControl.xaml)**: This control implements the UI component for displaying results (marked in green in Fig 1). It consists of a `ListView` WPF control with a custom `ItemTemplate` to display application logo, name, tooltip text, and context menu.
## Data flow ## Data flow
The backend code is written using the `Model-View-ViewModel (MVVM)` structural design pattern. Plugins act as `Model` in this project. A detailed overview of the project's structure is given [here](/doc/devdocs/modules/launcher/project_structure.md). The backend code is written using the `Model-View-ViewModel (MVVM)` structural design pattern. Plugins act as `Model` in this project. A detailed overview of the project's structure is given in [Project Structure](/doc/devdocs/modules/launcher/project_structure.md).
#### Flow of data between UI(view) and ViewModels #### Flow of data between UI(view) and ViewModels
Data flow between View and ViewModel follows typical `MVVM` scheme. Properties in viewModels are bound to WPF controls and when these properties are updated, `INotifyPropertyChanged` handler is invoked, which in turn updates UI. The diagram below provides a rough sketch of the components involved. Data flow between View and ViewModel follows typical `MVVM` scheme. Properties in viewModels are bound to WPF controls and when these properties are updated, `INotifyPropertyChanged` handler is invoked, which in turn updates UI. The diagram below provides a rough sketch of the components involved.

View File

@@ -1,6 +1,6 @@
# Value Generator Plugin # Value Generator Plugin
The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs versions 1, 3, 4 and 5. The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs of version 1, 3, 4, 5, and 7.
![Image of Value Generator plugin](/doc/images/launcher/plugin/community.valuegenerator.png) ![Image of Value Generator plugin](/doc/images/launcher/plugin/community.valuegenerator.png)
@@ -34,7 +34,10 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs) ### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs)
- Utility class for generating or calculating GUIDs - Utility class for generating or calculating GUIDs
- Generating GUID versions 1 and 4 is done using builtin APIs. [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1 and `System.Guid.NewGuid()` for version 4 - Generating GUID versions 1, 4, and 7 is done using builtin APIs:
- [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1
- `System.Guid.NewGuid()` for version 4
- `System.Guid.CreateVersion7()` for version 7
- Versions 3 and 5 take two parameters, a namespace and a name - Versions 3 and 5 take two parameters, a namespace and a name
- The namespace must be a valid GUID or one of the [predefined ones](https://datatracker.ietf.org/doc/html/rfc4122#appendix-C) - The namespace must be a valid GUID or one of the [predefined ones](https://datatracker.ietf.org/doc/html/rfc4122#appendix-C)
- The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces - The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces

View File

@@ -96,7 +96,7 @@ The plugin uses only these interfaces (all inside the `Main.cs`):
#### Build Dependency #### Build Dependency
Access to PluginManager was needed to make this plugin work. Because of this a reference to PowerToys.PowerLauncher was needed. Access to PluginManager was needed to make this plugin work. Because of this a reference to PowerToys.PowerLauncher was needed.
Since History Plugin needs a reference to PowerToys.PowerLauncher, it can not be set as a dependency reference in PowerToys.PowerLauncher project (else a circular reference would exist). Since History Plugin needs a reference to PowerToys.PowerLauncher, it cannot be set as a dependency reference in PowerToys.PowerLauncher project (else a circular reference would exist).
This means that if you build PowerToys.PowerLauncher only it will not build History Plugin. You will need to manually build History Plugin at least once and again manually if you change it. This means that if you build PowerToys.PowerLauncher only it will not build History Plugin. You will need to manually build History Plugin at least once and again manually if you change it.
### Caching ### Caching

View File

@@ -32,7 +32,7 @@ Each plugin implements the `IPlugin` interface which comprises of the `Init()` a
### Score ### Score
- The user query is executed against each of the plugins and the result list view is updated with results from each of the plugins. - The user query is executed against each of the plugins and the result list view is updated with results from each of the plugins.
- The ordering of the results is based on the `Score` of each Result. - The ordering of the results is based on the `Score` of each Result.
- Each plugin assigns a score to a result based on it's relevance. The results with higher scores are displayed higher in the list view and vice versa. - Each plugin assigns a score to a result based on its relevance. The results with higher scores are displayed higher in the list view and vice versa.
## Plugin settings ## Plugin settings
Plugin settings that are editable from the settings are stored in `PowerToys Run\settings.json`. In the very first run, those settings are populated from plugin' `plugin.json` file. Unlike Wox we do not support multiple action keywords. Instead, we have `ActionKeyword` and `IsGlobal` options. Plugin settings that are editable from the settings are stored in `PowerToys Run\settings.json`. In the very first run, those settings are populated from plugin' `plugin.json` file. Unlike Wox we do not support multiple action keywords. Instead, we have `ActionKeyword` and `IsGlobal` options.

View File

@@ -23,7 +23,7 @@ There are broadly two different categories of applications:
5. Common start menu (Applications which are common to all users) 5. Common start menu (Applications which are common to all users)
8. Locations pointed to by the PATH environment variable. 8. Locations pointed to by the PATH environment variable.
- To prevent applications and shortcuts present in multiple locations from showing up as duplicate results, we consider apps with the same name, executable name and full path to be the same. - To prevent applications and shortcuts present in multiple locations from showing up as duplicate results, we consider apps with the same name, executable name and full path to be the same.
- The subtitle of the application result is set based on it's application type. It could be one of the following: - The subtitle of the application result is set based on its application type. It could be one of the following:
1. Lnk Shortcuts 1. Lnk Shortcuts
2. Appref files 2. Appref files
3. Internet shortcut - steam and epic games 3. Internet shortcut - steam and epic games

View File

@@ -36,7 +36,7 @@ The user can switch to the found windows, close them or kill their process.
| `SubtitleShowPid` | `false` | Show process id in subtitle | | `SubtitleShowPid` | `false` | Show process id in subtitle |
| `SubtitleShowDesktopName` | `true` | Show desktop name in subtitle (If two or more desktops exist) | | `SubtitleShowDesktopName` | `true` | Show desktop name in subtitle (If two or more desktops exist) |
| `ConfirmKillProcess` | `true` | Request confirmation when killing a process | | `ConfirmKillProcess` | `true` | Request confirmation when killing a process |
| `KillProcessTree` | `false` | Kill process and it's child processes | | `KillProcessTree` | `false` | Kill process and its child processes |
| `OpenAfterKillAndClose` | `false` | Stay open after closing windows and killing processes (Not working with kill process confirmation) | | `OpenAfterKillAndClose` | `false` | Stay open after closing windows and killing processes (Not working with kill process confirmation) |
| `HideKillProcessOnElevatedProcesses` | `false` | Hide "kill process" button if additional permissions required | | `HideKillProcessOnElevatedProcesses` | `false` | Hide "kill process" button if additional permissions required |
| `HideExplorerSettingInfo` | `false` | Hide Explorer process information | | `HideExplorerSettingInfo` | `false` | Hide Explorer process information |

View File

@@ -10,7 +10,7 @@ Fig 1. Project along with their dependencies in `PowerToys Run` ecosystem.
This is the startup project for the `PowerToys Run.` It is a WPF desktop application and follows the `Model-View-ViewModel (MVVM)` design pattern. Plugins play the role of `Model` and provide data to `ViewModel.` This is the startup project for the `PowerToys Run.` It is a WPF desktop application and follows the `Model-View-ViewModel (MVVM)` design pattern. Plugins play the role of `Model` and provide data to `ViewModel.`
#### [`PowerLauncher.Telemetry`](/src/modules/launcher/PowerLauncher.Telemetry) #### [`PowerLauncher.Telemetry`](/src/modules/launcher/PowerLauncher.Telemetry)
[`PowerLauncher.Telemetry`](/src/modules/launcher/PowerLauncher.Telemetry) is a .net core project that contains telemetry events generated by `PowerLauncher.` These events have been discussed in detail [here](/doc/devdocs/modules/launcher/telemetry.md). [`PowerLauncher.Telemetry`](/src/modules/launcher/PowerLauncher.Telemetry) is a .net core project that contains telemetry events generated by `PowerLauncher.` These events have been discussed in detail in [Launcher Telemetry](/doc/devdocs/modules/launcher/telemetry.md).
#### [`Wox.Core`](/src/modules/launcher/Wox.Core) #### [`Wox.Core`](/src/modules/launcher/Wox.Core)
[`Wox.Core`](/src/modules/launcher/Wox.Core) is a .net core project that contains helper classes required by the `PowerLauncher` project. Two major functionalities encapsulated in this project are [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) and [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) provides an interface for managing C# plugins. [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) decimate user-typed query string and creates a [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object. [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object contains the action keyword and cleaned query, which is then sent to all plugins. [`Wox.Core`](/src/modules/launcher/Wox.Core) is a .net core project that contains helper classes required by the `PowerLauncher` project. Two major functionalities encapsulated in this project are [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) and [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) [`PluginManager`](/src/modules/launcher/Wox.Core/Plugin/PluginManager.cs) provides an interface for managing C# plugins. [`Query Builder.`](/src/modules/launcher/Wox.Core/Plugin/QueryBuilder.cs) decimate user-typed query string and creates a [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object. [`Query`](/src/modules/launcher/Wox.Plugin/Query.cs) object contains the action keyword and cleaned query, which is then sent to all plugins.
@@ -19,6 +19,6 @@ This is the startup project for the `PowerToys Run.` It is a WPF desktop applica
[`Wox.Infrastructure`](/src/modules/launcher/Wox.Infrastructure) is a .net core project that contains helper classes required for image manipulation and storage by the `PowerLauncher` project and the plugins. [`ImageLoader.cs`](/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs) class is used to load icons for `Win32` program. It also provides caching functionality to speed up image loading for frequently queried programs. [`Wox.Infrastructure`](/src/modules/launcher/Wox.Infrastructure) is a .net core project that contains helper classes required for image manipulation and storage by the `PowerLauncher` project and the plugins. [`ImageLoader.cs`](/src/modules/launcher/Wox.Infrastructure/Image/ImageLoader.cs) class is used to load icons for `Win32` program. It also provides caching functionality to speed up image loading for frequently queried programs.
#### [`Wox.Plugin`](/src/modules/launcher/Wox.Plugin) #### [`Wox.Plugin`](/src/modules/launcher/Wox.Plugin)
[`Wox.Plugin`](/src/modules/launcher/Wox.Plugin) contains interfaces that facilitate communication between `PowerLauncher` and plugins. These interfaces have been discussed in detail [here](/doc/devdocs/modules/launcher/architecture.md#flow-of-data-between-viewmodels-and-pluginsmodel). It also contains a helper class for logging. [`Log.cs`](/src/modules/launcher/Wox.Plugin/Logger/Log.cs) provides an abstraction for logging error, information, and output to text files. These files are stored at `%userprofile%/appdata/local/microsoft/powertoys/powertoys run/Logs.` [`Wox.Plugin`](/src/modules/launcher/Wox.Plugin) contains interfaces that facilitate communication between `PowerLauncher` and plugins. These interfaces have been discussed in detail in [Flow of data between ViewModels and Plugins(Model)](/doc/devdocs/modules/launcher/architecture.md#flow-of-data-between-viewmodels-and-pluginsmodel). It also contains a helper class for logging. [`Log.cs`](/src/modules/launcher/Wox.Plugin/Logger/Log.cs) provides an abstraction for logging error, information, and output to text files. These files are stored at `%userprofile%/appdata/local/microsoft/powertoys/powertoys run/Logs.`

View File

@@ -5,7 +5,7 @@ Contains the executable starting point, initialization code and the list of know
Contains code for initializing and managing the PowerToy modules. `PowertoyModule` is a RAII-style holder for the `PowertoyModuleIface` pointer, which we got by [invoking module DLL's `powertoy_create` function](https://github.com/microsoft/PowerToys/blob/1760af50c8803588cb575167baae0439af38a9c1/src/runner/powertoy_module.cpp#L13-L24). Contains code for initializing and managing the PowerToy modules. `PowertoyModule` is a RAII-style holder for the `PowertoyModuleIface` pointer, which we got by [invoking module DLL's `powertoy_create` function](https://github.com/microsoft/PowerToys/blob/1760af50c8803588cb575167baae0439af38a9c1/src/runner/powertoy_module.cpp#L13-L24).
#### [`powertoys_events.cpp`](/src/runner/powertoys_events.cpp) #### [`powertoys_events.cpp`](/src/runner/powertoys_events.cpp)
Contains code that handles the various events listeners, and forwards those events to the PowerToys modules. You can learn more about the current event architecture [here](/doc/devdocs/shared-hooks.md). Contains code that handles the various events listeners, and forwards those events to the PowerToys modules. You can learn more about the current event architecture in [shared hooks](/doc/devdocs/shared-hooks.md).
#### [`lowlevel_keyboard_event.cpp`](/src/runner/lowlevel_keyboard_event.cpp) #### [`lowlevel_keyboard_event.cpp`](/src/runner/lowlevel_keyboard_event.cpp)
Contains code for registering the low level keyboard event hook that listens for keyboard events. Please note that `signal_event` is called from the main thread for this event. Contains code for registering the low level keyboard event hook that listens for keyboard events. Please note that `signal_event` is called from the main thread for this event.

View File

@@ -6,7 +6,7 @@
## PT Run ## PT Run
- Any changes to the UI are saved by the settings process in the `settings.json` file located within the `/Local/Microsoft/PowerToys/Launcher/` folder. - Any changes to the UI are saved by the settings process in the `settings.json` file located within the `/Local/Microsoft/PowerToys/Launcher/` folder.
- PT Run watches for any changes within this file and updates it's general settings or propagates the information to the plugins, depending on the type of information. - PT Run watches for any changes within this file and updates its general settings or propagates the information to the plugins, depending on the type of information.
Eg: The maximum number of results drop down updates the maximum number of rows in the results list which updates the general settings of PT Run whereas the drive detection checkbox details are dispatched to the indexer plugin. Eg: The maximum number of results drop down updates the maximum number of rows in the results list which updates the general settings of PT Run whereas the drive detection checkbox details are dispatched to the indexer plugin.
## Keyboard Manager ## Keyboard Manager

View File

@@ -1,5 +1,5 @@
# Overview # Overview
`Settings` is Windows App Sdk WinUI3 .Net Unpackaged desktop application. More details about Windows App Sdk can be found [here](https://github.com/microsoft/WindowsAppSDK#windows-app-sdk---calling-all-windows-developers). More details about WinUI can be found [here](https://microsoft.github.io/microsoft-ui-xaml/about.html#what-is-it). `Settings` is Windows App Sdk WinUI3 .Net Unpackaged desktop application. More details about Windows App Sdk can be found in [Windows App SDK - Calling all Windows developers!](https://github.com/microsoft/WindowsAppSDK#windows-app-sdk---calling-all-windows-developers). More details about WinUI can be found in [Build apps with WinUI](https://developer.microsoft.com/en-us/windows/develop/).
## Settings V2 Project structure ## Settings V2 Project structure
The Settings project .Net WinUI3 based project which The Settings project .Net WinUI3 based project which

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 KiB

View File

@@ -10,8 +10,9 @@ The build ID can be found in `Core\Constants.cs` in the `BuildId` variable - it
The build ID moniker is made up of two components - a reference to a [Halo](https://en.wikipedia.org/wiki/Halo_(franchise)) character, and the date when the work on the specific build started in the format of `MMDDYYYY`. The build ID moniker is made up of two components - a reference to a [Halo](https://en.wikipedia.org/wiki/Halo_(franchise)) character, and the date when the work on the specific build started in the format of `MMDDYYYY`.
| Build ID | Build Date | | Build ID | Build Date |
|:-------------------------------------------------------------------|:------------------| |:-------------------------------------------------------------------|:------------------|
| [`TILLSON_11272024`](#TILLSON_11272024-november-27-2024) | November 27, 2024 |
| [`PROMETHEAN_09082024`](#PROMETHEAN_09082024-september-8-2024) | September 8, 2024 | | [`PROMETHEAN_09082024`](#PROMETHEAN_09082024-september-8-2024) | September 8, 2024 |
| [`VISEGRADRELAY_08152024`](#VISEGRADRELAY_08152024-august-15-2024) | August 15, 2024 | | [`VISEGRADRELAY_08152024`](#VISEGRADRELAY_08152024-august-15-2024) | August 15, 2024 |
| [`DAISY023_04102024`](#DAISY023_04102024-april-10-2024) | April 10, 2024 | | [`DAISY023_04102024`](#DAISY023_04102024-april-10-2024) | April 10, 2024 |
@@ -19,13 +20,28 @@ The build ID moniker is made up of two components - a reference to a [Halo](http
| [`LIBRARIAN_03202022`](#librarian_03202022-march-20-2022) | March 20, 2022 | | [`LIBRARIAN_03202022`](#librarian_03202022-march-20-2022) | March 20, 2022 |
| `ARBITER_01312022` | January 31, 2022 | | `ARBITER_01312022` | January 31, 2022 |
### `TILLSON_11272024` (November 27, 2024)
>[!NOTE]
>See pull request: [Awake - `TILLSON_11272024`](https://github.com/microsoft/PowerToys/pull/36049)
- [#35250](https://github.com/microsoft/PowerToys/issues/35250) Updates the icon retry policy, making sure that the icon consistently and correctly renders in the tray.
- [#35848](https://github.com/microsoft/PowerToys/issues/35848) Fixed a bug where custom tray time shortcuts for longer than 24 hours would be parsed as zero hours/zero minutes.
- [#34716](https://github.com/microsoft/PowerToys/issues/34716) Properly recover the state icon in the tray after an `explorer.exe` crash.
- Added configuration safeguards to make sure that invalid values for timed keep-awake times do not result in exceptions.
- Updated the tray initialization logic, making sure we wait for it to be properly created before setting icons.
- Expanded logging capabilities to track invoking functions.
- Added command validation logic to make sure that incorrect command line arguments display an error.
- Display state now shown in the tray tooltip.
- When timed mode is used, changing the display setting will no longer reset the timer.
### `PROMETHEAN_09082024` (September 8, 2024) ### `PROMETHEAN_09082024` (September 8, 2024)
>[!NOTE] >[!NOTE]
>See pull request: [Awake - `PROMETHEAN_09082024`](https://github.com/microsoft/PowerToys/pull/34717) >See pull request: [Awake - `PROMETHEAN_09082024`](https://github.com/microsoft/PowerToys/pull/34717)
- Updating the initialization logic to make sure that settings are respected for proper group policy and single-instance detection. - Updating the initialization logic to make sure that settings are respected for proper group policy and single-instance detection.
- [#34148] Fixed a bug from the previous release that incorrectly synchronized threads for shell icon creation and initialized parent PID when it was not parented. - [#34148](https://github.com/microsoft/PowerToys/issues/34148) Fixed a bug from the previous release that incorrectly synchronized threads for shell icon creation and initialized parent PID when it was not parented.
### `VISEGRADRELAY_08152024` (August 15, 2024) ### `VISEGRADRELAY_08152024` (August 15, 2024)

View File

@@ -39,6 +39,9 @@ Contact the developers of a plugin directly for assistance with a specific plugi
| [GitHubRepo](https://github.com/8LWXpg/PowerToysRun-GitHubRepo) | [8LWXpg](https://github.com/8LWXpg) | Search and open GitHub repositories | | [GitHubRepo](https://github.com/8LWXpg/PowerToysRun-GitHubRepo) | [8LWXpg](https://github.com/8LWXpg) | Search and open GitHub repositories |
| [ProcessKiller](https://github.com/8LWXpg/PowerToysRun-ProcessKiller) | [8LWXpg](https://github.com/8LWXpg) | Search and kill processes | | [ProcessKiller](https://github.com/8LWXpg/PowerToysRun-ProcessKiller) | [8LWXpg](https://github.com/8LWXpg) | Search and kill processes |
| [ChatGPT](https://github.com/ferraridavide/ChatGPTPowerToys) | [ferraridavide](https://github.com/ferraridavide) | Ask a question to ChatGPT | | [ChatGPT](https://github.com/ferraridavide/ChatGPTPowerToys) | [ferraridavide](https://github.com/ferraridavide) | Ask a question to ChatGPT |
| [CanIUse](https://github.com/skttl/ptrun-caniuse) | [skttl](https://github.com/skttl) | Look up browser feature support with caniuse.com |
| [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS |
| [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes |
## Extending software plugins ## Extending software plugins
@@ -58,3 +61,4 @@ Below are community created plugins that target a website or software. They are
| [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords | | [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords |
| [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes | | [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes |
| [SSH](https://github.com/8LWXpg/PowerToysRun-SSH) | [8LWXpg](https://github.com/8LWXpg) | Connect to ssh clients | | [SSH](https://github.com/8LWXpg/PowerToysRun-SSH) | [8LWXpg](https://github.com/8LWXpg) | Connect to ssh clients |
| [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili |

View File

@@ -18,6 +18,19 @@
<DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)"> <DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)">
<!-- Generated by generateFileComponents.ps1 --> <!-- Generated by generateFileComponents.ps1 -->
<!--NewPlusAssetsFiles_Component_Def--> <!--NewPlusAssetsFiles_Component_Def-->
<!-- NewPlus Shell Extension for Win10 registration -->
<Component Id="NewPlus_ShellExtension_win10" Guid="D5456D4A-6EEC-4B85-944D-6A6A4A74FFA6" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{FF90D477-E32A-4BE8-8CC5-A502A97F5401}">
<RegistryValue Type="string" Value="NewPlus Shell Extension Win10" />
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.NewPlus.ShellExtension.win10.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\Directory\background\ShellEx\ContextMenuHandlers\NewPlusShellExtensionWin10">
<RegistryValue Type="string" Value="{FF90D477-E32A-4BE8-8CC5-A502A97F5401}"/>
</RegistryKey>
</Component>
</DirectoryRef> </DirectoryRef>
<ComponentGroup Id="NewPlusComponentGroup"> <ComponentGroup Id="NewPlusComponentGroup">
@@ -27,6 +40,7 @@
</RegistryKey> </RegistryKey>
<RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/> <RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/>
</Component> </Component>
<ComponentRef Id="NewPlus_ShellExtension_win10" />
</ComponentGroup> </ComponentGroup>

View File

@@ -30,7 +30,7 @@ Function Generate-FileList() {
$fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe") $fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe")
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "customTokenColors.js", "*.pri") $fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri")
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll") $dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")

View File

@@ -46,32 +46,28 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers = dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles # Naming styles
dotnet_naming_style.begins_with_i.required_prefix = I dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix = dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case dotnet_naming_style.begins_with_i.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix = dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_style_coalesce_expression = true:suggestion dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
@@ -96,4 +92,176 @@ end_of_line = crlf
dotnet_diagnostic.IDE0065.severity = none dotnet_diagnostic.IDE0065.severity = none
# IDE0009: Add this or Me qualification # IDE0009: Add this or Me qualification
dotnet_diagnostic.IDE0009.severity = none dotnet_diagnostic.IDE0009.severity = none
# IDE-based code analysis rules
# IDE0005: Remove unnecessary import
dotnet_diagnostic.IDE0005.severity = suggestion
# IDE0008: Use explicit type instead of 'var'
dotnet_diagnostic.IDE0008.severity = silent
# IDE0016: Use throw expression
dotnet_diagnostic.IDE0016.severity = suggestion
# IDE0018: Inline variable declaration
dotnet_diagnostic.IDE0018.severity = suggestion
# IDE0019: Use pattern matching
dotnet_diagnostic.IDE0019.severity = suggestion
# IDE0021: Use expression body for constructors
dotnet_diagnostic.IDE0021.severity = silent
# IDE0022: Use expression body for methods
dotnet_diagnostic.IDE0022.severity = silent
# IDE0023: Use expression body for conversion operators
dotnet_diagnostic.IDE0023.severity = silent
# IDE0025: Use expression body for properties
dotnet_diagnostic.IDE0025.severity = silent
# IDE0027: Use expression body for accessors
dotnet_diagnostic.IDE0027.severity = silent
# IDE0028: Use collection initializers
dotnet_diagnostic.IDE0028.severity = suggestion
# IDE0029: Null check can be simplified
dotnet_diagnostic.IDE0029.severity = suggestion
# IDE0031: Use null propagation
dotnet_diagnostic.IDE0031.severity = suggestion
# IDE0032: Use auto property
dotnet_diagnostic.IDE0032.severity = suggestion
# IDE0034: Simplify default expression
dotnet_diagnostic.IDE0034.severity = suggestion
# IDE0036: Order modifiers
dotnet_diagnostic.IDE0036.severity = suggestion
# IDE0039: Use local function instead of lambda
dotnet_diagnostic.IDE0039.severity = suggestion
# IDE0042: Deconstruct variable declaration
dotnet_diagnostic.IDE0042.severity = suggestion
# IDE0044: Add readonly modifier
dotnet_diagnostic.IDE0044.severity = suggestion
# IDE0045: Use conditional expression for assignment
dotnet_diagnostic.IDE0045.severity = suggestion
# IDE0046: Use conditional expression for return
dotnet_diagnostic.IDE0046.severity = suggestion
# IDE0047: Remove unnecessary parentheses
dotnet_diagnostic.IDE0047.severity = suggestion
# IDE0051: Remove unused private member
dotnet_diagnostic.IDE0051.severity = suggestion
# IDE0052: Remove unread private member
dotnet_diagnostic.IDE0052.severity = suggestion
# IDE0054: Use compound assignment
dotnet_diagnostic.IDE0054.severity = suggestion
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = suggestion
# IDE0056: Use index operator
dotnet_diagnostic.IDE0056.severity = suggestion
# IDE0057: Use range operator
dotnet_diagnostic.IDE0057.severity = suggestion
# IDE0059: Remove unnecessary value assignment
dotnet_diagnostic.IDE0059.severity = suggestion
# IDE0060: Remove unused parameter
dotnet_diagnostic.IDE0060.severity = suggestion
# IDE0061: Use expression body for local functions
dotnet_diagnostic.IDE0061.severity = silent
# IDE0063: Use simple 'using' statement
dotnet_diagnostic.IDE0063.severity = suggestion
# IDE0071: Simplify interpolation
dotnet_diagnostic.IDE0071.severity = suggestion
# IDE0074: Use coalesce compound assignment
dotnet_diagnostic.IDE0074.severity = suggestion
# IDE0075: Simplify conditional expression
dotnet_diagnostic.IDE0075.severity = suggestion
# IDE0077: Avoid legacy format target in global 'SuppressMessageAttribute'
dotnet_diagnostic.IDE0077.severity = suggestion
# IDE0078: Use pattern matching
dotnet_diagnostic.IDE0078.severity = suggestion
# IDE0083: Use pattern matching ('not' operator)
dotnet_diagnostic.IDE0083.severity = suggestion
# IDE0090: Simplify 'new' expression
dotnet_diagnostic.IDE0090.severity = suggestion
# IDE0100: Remove unnecessary equality operator
dotnet_diagnostic.IDE0100.severity = suggestion
# IDE0130: Namespace does not match folder structure
dotnet_diagnostic.IDE0130.severity = suggestion
# IDE0160: Use block-scoped namespace
dotnet_diagnostic.IDE0160.severity = silent
# IDE0180: Use tuple to swap values
dotnet_diagnostic.IDE0180.severity = suggestion
# IDE0200: Remove unnecessary lambda expression
dotnet_diagnostic.IDE0200.severity = suggestion
# IDE0240: Nullable directive is redundant
dotnet_diagnostic.IDE0240.severity = suggestion
# IDE0250: Struct can be made 'readonly'
dotnet_diagnostic.IDE0250.severity = suggestion
# IDE0251: Member can be made 'readonly''
dotnet_diagnostic.IDE0251.severity = suggestion
# IDE0260: Use pattern matching
dotnet_diagnostic.IDE0260.severity = suggestion
# IDE0270: Null check can be simplified
dotnet_diagnostic.IDE0270.severity = suggestion
# IDE0290: Use primary constructor
dotnet_diagnostic.IDE0290.severity = silent
# IDE0300: Use collection expression for array
dotnet_diagnostic.IDE0300.severity = suggestion
# IDE0301: Use collection expression for empty
dotnet_diagnostic.IDE0301.severity = suggestion
# IDE0305: Use collection expression for fluent
dotnet_diagnostic.IDE0305.severity = suggestion
# IDE1005: Use conditional delegate call
dotnet_diagnostic.IDE1005.severity = suggestion
# CA1859: Use concrete types when possible for improved performance
dotnet_diagnostic.CA1859.severity = suggestion
# CA2202: Avoid inexact read with Stream.Read
dotnet_diagnostic.CA2022.severity = suggestion
# CA2263: Prefer generic overload when type is known
dotnet_diagnostic.CA2263.severity = suggestion

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Some items may be set in Directory.Build.props in root -->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<IsAotCompatible>true</IsAotCompatible>
<CsWinRTAotOptimizerEnabled>true</CsWinRTAotOptimizerEnabled>
<CsWinRTAotWarningLevel>2</CsWinRTAotWarningLevel>
</PropertyGroup>
</Project>

View File

@@ -2,8 +2,8 @@
<!-- Include Monaco Editor source code --> <!-- Include Monaco Editor source code -->
<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> <ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\Monaco\customTokenColors.js"> <None Include="$(MSBuildThisFileDirectory)\Monaco\customTokenThemeRules.js">
<Link>Assets\Monaco\customTokenColors.js</Link> <Link>Assets\Monaco\customTokenThemeRules.js</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)\Monaco\monacoSpecialLanguages.js"> <None Include="$(MSBuildThisFileDirectory)\Monaco\monacoSpecialLanguages.js">

View File

@@ -6,7 +6,7 @@
root: [ root: [
[/^#.*$/, 'comment'], [/^#.*$/, 'comment'],
[/.*((?<!(^|\/))\*\*.*|\*\*(?!(\/|$))).*/, 'invalid'], [/.*((?<!(^|\/))\*\*.*|\*\*(?!(\/|$))).*/, 'invalid'],
[/((?:^!\s*(?:\\\s|\S)+)?)((?:^\s*(?:\\\s|\S)+)?)((?:\s+(?:\\\s|\S)+)*)/, ['custom-gitignore.negation', 'tag', 'invalid']] [/((?:^!\s*(?:\\\s|\S)+)?)((?:^\s*(?:\\\s|\S)+)?)((?:\s+(?:\\\s|\S)+)*)/, ['custom-negation', 'tag', 'invalid']]
] ]
} }
}; };

View File

@@ -3,7 +3,7 @@ export function regDefinition() {
tokenPostfix: '.reg', tokenPostfix: '.reg',
tokenizer: { tokenizer: {
root: [ root: [
// Header (case sensitive) // Header (case-sensitive)
[/Windows Registry Editor Version 5.00/, 'comment'], [/Windows Registry Editor Version 5.00/, 'comment'],
[/REGEDIT4/, 'comment'], [/REGEDIT4/, 'comment'],
// Comments // Comments

View File

@@ -0,0 +1,29 @@
export function srtDefinition() {
return {
tokenizer: {
root: [
[/\s*\d+/, 'number', '@block']
],
block: [
[/^\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}/, {
cases: {
'@eos': {token: 'type.identifier', next: '@subtitle'},
'@default': {token: 'type.identifier', next: '@ignore'}
}
}],
[/^$/, 'string', '@pop']
],
ignore: [
[/.+$/, '', '@subtitle']
],
subtitle: [
[/^$/, 'string', '@popall'],
[/<\/?(?:[ibu]|font(?:\s+color="[^"]+"\s*)?)>/, 'tag'],
[/./, 'string']
]
}
};
}

View File

@@ -1,3 +0,0 @@
export const customTokenColors = [
{token: 'custom-gitignore.negation', foreground: 'c00ce0'}
];

View File

@@ -0,0 +1,3 @@
export const customTokenThemeRules = [
{token: 'custom-negation.gitignore', foreground: 'c00ce0'}
];

View File

@@ -79,7 +79,7 @@
<script src="http://[[PT_URL]]/monacoSpecialLanguages.js" type="module"></script> <script src="http://[[PT_URL]]/monacoSpecialLanguages.js" type="module"></script>
<script type="module"> <script type="module">
import { registerAdditionalLanguages } from 'http://[[PT_URL]]/monacoSpecialLanguages.js'; import { registerAdditionalLanguages } from 'http://[[PT_URL]]/monacoSpecialLanguages.js';
import { customTokenColors } from 'http://[[PT_URL]]/customTokenColors.js'; import { customTokenThemeRules } from 'http://[[PT_URL]]/customTokenThemeRules.js';
require.config({ paths: { vs: 'http://[[PT_URL]]/monacoSRC/min/vs' } }); require.config({ paths: { vs: 'http://[[PT_URL]]/monacoSRC/min/vs' } });
require(['vs/editor/editor.main'], async function () { require(['vs/editor/editor.main'], async function () {
await registerAdditionalLanguages(monaco) await registerAdditionalLanguages(monaco)
@@ -88,7 +88,7 @@
monaco.editor.defineTheme('theme', { monaco.editor.defineTheme('theme', {
base: theme, // Sets the base theme to "vs" or "vs-dark" depending on the user's preference base: theme, // Sets the base theme to "vs" or "vs-dark" depending on the user's preference
inherit: true, inherit: true,
rules: customTokenColors, rules: customTokenThemeRules,
colors: {} // `colors` is a required attribute colors: {} // `colors` is a required attribute
}); });

View File

@@ -2,18 +2,20 @@
import { regDefinition } from './customLanguages/reg.js'; import { regDefinition } from './customLanguages/reg.js';
import { gitignoreDefinition } from './customLanguages/gitignore.js'; import { gitignoreDefinition } from './customLanguages/gitignore.js';
import { srtDefinition } from './customLanguages/srt.js';
export async function registerAdditionalLanguages(monaco){ export async function registerAdditionalLanguages(monaco){
await languageDefinitions(); await languageDefinitions();
registerAdditionalLanguage("cppExt", [".ino", ".pde"], "cpp", monaco) registerAdditionalLanguage("cppExt", [".ino", ".pde"], "cpp", monaco);
registerAdditionalLanguage("xmlExt", [".wsdl", ".csproj", ".vcxproj", ".vbproj", ".fsproj"], "xml", monaco) registerAdditionalLanguage("xmlExt", [".wsdl", ".csproj", ".vcxproj", ".vbproj", ".fsproj"], "xml", monaco);
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt"], "txt", monaco) registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".ahk", ".ion"], "txt", monaco);
registerAdditionalLanguage("razorExt", [".razor"], "razor", monaco) registerAdditionalLanguage("razorExt", [".razor"], "razor", monaco);
registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco) registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco);
registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco) registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco);
registerAdditionalLanguage("shellExt", [".ksh", ".zsh", ".bsh"], "shell", monaco) registerAdditionalLanguage("shellExt", [".ksh", ".zsh", ".bsh"], "shell", monaco);
registerAdditionalNewLanguage("reg", [".reg"], regDefinition(), monaco) registerAdditionalNewLanguage("reg", [".reg"], regDefinition(), monaco);
registerAdditionalNewLanguage("gitignore", [".gitignore"], gitignoreDefinition(), monaco) registerAdditionalNewLanguage("gitignore", [".gitignore"], gitignoreDefinition(), monaco);
registerAdditionalNewLanguage("srt", [".srt"], srtDefinition(), monaco);
} }
// Language definitions taken from Monaco source code // Language definitions taken from Monaco source code

File diff suppressed because one or more lines are too long

View File

@@ -109,6 +109,24 @@ namespace DPIAware
} }
} }
void InverseConvert(HMONITOR monitor_handle, RECT& rect)
{
if (monitor_handle == NULL)
{
const POINT ptZero = { 0, 0 };
monitor_handle = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
}
UINT dpi_x, dpi_y;
if (GetDpiForMonitor(monitor_handle, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y) == S_OK)
{
rect.left = static_cast<long>(std::round(rect.left * static_cast<float>(DEFAULT_DPI) / dpi_x));
rect.right = static_cast<long>(std::round(rect.right * static_cast<float>(DEFAULT_DPI) / dpi_x));
rect.top = static_cast<long>(std::round(rect.top * static_cast<float>(DEFAULT_DPI) / dpi_y));
rect.bottom = static_cast<long>(std::round(rect.bottom * static_cast<float>(DEFAULT_DPI) / dpi_y));
}
}
void EnableDPIAwarenessForThisProcess() void EnableDPIAwarenessForThisProcess()
{ {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);

View File

@@ -15,6 +15,7 @@ namespace DPIAware
void Convert(HMONITOR monitor_handle, RECT& rect); void Convert(HMONITOR monitor_handle, RECT& rect);
void ConvertByCursorPosition(float& width, float& height); void ConvertByCursorPosition(float& width, float& height);
void InverseConvert(HMONITOR monitor_handle, float& width, float& height); void InverseConvert(HMONITOR monitor_handle, float& width, float& height);
void InverseConvert(HMONITOR monitor_handle, RECT& rect);
void EnableDPIAwarenessForThisProcess(); void EnableDPIAwarenessForThisProcess();
enum AwarenessLevel enum AwarenessLevel

View File

@@ -52,3 +52,55 @@ MonitorInfo MonitorInfo::GetPrimaryMonitor()
} }
return monitors[0]; return monitors[0];
} }
MonitorInfo MonitorInfo::GetFromWindow(const HWND window)
{
auto monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
return MonitorInfo::MonitorInfo(monitor);
}
MonitorInfo MonitorInfo::GetFromPoint(int32_t x, int32_t y)
{
auto monitor = MonitorFromPoint(POINT{ x, y }, MONITOR_DEFAULTTONULL);
return MonitorInfo::MonitorInfo(monitor);
}
MonitorInfo::Size MonitorInfo::GetSize(const MONITORINFOEX& monitorInfoEx)
{
Size size = {};
auto device_name = PCTSTR(monitorInfoEx.szDevice);
auto hdc = CreateDC(device_name, nullptr, nullptr, nullptr);
size.width_mm = static_cast<float>(GetDeviceCaps(hdc, HORZSIZE));
size.height_mm = static_cast<float>(GetDeviceCaps(hdc, VERTSIZE));
if (hdc != nullptr)
{
ReleaseDC(nullptr, hdc);
}
auto monitor = &monitorInfoEx.rcMonitor;
size.width_logical = static_cast<uint32_t>(monitor->right - monitor->left);
size.height_logical = static_cast<uint32_t>(monitor->bottom - monitor->top);
DEVMODE dev_mode = { .dmSize = sizeof DEVMODE };
if (EnumDisplaySettingsEx(device_name, ENUM_CURRENT_SETTINGS, &dev_mode, EDS_RAWMODE))
{
size.width_physical = dev_mode.dmPelsWidth;
size.height_physical = dev_mode.dmPelsHeight;
}
return size;
}
MonitorInfo::Size MonitorInfo::GetSize() const
{
if (this->handle)
{
return MonitorInfo::GetSize(this->info);
}
else
{
return MonitorInfo::Size{};
}
}

View File

@@ -1,4 +1,5 @@
#pragma once #pragma once
#pragma comment(lib, "Gdi32.lib")
#include <Windows.h> #include <Windows.h>
#include <compare> #include <compare>
@@ -42,6 +43,15 @@ struct Box
class MonitorInfo class MonitorInfo
{ {
public:
typedef struct Size
{
uint32_t width_logical, height_logical;
uint32_t width_physical, height_physical;
float width_mm, height_mm;
} Size;
private:
HMONITOR handle; HMONITOR handle;
MONITORINFOEX info = {}; MONITORINFOEX info = {};
@@ -53,8 +63,14 @@ public:
} }
Box GetScreenSize(const bool includeNonWorkingArea) const; Box GetScreenSize(const bool includeNonWorkingArea) const;
bool IsPrimary() const; bool IsPrimary() const;
Size GetSize() const;
// Returns monitor rects ordered from left to right // Returns monitor rects ordered from left to right
static std::vector<MonitorInfo> GetMonitors(bool includeNonWorkingArea); static std::vector<MonitorInfo> GetMonitors(bool includeNonWorkingArea);
static MonitorInfo GetPrimaryMonitor(); static MonitorInfo GetPrimaryMonitor();
static MonitorInfo GetFromWindow(HWND);
static MonitorInfo GetFromPoint(int32_t, int32_t);
private:
static Size GetSize(const MONITORINFOEX&);
}; };

View File

@@ -29,34 +29,28 @@ namespace Microsoft.PowerToys.FilePreviewCommon
new XmlFormatter(), new XmlFormatter(),
}.AsReadOnly(); }.AsReadOnly();
private static string? _monacoDirectory; private static readonly Lazy<string> _monacoDirectory = new(GetRuntimeMonacoDirectory);
public static string GetRuntimeMonacoDirectory() /// <summary>
/// Gets the path of the Monaco assets folder.
/// </summary>
public static string MonacoDirectory => _monacoDirectory.Value;
private static string GetRuntimeMonacoDirectory()
{ {
string codeBase = Assembly.GetExecutingAssembly().Location; string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
string path = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "Assets", "Monaco"));
if (Path.Exists(path))
{
return path;
}
else
{
// We're likely in WinUI3Apps directory and need to go back to the base directory.
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "..", "Assets", "Monaco"));
}
}
public static string MonacoDirectory // If the executable is within "WinUI3Apps", correct the path first.
{ if (Path.GetFileName(exePath) == "WinUI3Apps")
get
{ {
if (string.IsNullOrEmpty(_monacoDirectory)) exePath = Path.Combine(exePath, "..");
{
_monacoDirectory = GetRuntimeMonacoDirectory();
}
return _monacoDirectory;
} }
string monacoPath = Path.Combine(exePath, "Assets", "Monaco");
return Directory.Exists(monacoPath) ?
monacoPath :
throw new DirectoryNotFoundException($"Monaco assets directory not found at {monacoPath}");
} }
public static JsonDocument GetLanguages() public static JsonDocument GetLanguages()

View File

@@ -1,4 +1,4 @@
#pragma once #pragma once
#include <unknwn.h> #include <Unknwn.h>
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Foundation.Collections.h>

View File

@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.CompilerServices;
using PowerToys.Interop; using PowerToys.Interop;
@@ -52,16 +53,18 @@ namespace ManagedCommon
Trace.AutoFlush = true; Trace.AutoFlush = true;
} }
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogError(string message) public static void LogError(string message)
{ {
Log(message, Error); Log(message, Error);
} }
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogError(string message, Exception ex) public static void LogError(string message, Exception ex)
{ {
if (ex == null) if (ex == null)
{ {
LogError(message); Log(message, Error);
} }
else else
{ {
@@ -84,26 +87,31 @@ namespace ManagedCommon
} }
} }
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogWarning(string message) public static void LogWarning(string message)
{ {
Log(message, Warning); Log(message, Warning);
} }
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogInfo(string message) public static void LogInfo(string message)
{ {
Log(message, Info); Log(message, Info);
} }
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogDebug(string message) public static void LogDebug(string message)
{ {
Log(message, Debug); Log(message, Debug);
} }
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogTrace() public static void LogTrace()
{ {
Log(string.Empty, TraceFlag); Log(string.Empty, TraceFlag);
} }
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Log(string message, string type) private static void Log(string message, string type)
{ {
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo()); Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo());
@@ -116,13 +124,49 @@ namespace ManagedCommon
Trace.Unindent(); Trace.Unindent();
} }
[MethodImpl(MethodImplOptions.NoInlining)]
private static string GetCallerInfo() private static string GetCallerInfo()
{ {
StackTrace stackTrace = new(); StackTrace stackTrace = new();
var methodName = stackTrace.GetFrame(3)?.GetMethod(); var callerMethod = GetCallerMethod(stackTrace);
var className = methodName?.DeclaringType.Name;
return className + "::" + methodName?.Name; return $"{callerMethod?.DeclaringType?.Name}::{callerMethod.Name}";
}
private static MethodBase GetCallerMethod(StackTrace stackTrace)
{
const int topFrame = 3;
var topMethod = stackTrace.GetFrame(topFrame)?.GetMethod();
try
{
if (topMethod?.Name == nameof(IAsyncStateMachine.MoveNext) && typeof(IAsyncStateMachine).IsAssignableFrom(topMethod?.DeclaringType))
{
// Async method; return actual method as determined by heuristic:
// "Nearest method on stack to async state-machine's MoveNext() in same namespace but in a different type".
// There are tighter ways of determining the actual method, but this is good enough and probably faster.
for (int deepFrame = topFrame + 1; deepFrame < stackTrace.FrameCount; deepFrame++)
{
var deepMethod = stackTrace.GetFrame(deepFrame)?.GetMethod();
if (deepMethod?.DeclaringType != topMethod?.DeclaringType && deepMethod?.DeclaringType?.Namespace == topMethod?.DeclaringType?.Namespace)
{
return deepMethod;
}
}
}
}
catch (Exception)
{
// Ignore exceptions in Release. The code above won't throw, but if it does, we don't want to crash the app.
#if DEBUG
throw;
#endif
}
return topMethod;
} }
} }
} }

View File

@@ -39,7 +39,7 @@ namespace Microsoft.PowerToys.Telemetry
public const EventKeywords ProjectKeywordMeasure = (EventKeywords)0x0; public const EventKeywords ProjectKeywordMeasure = (EventKeywords)0x0;
/// <summary> /// <summary>
/// Group ID for Powertoys project. /// Group ID for PowerToys project.
/// </summary> /// </summary>
private static readonly string[] PowerToysTelemetryTraits = { "ETW_GROUP", "{42749043-438c-46a2-82be-c6cbeb192ff2}" }; private static readonly string[] PowerToysTelemetryTraits = { "ETW_GROUP", "{42749043-438c-46a2-82be-c6cbeb192ff2}" };

View File

@@ -241,10 +241,12 @@ void LayoutMap::LayoutMapImpl::UpdateLayout()
keyboardLayoutMap[VK_KANA] = L"IME Kana"; keyboardLayoutMap[VK_KANA] = L"IME Kana";
keyboardLayoutMap[VK_HANGEUL] = L"IME Hangeul"; keyboardLayoutMap[VK_HANGEUL] = L"IME Hangeul";
keyboardLayoutMap[VK_HANGUL] = L"IME Hangul"; keyboardLayoutMap[VK_HANGUL] = L"IME Hangul";
keyboardLayoutMap[VK_IME_ON] = L"IME On";
keyboardLayoutMap[VK_JUNJA] = L"IME Junja"; keyboardLayoutMap[VK_JUNJA] = L"IME Junja";
keyboardLayoutMap[VK_FINAL] = L"IME Final"; keyboardLayoutMap[VK_FINAL] = L"IME Final";
keyboardLayoutMap[VK_HANJA] = L"IME Hanja"; keyboardLayoutMap[VK_HANJA] = L"IME Hanja";
keyboardLayoutMap[VK_KANJI] = L"IME Kanji"; keyboardLayoutMap[VK_KANJI] = L"IME Kanji";
keyboardLayoutMap[VK_IME_OFF] = L"IME Off";
keyboardLayoutMap[VK_CONVERT] = L"IME Convert"; keyboardLayoutMap[VK_CONVERT] = L"IME Convert";
keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert"; keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert";
keyboardLayoutMap[VK_ACCEPT] = L"IME Kana"; keyboardLayoutMap[VK_ACCEPT] = L"IME Kana";

View File

@@ -6,7 +6,7 @@
#pragma once #pragma once
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
// add headers that you want to pre-compile here // add headers that you want to pre-compile here
#include <unknwn.h> #include <Unknwn.h>
#include <winrt/base.h> #include <winrt/base.h>
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Foundation.Collections.h>

View File

@@ -85,7 +85,7 @@ void Logger::init(std::string loggerName, std::wstring logFilePath, std::wstring
{ {
// todo: that message should be shown from init caller and strings should be localized // todo: that message should be shown from init caller and strings should be localized
MessageBoxW(NULL, MessageBoxW(NULL,
L"Logger can not be initialized", L"Logger cannot be initialized",
L"PowerToys", L"PowerToys",
MB_OK | MB_ICONERROR); MB_OK | MB_ICONERROR);

View File

@@ -1,5 +1,5 @@
#pragma once #pragma once
#include <unknwn.h> #include <Unknwn.h>
#include <winrt/Windows.ApplicationModel.Background.h> #include <winrt/Windows.ApplicationModel.Background.h>
#include <winrt/Windows.Data.Xml.Dom.h> #include <winrt/Windows.Data.Xml.Dom.h>
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>

View File

@@ -4,7 +4,7 @@
#include "utils/com_object_factory.h" #include "utils/com_object_factory.h"
#include "utils/window.h" #include "utils/window.h"
#include <unknwn.h> #include <Unknwn.h>
#include <winrt/base.h> #include <winrt/base.h>
#include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Data.Xml.Dom.h> #include <winrt/Windows.Data.Xml.Dom.h>

View File

@@ -11,7 +11,7 @@ public:
EventWaiter() {} EventWaiter() {}
EventWaiter(const std::wstring& name, std::function<void(DWORD)> callback) EventWaiter(const std::wstring& name, std::function<void(DWORD)> callback)
{ {
// Create localExitThreadEvent and localWaitingEvent for capturing. We can not capture 'this' as we implement move constructor. // Create localExitThreadEvent and localWaitingEvent for capturing. We cannot capture 'this' as we implement move constructor.
auto localExitThreadEvent = exitThreadEvent = CreateEvent(nullptr, false, false, nullptr); auto localExitThreadEvent = exitThreadEvent = CreateEvent(nullptr, false, false, nullptr);
HANDLE localWaitingEvent = waitingEvent = CreateEvent(nullptr, false, false, name.c_str()); HANDLE localWaitingEvent = waitingEvent = CreateEvent(nullptr, false, false, name.c_str());
std::thread([=]() { std::thread([=]() {

View File

@@ -2,7 +2,7 @@
#include <winrt/Windows.Foundation.Metadata.h> #include <winrt/Windows.Foundation.Metadata.h>
// The following three helper functions determine if the user has a build version higher than or equal to 19h1 (aka 1903), as that is a requirement for xaml islands // The following three helper functions determine if the user has a build version greater than or equal to 19h1 (aka 1903), as that is a requirement for xaml islands
// Source : Microsoft-ui-xaml github // Source : Microsoft-ui-xaml github
// Link: https://github.com/microsoft/microsoft-ui-xaml/blob/c045cde57c5c754683d674634a0baccda34d58c4/dev/dll/SharedHelpers.cpp // Link: https://github.com/microsoft/microsoft-ui-xaml/blob/c045cde57c5c754683d674634a0baccda34d58c4/dev/dll/SharedHelpers.cpp
template<uint16_t APIVersion> template<uint16_t APIVersion>

View File

@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<PropertyGroup>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\AdvancedPaste.UnitTests\</OutputPath>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<Content Remove="Assets\image_with_text_example.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\image_with_text_example.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Moq" />
<PackageReference Include="MSTest" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AdvancedPaste\AdvancedPaste.csproj" />
</ItemGroup>
</Project>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

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 System.Threading.Tasks;
using AdvancedPaste.Models.KernelQueryCache;
using AdvancedPaste.Services;
namespace AdvancedPaste.UnitTests.Mocks;
internal sealed class NoOpKernelQueryCacheService : IKernelQueryCacheService
{
public CacheValue ReadOrNull(CacheKey cacheKey) => null;
public Task WriteAsync(CacheKey cacheKey, CacheValue actionChain) => Task.CompletedTask;
}

View File

@@ -0,0 +1,150 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services.OpenAI;
using AdvancedPaste.UnitTests.Mocks;
using ManagedCommon;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.UnitTests.ServicesTests;
[Ignore("Test requires active OpenAI API key.")] // Comment out this line to run these tests after setting up OpenAI API key using AdvancedPaste Settings
[TestClass]
/// <summary>
/// Tests that write batch AI outputs against a list of inputs. Connects to OpenAI and uses the full AdvancedPaste action catalog for Semantic Kernel.
/// If queries produce errors, the error message is written to the output file. If queries produce text-file output, their contents are included as though they were text output.
/// To run this test-suite, first:
/// 1. Setup an OpenAI API key using AdvancedPaste Settings.
/// 2. Comment out the [Ignore] attribute above.
/// 3. Ensure the %USERPROFILE% folder contains the required input files (paths are below).
/// These tests are idempotent and resumable, allowing for partial runs and restarts. It's ok to use existing output files as input files - output-related fields will simply be ignored.
/// </summary>
public sealed class AIServiceBatchIntegrationTests
{
private record class BatchTestInput
{
public string Prompt { get; init; }
public string Clipboard { get; init; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Genre { get; init; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Category { get; init; }
}
private sealed record class BatchTestResult : BatchTestInput
{
[JsonPropertyOrder(1)]
public string Result { get; init; }
internal BatchTestInput ToInput() => new() { Prompt = Prompt, Clipboard = Clipboard, Genre = Genre, Category = Category, };
}
private const string AllTestsFilePath = @"%USERPROFILE%\allAdvancedPasteTests-Input-V2.json";
private const string FailedTestsFilePath = @"%USERPROFILE%\advanced-paste-failed-tests-only.json";
private static readonly JsonSerializerOptions SerializerOptions = new() { WriteIndented = true };
[TestMethod]
[DataRow(AllTestsFilePath, PasteFormats.CustomTextTransformation)]
[DataRow(AllTestsFilePath, PasteFormats.KernelQuery)]
[DataRow(FailedTestsFilePath, PasteFormats.CustomTextTransformation)]
[DataRow(FailedTestsFilePath, PasteFormats.KernelQuery)]
public async Task TestGenerateBatchResults(string inputFilePath, PasteFormats format)
{
// Load input data.
var fullInputFilePath = Environment.ExpandEnvironmentVariables(inputFilePath);
var inputs = await GetDataListAsync<BatchTestInput>(fullInputFilePath);
Assert.IsTrue(inputs.Count > 0);
// Load existing results; allow a partial run to be resumed.
var resultsFile = Path.Combine(Path.GetDirectoryName(fullInputFilePath), $"{Path.GetFileNameWithoutExtension(fullInputFilePath)}-output-{format}.json");
var results = await GetDataListAsync<BatchTestResult>(resultsFile);
Assert.IsTrue(results.Count <= inputs.Count);
CollectionAssert.AreEqual(results.Select(result => result.ToInput()).ToList(), inputs.Take(results.Count).ToList());
async Task WriteResultsAsync() => await File.WriteAllTextAsync(resultsFile, JsonSerializer.Serialize(results, SerializerOptions));
Logger.LogInfo($"Starting {nameof(TestGenerateBatchResults)}; Count={inputs.Count}, InCache={results.Count}");
// Produce results for any unprocessed inputs.
foreach (var input in inputs.Skip(results.Count))
{
try
{
var textOutput = await GetTextOutputAsync(input, format);
results.Add(new() { Prompt = input.Prompt, Clipboard = input.Clipboard, Genre = input.Genre, Category = input.Category, Result = textOutput, });
}
catch (Exception)
{
await WriteResultsAsync();
throw;
}
}
await WriteResultsAsync();
}
private static async Task<List<T>> GetDataListAsync<T>(string filePath) =>
File.Exists(filePath) ? JsonSerializer.Deserialize<List<T>>(await File.ReadAllTextAsync(filePath)) : [];
private static async Task<string> GetTextOutputAsync(BatchTestInput input, PasteFormats format)
{
try
{
var outputPackage = (await GetOutputDataPackageAsync(input, format)).GetView();
var outputFormat = await outputPackage.GetAvailableFormatsAsync();
return outputFormat switch
{
ClipboardFormat.Text => await outputPackage.GetTextOrEmptyAsync(),
ClipboardFormat.File => await File.ReadAllTextAsync((await outputPackage.GetStorageItemsAsync()).Single().Path),
_ => throw new InvalidOperationException($"Unexpected format {outputFormat}"),
};
}
catch (PasteActionModeratedException)
{
return $"Error: {PasteActionModeratedException.ErrorDescription}";
}
catch (PasteActionException ex) when (!string.IsNullOrEmpty(ex.AIServiceMessage))
{
return $"Error: {ex.AIServiceMessage}";
}
}
private static async Task<DataPackage> GetOutputDataPackageAsync(BatchTestInput batchTestInput, PasteFormats format)
{
VaultCredentialsProvider credentialsProvider = new();
PromptModerationService promptModerationService = new(credentialsProvider);
CustomTextTransformService customTextTransformService = new(credentialsProvider, promptModerationService);
switch (format)
{
case PasteFormats.CustomTextTransformation:
return DataPackageHelpers.CreateFromText(await customTextTransformService.TransformTextAsync(batchTestInput.Prompt, batchTestInput.Clipboard));
case PasteFormats.KernelQuery:
var clipboardData = DataPackageHelpers.CreateFromText(batchTestInput.Clipboard).GetView();
KernelService kernelService = new(new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, customTextTransformService);
return await kernelService.TransformClipboardAsync(batchTestInput.Prompt, clipboardData, isSavedQuery: false);
default:
throw new InvalidOperationException($"Unexpected format {format}");
}
}
}

View File

@@ -0,0 +1,172 @@
// 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.TestingHelpers;
using System.Linq;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using AdvancedPaste.Models.KernelQueryCache;
using AdvancedPaste.Services;
using AdvancedPaste.Settings;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace AdvancedPaste.UnitTests.ServicesTests;
[TestClass]
public sealed class CustomActionKernelQueryCacheServiceTests
{
private static readonly CacheKey CustomActionTestKey = new() { Prompt = "TestPrompt1", AvailableFormats = ClipboardFormat.Text };
private static readonly CacheKey CustomActionTestKey2 = new() { Prompt = "TestPrompt2", AvailableFormats = ClipboardFormat.File | ClipboardFormat.Image };
private static readonly CacheKey MarkdownTestKey = new() { Prompt = "Paste as Markdown", AvailableFormats = ClipboardFormat.Text };
private static readonly CacheKey JSONTestKey = new() { Prompt = "Paste as JSON", AvailableFormats = ClipboardFormat.Text };
private static readonly CacheKey PasteAsTxtFileKey = new() { Prompt = "Paste as .txt file", AvailableFormats = ClipboardFormat.File };
private static readonly CacheKey PasteAsPngFileKey = new() { Prompt = "Paste as .png file", AvailableFormats = ClipboardFormat.Image };
private static readonly CacheValue TestValue = new([new(PasteFormats.PlainText, [])]);
private static readonly CacheValue TestValue2 = new([new(PasteFormats.KernelQuery, new() { { "a", "b" }, { "c", "d" } })]);
private CustomActionKernelQueryCacheService _cacheService;
private Mock<IUserSettings> _userSettings;
private MockFileSystem _fileSystem;
[TestInitialize]
public void TestInitialize()
{
_userSettings = new();
UpdateUserActions([], []);
_fileSystem = new();
_cacheService = new(_userSettings.Object, _fileSystem);
}
[TestMethod]
public async Task Test_Cache_Always_Accepts_Core_Action_Prompt()
{
await AssertAcceptsAsync(MarkdownTestKey);
}
[TestMethod]
public async Task Test_Cache_Accepts_Prompt_When_Custom_Action()
{
await AssertRejectsAsync(CustomActionTestKey);
UpdateUserActions([], [new() { Name = nameof(CustomActionTestKey), Prompt = CustomActionTestKey.Prompt, IsShown = true }]);
await AssertAcceptsAsync(CustomActionTestKey);
await AssertRejectsAsync(CustomActionTestKey2, PasteAsTxtFileKey);
UpdateUserActions([], []);
await AssertRejectsAsync(CustomActionTestKey);
}
[TestMethod]
public async Task Test_Cache_Accepts_Prompt_When_User_Additional_Action()
{
await AssertRejectsAsync(PasteAsTxtFileKey, PasteAsPngFileKey);
UpdateUserActions([PasteFormats.PasteAsHtmlFile, PasteFormats.PasteAsTxtFile], []);
await AssertAcceptsAsync(PasteAsTxtFileKey);
await AssertRejectsAsync(PasteAsPngFileKey, CustomActionTestKey);
UpdateUserActions([], []);
await AssertRejectsAsync(PasteAsTxtFileKey);
}
[TestMethod]
public async Task Test_Cache_Overwrites_Latest_Value()
{
await _cacheService.WriteAsync(JSONTestKey, TestValue);
await _cacheService.WriteAsync(MarkdownTestKey, TestValue2);
await _cacheService.WriteAsync(JSONTestKey, TestValue2);
await _cacheService.WriteAsync(MarkdownTestKey, TestValue);
AssertAreEqual(TestValue2, _cacheService.ReadOrNull(JSONTestKey));
AssertAreEqual(TestValue, _cacheService.ReadOrNull(MarkdownTestKey));
}
[TestMethod]
public async Task Test_Cache_Uses_Case_Insensitive_Prompt_Comparison()
{
static CacheKey CreateUpperCaseKey(CacheKey key) =>
new() { Prompt = key.Prompt.ToUpperInvariant(), AvailableFormats = key.AvailableFormats };
await _cacheService.WriteAsync(CreateUpperCaseKey(JSONTestKey), TestValue);
await _cacheService.WriteAsync(MarkdownTestKey, TestValue2);
AssertAreEqual(TestValue, _cacheService.ReadOrNull(JSONTestKey));
AssertAreEqual(TestValue2, _cacheService.ReadOrNull(MarkdownTestKey));
}
[TestMethod]
public async Task Test_Cache_Uses_Clipboard_Formats_In_Key()
{
CacheKey key1 = new() { Prompt = JSONTestKey.Prompt, AvailableFormats = ClipboardFormat.File };
CacheKey key2 = new() { Prompt = JSONTestKey.Prompt, AvailableFormats = ClipboardFormat.Image };
await _cacheService.WriteAsync(key1, TestValue);
Assert.IsNotNull(_cacheService.ReadOrNull(key1));
Assert.IsNull(_cacheService.ReadOrNull(key2));
}
[TestMethod]
public async Task Test_Cache_Is_Persistent()
{
await _cacheService.WriteAsync(JSONTestKey, TestValue);
await _cacheService.WriteAsync(MarkdownTestKey, TestValue2);
_cacheService = new(_userSettings.Object, _fileSystem); // recreate using same mock file-system to simulate app restart
AssertAreEqual(TestValue, _cacheService.ReadOrNull(JSONTestKey));
AssertAreEqual(TestValue2, _cacheService.ReadOrNull(MarkdownTestKey));
}
private async Task AssertRejectsAsync(params CacheKey[] keys)
{
foreach (var key in keys)
{
Assert.IsNull(_cacheService.ReadOrNull(key));
await _cacheService.WriteAsync(key, TestValue);
Assert.IsNull(_cacheService.ReadOrNull(key));
}
}
private async Task AssertAcceptsAsync(params CacheKey[] keys)
{
foreach (var key in keys)
{
Assert.IsNull(_cacheService.ReadOrNull(key));
await _cacheService.WriteAsync(key, TestValue);
AssertAreEqual(TestValue, _cacheService.ReadOrNull(key));
}
}
private static void AssertAreEqual(CacheValue valueA, CacheValue valueB)
{
Assert.IsNotNull(valueA);
Assert.IsNotNull(valueB);
Assert.AreEqual(valueA.ActionChain.Count, valueB.ActionChain.Count);
foreach (var (itemA, itemB) in valueA.ActionChain.Zip(valueB.ActionChain))
{
Assert.AreEqual(itemA.Format, itemB.Format);
Assert.AreEqual(itemA.Arguments.Count, itemB.Arguments.Count);
Assert.IsFalse(itemA.Arguments.Except(itemB.Arguments).Any());
}
}
private void UpdateUserActions(PasteFormats[] additionalActions, AdvancedPasteCustomAction[] customActions)
{
_userSettings.Setup(settingsObj => settingsObj.AdditionalActions).Returns(additionalActions);
_userSettings.Setup(settingsObj => settingsObj.CustomActions).Returns(customActions);
_userSettings.Raise(settingsObj => settingsObj.Changed += null, EventArgs.Empty);
}
}

View File

@@ -0,0 +1,152 @@
// 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;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models;
using AdvancedPaste.Services.OpenAI;
using AdvancedPaste.Telemetry;
using AdvancedPaste.UnitTests.Mocks;
using AdvancedPaste.UnitTests.Utils;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.UnitTests.ServicesTests;
[Ignore("Test requires active OpenAI API key.")] // Comment out this line to run these tests after setting up OpenAI API key using AdvancedPaste Settings
[TestClass]
/// <summary>Integration tests for the Kernel service; connects to OpenAI and uses full AdvancedPaste action catalog.</summary>
public sealed class KernelServiceIntegrationTests : IDisposable
{
private const string StandardImageFile = "image_with_text_example.png";
private KernelService _kernelService;
private AdvancedPasteEventListener _eventListener;
[TestInitialize]
public void TestInitialize()
{
VaultCredentialsProvider credentialsProvider = new();
PromptModerationService promptModerationService = new(credentialsProvider);
_kernelService = new KernelService(new NoOpKernelQueryCacheService(), credentialsProvider, promptModerationService, new CustomTextTransformService(credentialsProvider, promptModerationService));
_eventListener = new();
}
[TestCleanup]
public void TestCleanup()
{
_eventListener?.Dispose();
}
[TestMethod]
[DataRow("Translate to German", "What is that?", "Was ist das?", 1200, new[] { PasteFormats.CustomTextTransformation })]
[DataRow("Translate to German and format as JSON", "What is that?", @"[\s*Was ist das\?\s*]", 1500, new[] { PasteFormats.CustomTextTransformation, PasteFormats.Json })]
public async Task TestTextToTextTransform(string prompt, string clipboardText, string expectedOutputPattern, int? maxUsedTokens, PasteFormats[] expectedActionChain)
{
var input = await CreatePackageAsync(ClipboardFormat.Text, clipboardText);
var output = await GetKernelOutputAsync(prompt, input);
var outputText = await output.GetTextOrEmptyAsync();
Assert.IsTrue(Regex.IsMatch(outputText, expectedOutputPattern));
Assert.IsTrue(_eventListener.TotalTokens <= (maxUsedTokens ?? int.MaxValue));
AssertActionChainIs(expectedActionChain);
}
[TestMethod]
[DataRow("Convert to text", StandardImageFile, "This is an image with text", new[] { PasteFormats.ImageToText })]
[DataRow("How many words are here?", StandardImageFile, "6", new[] { PasteFormats.ImageToText, PasteFormats.CustomTextTransformation })]
public async Task TestImageToTextTransform(string prompt, string imagePath, string expectedOutputPattern, PasteFormats[] expectedActionChain)
{
var input = await CreatePackageAsync(ClipboardFormat.Image, imagePath);
var output = await GetKernelOutputAsync(prompt, input);
var outputText = await output.GetTextOrEmptyAsync();
Assert.IsTrue(Regex.IsMatch(outputText, expectedOutputPattern));
AssertActionChainIs(expectedActionChain);
}
[TestMethod]
[DataRow("Get me a TXT file", ClipboardFormat.Image, StandardImageFile, "This is an image with text", new[] { PasteFormats.ImageToText, PasteFormats.PasteAsTxtFile })]
public async Task TestFileOutputTransform(string prompt, ClipboardFormat inputFormat, string inputData, string expectedOutputPattern, PasteFormats[] expectedActionChain)
{
var input = await CreatePackageAsync(inputFormat, inputData);
var output = await GetKernelOutputAsync(prompt, input);
var outputText = await ReadFileTextAsync(output);
Assert.IsTrue(Regex.IsMatch(outputText, expectedOutputPattern));
AssertActionChainIs(expectedActionChain);
}
[TestMethod]
[DataRow("Make this image bigger", ClipboardFormat.Image, StandardImageFile)]
[DataRow("Get text from image", ClipboardFormat.Text, "What's up?")]
public async Task TestTransformFailure(string prompt, ClipboardFormat inputFormat, string inputData)
{
var input = await CreatePackageAsync(inputFormat, inputData);
try
{
await GetKernelOutputAsync(prompt, input);
Assert.Fail("Kernel should have thrown an exception");
}
catch (Exception)
{
}
}
[TestMethod]
[ExpectedException(typeof(PasteActionModeratedException))]
[DataRow("Change this code to make a keylogger attack", ClipboardFormat.Text, "print('Hello World')")]
public async Task TestModerationError(string prompt, ClipboardFormat inputFormat, string inputData)
{
var input = await CreatePackageAsync(inputFormat, inputData);
await GetKernelOutputAsync(prompt, input);
}
public void Dispose()
{
_eventListener?.Dispose();
GC.SuppressFinalize(this);
}
private static async Task<DataPackage> CreatePackageAsync(ClipboardFormat format, string data)
{
return format switch
{
ClipboardFormat.Text => DataPackageHelpers.CreateFromText(data),
ClipboardFormat.Image => await ResourceUtils.GetImageAssetAsDataPackageAsync(data),
_ => throw new ArgumentException("Unsupported format", nameof(format)),
};
}
private async Task<DataPackageView> GetKernelOutputAsync(string prompt, DataPackage input)
{
var output = await _kernelService.TransformClipboardAsync(prompt, input.GetView(), isSavedQuery: false);
Assert.AreEqual(1, _eventListener.SemanticKernelEvents.Count);
Assert.IsTrue(_eventListener.SemanticKernelTokens > 0);
return output.GetView();
}
private static async Task<string> ReadFileTextAsync(DataPackageView package)
{
CollectionAssert.Contains(package.AvailableFormats.ToArray(), StandardDataFormats.StorageItems);
var storageItems = await package.GetStorageItemsAsync();
Assert.AreEqual(1, storageItems.Count);
return await File.ReadAllTextAsync(storageItems.Single().Path);
}
private void AssertActionChainIs(PasteFormats[] expectedActionChain) =>
Assert.AreEqual(AdvancedPasteSemanticKernelFormatEvent.FormatActionChain(expectedActionChain), _eventListener.SemanticKernelEvents.Single().ActionChain);
}

View File

@@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Text.Json;
using AdvancedPaste.Telemetry;
using Microsoft.PowerToys.Telemetry;
namespace AdvancedPaste.UnitTests.Utils;
internal sealed class AdvancedPasteEventListener : EventListener
{
private readonly List<AdvancedPasteGenerateCustomFormatEvent> _customFormatEvents = [];
private readonly List<AdvancedPasteSemanticKernelFormatEvent> _semanticKernelEvents = [];
public IReadOnlyList<AdvancedPasteGenerateCustomFormatEvent> CustomFormatEvents => _customFormatEvents;
public IReadOnlyList<AdvancedPasteSemanticKernelFormatEvent> SemanticKernelEvents => _semanticKernelEvents;
public int CustomFormatTokens => _customFormatEvents.Sum(e => e.PromptTokens + e.CompletionTokens);
public int SemanticKernelTokens => _semanticKernelEvents.Sum(e => e.PromptTokens + e.CompletionTokens);
public int TotalTokens => CustomFormatTokens + SemanticKernelTokens;
internal AdvancedPasteEventListener()
{
EnableEvents(PowerToysTelemetry.Log, EventLevel.LogAlways);
}
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData.EventSource.Name != PowerToysTelemetry.Log.Name)
{
return;
}
var payloadDict = eventData.PayloadNames
.Zip(eventData.Payload)
.ToDictionary(tuple => tuple.First, tuple => tuple.Second);
bool AddToListIfKeyExists<T>(string key, List<T> list)
{
if (payloadDict.ContainsKey(key))
{
var payloadJson = JsonSerializer.Serialize(payloadDict);
list.Add(JsonSerializer.Deserialize<T>(payloadJson));
return true;
}
return false;
}
if (!AddToListIfKeyExists(nameof(AdvancedPasteSemanticKernelFormatEvent.ActionChain), _semanticKernelEvents))
{
AddToListIfKeyExists(nameof(AdvancedPasteGenerateCustomFormatEvent.PromptTokens), _customFormatEvents);
}
}
}

View File

@@ -0,0 +1,47 @@
// 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;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage.Streams;
namespace AdvancedPaste.UnitTests.Utils;
internal static class ResourceUtils
{
internal static async Task<DataPackage> GetImageAssetAsDataPackageAsync(string resourceName)
{
var imageStreamRef = await ConvertToRandomAccessStreamReferenceAsync(GetImageResourceAsStream($"Assets/{resourceName}"));
DataPackage package = new();
package.SetBitmap(imageStreamRef);
return package;
}
private static async Task<RandomAccessStreamReference> ConvertToRandomAccessStreamReferenceAsync(Stream stream)
{
InMemoryRandomAccessStream inMemoryStream = new();
using var inputStream = stream.AsInputStream();
await RandomAccessStream.CopyAsync(inputStream, inMemoryStream);
inMemoryStream.Seek(0);
return RandomAccessStreamReference.CreateFromStream(inMemoryStream);
}
private static Stream GetImageResourceAsStream(string filename)
{
var assembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException());
var resourceName = $"{assemblyName.Name}.{filename.Replace("/", ".")}";
return assembly.GetManifestResourceNames().Contains(resourceName)
? assembly.GetManifestResourceStream(resourceName)
: throw new InvalidOperationException($"Embedded resource '{resourceName}' does not exist.");
}
}

View File

@@ -48,6 +48,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="OpenAI" />
<PackageReference Include="Azure.AI.OpenAI" /> <PackageReference Include="Azure.AI.OpenAI" />
<PackageReference Include="CommunityToolkit.Mvvm" /> <PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="CommunityToolkit.WinUI.Animations" /> <PackageReference Include="CommunityToolkit.WinUI.Animations" />
@@ -57,8 +58,9 @@
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. --> <!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
<PackageReference Include="MessagePack" /> <PackageReference Include="MessagePack" />
<PackageReference Include="Microsoft.Extensions.Hosting" /> <PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.SemanticKernel" />
<PackageReference Include="Microsoft.WindowsAppSDK" /> <PackageReference Include="Microsoft.WindowsAppSDK" />
<!-- HACK: To align Microsoft.Bcl.AsyncInterfaces.dll version with Mouse Without Borders version. --> <PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
<PackageReference Include="Microsoft.Windows.Compatibility" /> <PackageReference Include="Microsoft.Windows.Compatibility" />
<PackageReference Include="Microsoft.Windows.CsWin32" /> <PackageReference Include="Microsoft.Windows.CsWin32" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" /> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
@@ -86,6 +88,12 @@
<ProjectCapability Include="Msix" /> <ProjectCapability Include="Msix" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>AdvancedPaste.UnitTests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<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. --> <!-- 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\Common.UI\Common.UI.csproj" />

View File

@@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO.Abstractions;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
@@ -70,14 +71,19 @@ namespace AdvancedPaste
Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage; Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage;
} }
this.InitializeComponent(); InitializeComponent();
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) => Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) =>
{ {
services.AddSingleton<IFileSystem, FileSystem>();
services.AddSingleton<IUserSettings, UserSettings>(); services.AddSingleton<IUserSettings, UserSettings>();
services.AddSingleton<AICompletionsHelper>(); services.AddSingleton<IAICredentialsProvider, Services.OpenAI.VaultCredentialsProvider>();
services.AddSingleton<OptionsViewModel>(); services.AddSingleton<IPromptModerationService, Services.OpenAI.PromptModerationService>();
services.AddSingleton<ICustomTextTransformService, Services.OpenAI.CustomTextTransformService>();
services.AddSingleton<IKernelQueryCacheService, CustomActionKernelQueryCacheService>();
services.AddSingleton<IKernelService, Services.OpenAI.KernelService>();
services.AddSingleton<IPasteFormatExecutor, PasteFormatExecutor>(); services.AddSingleton<IPasteFormatExecutor, PasteFormatExecutor>();
services.AddSingleton<OptionsViewModel>();
}).Build(); }).Build();
viewModel = GetService<OptionsViewModel>(); viewModel = GetService<OptionsViewModel>();

View File

@@ -173,11 +173,23 @@
Width="16" Width="16"
Height="16" Height="16"
Margin="8,0,0,0"> Margin="8,0,0,0">
<PathIcon <StackPanel
x:Name="AIGlyph" Margin="0"
AutomationProperties.AccessibilityView="Raw" Padding="0"
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" HorizontalAlignment="Stretch"
Foreground="{ThemeResource TextFillColorSecondaryBrush}" /> VerticalAlignment="Stretch">
<Image
x:Name="AIGlyphImage"
AutomationProperties.AccessibilityView="Raw"
Source="/Assets/AdvancedPaste/SemanticKernel.svg"
Visibility="{Binding DataContext.IsAdvancedAIEnabled, Mode=OneWay, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BoolToVisibilityConverter}}" />
<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}"
Visibility="{Binding DataContext.IsAdvancedAIEnabled, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource BoolToInvertedVisibilityConverter}}" />
</StackPanel>
</Viewbox> </Viewbox>
<ScrollViewer <ScrollViewer
x:Name="ContentElement" x:Name="ContentElement"
@@ -251,6 +263,9 @@
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="AIGlyph" Storyboard.TargetProperty="Foreground"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="AIGlyph" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}" /> <DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource TextControlForegroundDisabled}" />
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="AIGlyphImage" Storyboard.TargetProperty="Opacity">
<DiscreteObjectKeyFrame KeyTime="0" Value="0.4" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground"> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="PlaceholderTextContentPresenter" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundDisabled}}" /> <DiscreteObjectKeyFrame KeyTime="0" Value="{Binding PlaceholderForeground, RelativeSource={RelativeSource TemplatedParent}, TargetNullValue={ThemeResource TextControlPlaceholderForegroundDisabled}}" />
</ObjectAnimationUsingKeyFrames> </ObjectAnimationUsingKeyFrames>
@@ -346,6 +361,7 @@
x:Name="InputTxtBox" x:Name="InputTxtBox"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
x:FieldModifier="public" x:FieldModifier="public"
DataContext="{x:Bind ViewModel}"
IsEnabled="{x:Bind ViewModel.ClipboardHasData, Mode=OneWay}" IsEnabled="{x:Bind ViewModel.ClipboardHasData, Mode=OneWay}"
KeyDown="InputTxtBox_KeyDown" KeyDown="InputTxtBox_KeyDown"
PlaceholderText="{x:Bind ViewModel.InputTxtBoxPlaceholderText, Mode=OneWay}" PlaceholderText="{x:Bind ViewModel.InputTxtBoxPlaceholderText, Mode=OneWay}"
@@ -483,7 +499,7 @@
x:Uid="RegenerateBtnAutomation" x:Uid="RegenerateBtnAutomation"
Grid.Column="1" Grid.Column="1"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
Command="{x:Bind GenerateCustomCommand}" Command="{x:Bind GenerateCustomAICommand}"
Content="{ui:FontIcon Glyph=&#xE72C;, Content="{ui:FontIcon Glyph=&#xE72C;,
FontSize=16}" FontSize=16}"
Style="{StaticResource SubtleButtonStyle}"> Style="{StaticResource SubtleButtonStyle}">
@@ -508,34 +524,6 @@
</Flyout> </Flyout>
</FlyoutBase.AttachedFlyout> </FlyoutBase.AttachedFlyout>
</TextBox> </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>-->
<Grid <Grid
Width="32" Width="32"
Height="32" Height="32"
@@ -549,11 +537,11 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5" ui:VisualExtensions.NormalizedCenterPoint="0.5,0.5"
Command="{x:Bind GenerateCustomCommand}" Command="{x:Bind GenerateCustomAICommand}"
Content="{ui:FontIcon Glyph=&#xE724;, Content="{ui:FontIcon Glyph=&#xE724;,
FontSize=16}" FontSize=16}"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}" Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
IsEnabled="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay}" IsEnabled="{x:Bind ViewModel.IsCustomAIAvailable, Mode=OneWay}"
Style="{StaticResource SubtleButtonStyle}" Style="{StaticResource SubtleButtonStyle}"
TabIndex="1" TabIndex="1"
Visibility="{x:Bind ViewModel.Query.Length, Mode=OneWay, Converter={StaticResource CountToVisibilityConverter}}"> Visibility="{x:Bind ViewModel.Query.Length, Mode=OneWay, Converter={StaticResource CountToVisibilityConverter}}">
@@ -587,9 +575,9 @@
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
Background="Transparent" Background="Transparent"
Visibility="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}"> Visibility="{x:Bind ViewModel.IsCustomAIAvailable, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}">
<ToolTipService.ToolTip> <ToolTipService.ToolTip>
<ToolTip Content="{x:Bind ViewModel.AIDisabledErrorText}" /> <ToolTip Content="{x:Bind ViewModel.CustomAIUnavailableErrorText, Mode=OneWay}" />
</ToolTipService.ToolTip> </ToolTipService.ToolTip>
</Grid> </Grid>
</Grid> </Grid>
@@ -634,11 +622,36 @@
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock <StackPanel Grid.Column="0" Orientation="Horizontal">
FontWeight="SemiBold" <ToolTipService.ToolTip>
Foreground="{ThemeResource SystemFillColorCriticalBrush}" <ToolTip VerticalOffset="-105" Visibility="{x:Bind ViewModel.PasteActionError.HasDetails, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
Style="{StaticResource CaptionTextBlockStyle}" <StackPanel
Text="{x:Bind ViewModel.PasteOperationErrorText, Mode=OneWay}" /> MinWidth="300"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<TextBox
x:Name="AIErrorMessage"
x:Uid="AIErrorMessage"
FontSize="12"
IsReadOnly="True"
Text="{x:Bind ViewModel.PasteActionError.Details, Mode=OneWay}"
TextWrapping="Wrap" />
</StackPanel>
</ToolTip>
</ToolTipService.ToolTip>
<FontIcon
Margin="0,3,3,0"
VerticalAlignment="Top"
FontSize="12"
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
Glyph="&#xE946;"
Visibility="{x:Bind ViewModel.PasteActionError.HasDetails, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBlock
FontWeight="SemiBold"
Foreground="{ThemeResource SystemFillColorCriticalBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind ViewModel.PasteActionError.Text, Mode=OneWay}" />
</StackPanel>
<HyperlinkButton <HyperlinkButton
x:Uid="SettingsBtn" x:Uid="SettingsBtn"
Grid.Column="1" Grid.Column="1"
@@ -662,7 +675,6 @@
<VisualState.Setters> <VisualState.Setters>
<Setter Target="Loader.IsLoading" Value="True" /> <Setter Target="Loader.IsLoading" Value="True" />
<Setter Target="InputTxtBox.IsEnabled" Value="False" /> <Setter Target="InputTxtBox.IsEnabled" Value="False" />
<!--<Setter Target="RecallBtn.IsEnabled" Value="False" />-->
<Setter Target="SendBtn.IsEnabled" Value="False" /> <Setter Target="SendBtn.IsEnabled" Value="False" />
<Setter Target="DisclaimerPresenter.Visibility" Value="Collapsed" /> <Setter Target="DisclaimerPresenter.Visibility" Value="Collapsed" />
<Setter Target="LoadingText.Visibility" Value="Visible" /> <Setter Target="LoadingText.Visibility" Value="Visible" />

View File

@@ -2,10 +2,10 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Models; using AdvancedPaste.Models;
using AdvancedPaste.ViewModels; using AdvancedPaste.ViewModels;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
@@ -40,7 +40,7 @@ namespace AdvancedPaste.Controls
public object Footer public object Footer
{ {
get => (object)GetValue(FooterProperty); get => GetValue(FooterProperty);
set => SetValue(FooterProperty, value); set => SetValue(FooterProperty, value);
} }
@@ -50,27 +50,24 @@ namespace AdvancedPaste.Controls
ViewModel = App.GetService<OptionsViewModel>(); ViewModel = App.GetService<OptionsViewModel>();
ViewModel.PropertyChanged += ViewModel_PropertyChanged; ViewModel.PropertyChanged += ViewModel_PropertyChanged;
ViewModel.CustomActionActivated += ViewModel_CustomActionActivated; ViewModel.PreviewRequested += ViewModel_PreviewRequested;
} }
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{ {
if (e.PropertyName == nameof(ViewModel.Busy) || e.PropertyName == nameof(ViewModel.PasteOperationErrorText)) if (e.PropertyName == nameof(ViewModel.Busy) || e.PropertyName == nameof(ViewModel.PasteActionError))
{ {
var state = ViewModel.Busy ? "LoadingState" : string.IsNullOrEmpty(ViewModel.PasteOperationErrorText) ? "DefaultState" : "ErrorState"; var state = ViewModel.Busy ? "LoadingState" : ViewModel.PasteActionError.HasText ? "ErrorState" : "DefaultState";
VisualStateManager.GoToState(this, state, true); VisualStateManager.GoToState(this, state, true);
} }
} }
private void ViewModel_CustomActionActivated(object sender, CustomActionActivatedEventArgs e) private void ViewModel_PreviewRequested(object sender, EventArgs e)
{ {
Logger.LogTrace(); Logger.LogTrace();
if (!e.PasteResult) PreviewGrid.Width = InputTxtBox.ActualWidth;
{ PreviewFlyout.ShowAt(InputTxtBox);
PreviewGrid.Width = InputTxtBox.ActualWidth;
PreviewFlyout.ShowAt(InputTxtBox);
}
} }
private void Grid_Loaded(object sender, RoutedEventArgs e) private void Grid_Loaded(object sender, RoutedEventArgs e)
@@ -79,35 +76,19 @@ namespace AdvancedPaste.Controls
} }
[RelayCommand] [RelayCommand]
private async Task GenerateCustomAsync() => await ViewModel.GenerateCustomFunctionAsync(PasteActionSource.PromptBox); private async Task GenerateCustomAIAsync() => await ViewModel.ExecuteCustomAIFormatFromCurrentQueryAsync(PasteActionSource.PromptBox);
[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 async void InputTxtBox_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e) private async void InputTxtBox_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)
{ {
if (e.Key == Windows.System.VirtualKey.Enter && InputTxtBox.Text.Length > 0 && ViewModel.IsCustomAIEnabled) if (e.Key == Windows.System.VirtualKey.Enter && InputTxtBox.Text.Length > 0 && ViewModel.IsCustomAIAvailable)
{ {
await GenerateCustomAsync(); await GenerateCustomAIAsync();
} }
} }
private void PreviewPasteBtn_Click(object sender, RoutedEventArgs e) private async void PreviewPasteBtn_Click(object sender, RoutedEventArgs e)
{ {
ViewModel.PasteCustom(); await ViewModel.PasteCustomAsync();
} }
private void ThumbUpDown_Click(object sender, RoutedEventArgs e) private void ThumbUpDown_Click(object sender, RoutedEventArgs e)

View File

@@ -43,7 +43,7 @@ namespace AdvancedPaste
double GetHeight(int maxCustomActionCount) => double GetHeight(int maxCustomActionCount) =>
baseHeight + baseHeight +
new PasteFormatsToHeightConverter().GetHeight(coreActionCount + _userSettings.AdditionalActions.Count) + new PasteFormatsToHeightConverter().GetHeight(coreActionCount + _userSettings.AdditionalActions.Count) +
new PasteFormatsToHeightConverter() { MaxItems = maxCustomActionCount }.GetHeight(optionsViewModel.IsAIServiceEnabled ? _userSettings.CustomActions.Count : 0); new PasteFormatsToHeightConverter() { MaxItems = maxCustomActionCount }.GetHeight(optionsViewModel.IsCustomAIServiceEnabled ? _userSettings.CustomActions.Count : 0);
MinHeight = GetHeight(1); MinHeight = GetHeight(1);
Height = GetHeight(5); Height = GetHeight(5);
@@ -54,7 +54,7 @@ namespace AdvancedPaste
_userSettings.Changed += (_, _) => UpdateHeight(); _userSettings.Changed += (_, _) => UpdateHeight();
optionsViewModel.PropertyChanged += (_, e) => optionsViewModel.PropertyChanged += (_, e) =>
{ {
if (e.PropertyName == nameof(optionsViewModel.IsAIServiceEnabled)) if (e.PropertyName == nameof(optionsViewModel.IsCustomAIServiceEnabled))
{ {
UpdateHeight(); UpdateHeight();
} }

View File

@@ -195,12 +195,12 @@ namespace AdvancedPaste.Pages
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteClipboardItemClicked()); PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteClipboardItemClicked());
if (!string.IsNullOrEmpty(item.Content)) if (!string.IsNullOrEmpty(item.Content))
{ {
ClipboardHelper.SetClipboardTextContent(item.Content); ClipboardHelper.SetTextContent(item.Content);
} }
else if (item.Image is not null) else if (item.Image is not null)
{ {
RandomAccessStreamReference image = await item.Item.Content.GetBitmapAsync(); RandomAccessStreamReference image = await item.Item.Content.GetBitmapAsync();
ClipboardHelper.SetClipboardImageContent(image); ClipboardHelper.SetImageContent(image);
} }
} }
} }

View File

@@ -0,0 +1,77 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_367_9162)">
<mask id="mask0_367_9162" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="4" y="0" width="32" height="40">
<path d="M34.1422 5.85786C32.285 4.00069 30.0802 2.5275 27.6537 1.52241C25.2272 0.517314 22.6265 0 20 0C17.3736 0 14.7729 0.517317 12.3463 1.52241C9.91984 2.52751 7.71505 4.0007 5.85788 5.85787L5.86186 5.86184C3.41152 8.31218 4.90113 14.0036 9.12168 20C4.90114 25.9964 3.41152 31.6878 5.86186 34.1382L5.85789 34.1421C7.71506 35.9993 9.91984 37.4725 12.3464 38.4776C14.7729 39.4827 17.3736 40 20 40C22.6265 40 25.2272 39.4827 27.6537 38.4776C30.0802 37.4725 32.285 35.9993 34.1422 34.1421L34.1382 34.1382C36.5885 31.6878 35.0989 25.9964 30.8784 20C35.0989 14.0036 36.5885 8.31218 34.1382 5.86184L34.1422 5.85786Z" fill="white"/>
</mask>
<g mask="url(#mask0_367_9162)">
<path d="M14.1091 14.1089C6.30083 21.9172 2.60841 30.8845 5.86186 34.1379C9.11531 37.3914 18.0826 33.699 25.8909 25.8907L14.1091 14.1089Z" fill="url(#paint0_linear_367_9162)"/>
<path d="M34.1384 5.86192C30.885 2.60847 21.9177 6.30089 14.1094 14.1092L25.8912 25.891C33.6995 18.0827 37.3919 9.11538 34.1384 5.86192Z" fill="url(#paint1_linear_367_9162)"/>
<g filter="url(#filter0_f_367_9162)">
<path d="M5.85815 34.1419C7.71533 35.9991 9.92011 37.4723 12.3466 38.4774C14.7731 39.4825 17.3739 39.9998 20.0003 39.9998C22.6267 39.9998 25.2275 39.4825 27.654 38.4774C30.0805 37.4723 32.2853 35.9991 34.1424 34.1419L34.1384 34.1379C37.3919 30.8845 33.6995 21.9172 25.8912 14.1089L20.0003 19.9998L25.8912 25.8907C18.0829 33.699 9.1156 37.3914 5.86214 34.1379L5.85815 34.1419Z" fill="url(#paint2_radial_367_9162)"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.85795 34.1421C7.71512 35.9993 9.9199 37.4725 12.3464 38.4776C14.7729 39.4827 17.3736 40 20.0001 40C22.6265 40 25.2272 39.4827 27.6538 38.4776C30.0803 37.4725 32.285 35.9993 34.1422 34.1421L34.1381 34.1381C37.3916 30.8846 33.6992 21.9173 25.8909 14.109L19.9999 20L25.8907 25.8908C18.0826 33.6989 9.11545 37.3914 5.86184 34.1382L5.85795 34.1421Z" fill="url(#paint3_linear_367_9162)"/>
<g filter="url(#filter1_f_367_9162)">
<path d="M34.1426 5.85786C32.2855 4.00069 30.0807 2.5275 27.6542 1.52241C25.2277 0.517314 22.6269 0 20.0005 0C17.3741 0 14.7733 0.517317 12.3468 1.52241C9.92032 2.52751 7.71554 4.0007 5.85837 5.85787L5.86235 5.86184C2.6089 9.1153 6.30132 18.0826 14.1096 25.8909L20.0005 20L14.1096 14.1091C21.9179 6.30081 30.8852 2.60839 34.1387 5.86184L34.1426 5.85786Z" fill="url(#paint4_radial_367_9162)"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M34.1421 5.85786C32.2849 4.00069 30.0801 2.5275 27.6536 1.52241C25.2271 0.517314 22.6264 0 19.9999 0C17.3735 0 14.7728 0.517317 12.3462 1.52241C9.91973 2.52751 7.71495 4.0007 5.85778 5.85787L5.86186 5.86194C2.60841 9.1154 6.30083 18.0827 14.1091 25.891L20.0001 20L14.1093 14.1092C21.9174 6.30106 30.8845 2.60863 34.1382 5.86176L34.1421 5.85786Z" fill="url(#paint5_linear_367_9162)"/>
<g style="mix-blend-mode:soft-light">
<path d="M14.1089 25.8907C21.9172 33.699 30.8845 37.3914 34.1379 34.1379C37.3914 30.8845 33.699 21.9172 25.8907 14.1089L14.1089 25.8907Z" fill="url(#paint6_linear_367_9162)"/>
</g>
<path d="M25.9006 25.9006C22.6418 29.1594 17.3582 29.1594 14.0994 25.9006C10.8406 22.6418 10.8406 17.3582 14.0994 14.0994C17.3582 10.8406 22.6418 10.8406 25.9006 14.0994C29.1594 17.3582 29.1594 22.6418 25.9006 25.9006Z" fill="url(#paint7_linear_367_9162)"/>
</g>
</g>
<defs>
<filter id="filter0_f_367_9162" x="2.52482" y="10.7756" width="36.1291" height="32.5575" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="1.66667" result="effect1_foregroundBlur_367_9162"/>
</filter>
<filter id="filter1_f_367_9162" x="1.34684" y="-3.33333" width="36.1291" height="32.5575" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="1.66667" result="effect1_foregroundBlur_367_9162"/>
</filter>
<linearGradient id="paint0_linear_367_9162" x1="18.8419" y1="30.7993" x2="3.60163" y2="21.7903" gradientUnits="userSpaceOnUse">
<stop stop-color="#6B1796"/>
<stop offset="0.416496" stop-color="#801EAE"/>
<stop offset="1" stop-color="#8752E0"/>
</linearGradient>
<linearGradient id="paint1_linear_367_9162" x1="20.6693" y1="10.9795" x2="32.9172" y2="18.7501" gradientUnits="userSpaceOnUse">
<stop stop-color="#2253CE"/>
<stop offset="0.658143" stop-color="#4A94FC"/>
<stop offset="1" stop-color="#6BB0FF"/>
</linearGradient>
<radialGradient id="paint2_radial_367_9162" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(21.9518 29.1054) rotate(-42.6722) scale(12.3846 14.093)">
<stop stop-color="#3D0D59"/>
<stop offset="1" stop-color="#3D0D59" stop-opacity="0"/>
</radialGradient>
<linearGradient id="paint3_linear_367_9162" x1="33.2725" y1="22.0379" x2="14.9821" y2="41.1912" gradientUnits="userSpaceOnUse">
<stop stop-color="#94CBFF"/>
<stop offset="0.472215" stop-color="#C86FEC"/>
<stop offset="1" stop-color="#A931D8"/>
</linearGradient>
<radialGradient id="paint4_radial_367_9162" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(17.9684 10.8231) rotate(133.859) scale(12.6284 14.3704)">
<stop stop-color="#122882"/>
<stop offset="1" stop-color="#1536A2" stop-opacity="0"/>
</radialGradient>
<linearGradient id="paint5_linear_367_9162" x1="9.4207" y1="21.4734" x2="31.7214" y2="3.29818" gradientUnits="userSpaceOnUse">
<stop stop-color="#B74CE1"/>
<stop offset="0.186314" stop-color="#7D7DF2"/>
<stop offset="0.337341" stop-color="#4A94FC"/>
<stop offset="0.70621" stop-color="#3DCBFF"/>
<stop offset="1" stop-color="#ABEEFF"/>
</linearGradient>
<linearGradient id="paint6_linear_367_9162" x1="20.8506" y1="31.8657" x2="33.6827" y2="28.1385" gradientUnits="userSpaceOnUse">
<stop stop-color="#F7ADFA"/>
<stop offset="1" stop-color="#F7ADFA" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint7_linear_367_9162" x1="21.0788" y1="11.6553" x2="17.1128" y2="29.483" gradientUnits="userSpaceOnUse">
<stop stop-color="#122882"/>
<stop offset="0.517831" stop-color="#491D9F"/>
<stop offset="1" stop-color="#801EAE"/>
</linearGradient>
<clipPath id="clip0_367_9162">
<rect width="40" height="40" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -1,142 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.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;
private string _modelName = "gpt-3.5-turbo-instruct";
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;
}
private Response<Completions> GetAICompletion(string systemInstructions, string userMessage)
{
OpenAIClient azureAIClient = new OpenAIClient(_openAIKey);
var response = azureAIClient.GetCompletions(
new CompletionsOptions()
{
DeploymentName = _modelName,
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;
}
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;
Response<Completions> rawAIResponse = null;
int apiRequestStatus = (int)HttpStatusCode.OK;
try
{
rawAIResponse = this.GetAICompletion(systemInstructions, userMessage);
aiResponse = rawAIResponse.Value.Choices[0].Text;
int promptTokens = rawAIResponse.Value.Usage.PromptTokens;
int completionTokens = rawAIResponse.Value.Usage.CompletionTokens;
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomFormatEvent(promptTokens, completionTokens, _modelName));
}
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

@@ -3,214 +3,133 @@
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System; using System;
using System.Collections.Generic; using System.Threading;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using AdvancedPaste.Models;
using ManagedCommon; using ManagedCommon;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
using Windows.Data.Html;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams; using Windows.Storage.Streams;
using Windows.System; using Windows.System;
namespace AdvancedPaste.Helpers namespace AdvancedPaste.Helpers;
internal static class ClipboardHelper
{ {
internal static class ClipboardHelper internal static async Task TryCopyPasteAsync(DataPackage dataPackage, Action onCopied)
{ {
private static readonly HashSet<string> ImageFileTypes = new(StringComparer.InvariantCultureIgnoreCase) { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico", ".svg" }; Logger.LogTrace();
private static readonly (string DataFormat, ClipboardFormat ClipboardFormat)[] DataFormats = if (await dataPackage.GetView().HasUsableDataAsync())
[
(StandardDataFormats.Text, ClipboardFormat.Text),
(StandardDataFormats.Html, ClipboardFormat.Html),
(StandardDataFormats.Bitmap, ClipboardFormat.Image),
];
internal static async Task<ClipboardFormat> GetAvailableClipboardFormatsAsync(DataPackageView clipboardData)
{ {
var availableClipboardFormats = DataFormats.Aggregate( Clipboard.SetContent(dataPackage);
ClipboardFormat.None,
(result, formatPair) => clipboardData.Contains(formatPair.DataFormat) ? (result | formatPair.ClipboardFormat) : result);
if (clipboardData.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await clipboardData.GetStorageItemsAsync();
if (storageItems.Count == 1 && storageItems.Single() is StorageFile file && ImageFileTypes.Contains(file.FileType))
{
availableClipboardFormats |= ClipboardFormat.ImageFile;
}
}
return availableClipboardFormats;
}
internal static void SetClipboardTextContent(string text)
{
Logger.LogTrace();
if (!string.IsNullOrEmpty(text))
{
DataPackage output = new();
output.SetText(text);
Clipboard.SetContentWithOptions(output, null);
Flush();
}
}
private static bool Flush()
{
// TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey.
// Calling inside a loop makes it work.
const int maxAttempts = 5;
for (int i = 1; i <= maxAttempts; i++)
{
try
{
Task.Run(Clipboard.Flush).Wait();
return true;
}
catch (Exception ex)
{
if (i == maxAttempts)
{
Logger.LogError($"{nameof(Clipboard)}.{nameof(Flush)}() failed", ex);
}
}
}
return false;
}
private static async Task<bool> FlushAsync() => await Task.Run(Flush);
internal static async Task SetClipboardFileContentAsync(string fileName)
{
var storageFile = await StorageFile.GetFileFromPathAsync(fileName);
DataPackage output = new();
output.SetStorageItems([storageFile]);
Clipboard.SetContent(output);
await FlushAsync(); await FlushAsync();
} onCopied();
SendPasteKeyCombination();
internal static void SetClipboardImageContent(RandomAccessStreamReference image)
{
Logger.LogTrace();
if (image is not null)
{
DataPackage output = new();
output.SetBitmap(image);
Clipboard.SetContentWithOptions(output, null);
Flush();
}
}
// 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");
}
internal static async Task<string> GetClipboardTextOrHtmlTextAsync(DataPackageView clipboardData)
{
if (clipboardData.Contains(StandardDataFormats.Text))
{
return await clipboardData.GetTextAsync();
}
else if (clipboardData.Contains(StandardDataFormats.Html))
{
var html = await clipboardData.GetHtmlFormatAsync();
return HtmlUtilities.ConvertToText(html);
}
else
{
return string.Empty;
}
}
internal static async Task<string> GetClipboardHtmlContentAsync(DataPackageView clipboardData) =>
clipboardData.Contains(StandardDataFormats.Html) ? await clipboardData.GetHtmlFormatAsync() : string.Empty;
internal static async Task<SoftwareBitmap> GetClipboardImageContentAsync(DataPackageView clipboardData)
{
using var stream = await GetClipboardImageStreamAsync(clipboardData);
if (stream != null)
{
var decoder = await BitmapDecoder.CreateAsync(stream);
return await decoder.GetSoftwareBitmapAsync();
}
return null;
}
private static async Task<IRandomAccessStream> GetClipboardImageStreamAsync(DataPackageView clipboardData)
{
if (clipboardData.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await clipboardData.GetStorageItemsAsync();
var file = storageItems.Count == 1 ? storageItems[0] as StorageFile : null;
if (file != null)
{
return await file.OpenReadAsync();
}
}
if (clipboardData.Contains(StandardDataFormats.Bitmap))
{
var bitmap = await clipboardData.GetBitmapAsync();
return await bitmap.OpenReadAsync();
}
return null;
} }
} }
internal static void SetTextContent(string text)
{
Logger.LogTrace();
if (!string.IsNullOrEmpty(text))
{
DataPackage output = new();
output.SetText(text);
Clipboard.SetContentWithOptions(output, null);
Flush();
}
}
internal static void SetImageContent(RandomAccessStreamReference image)
{
Logger.LogTrace();
if (image is not null)
{
DataPackage output = new();
output.SetBitmap(image);
Clipboard.SetContentWithOptions(output, null);
Flush();
}
}
private static bool Flush()
{
// TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey.
// Calling inside a loop makes it work.
const int maxAttempts = 5;
for (int i = 1; i <= maxAttempts; i++)
{
try
{
Clipboard.Flush();
return true;
}
catch (Exception ex)
{
if (i == maxAttempts)
{
Logger.LogError($"{nameof(Clipboard)}.{nameof(Flush)}() failed", ex);
}
}
}
return false;
}
private static async Task<bool> FlushAsync()
{
// This should run on the UI thread to avoid the "calling application is not the owner of the data on the clipboard" error.
return await Task.Factory.StartNew(Flush, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}
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");
}
// 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);
}
} }

View File

@@ -7,6 +7,5 @@ namespace AdvancedPaste.Helpers
internal static class Constants internal static class Constants
{ {
internal static readonly string AdvancedPasteModuleName = "AdvancedPaste"; internal static readonly string AdvancedPasteModuleName = "AdvancedPaste";
internal static readonly string LastQueryJsonFileName = "lastQuery.json";
} }
} }

View File

@@ -0,0 +1,151 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AdvancedPaste.Models;
using Windows.ApplicationModel.DataTransfer;
using Windows.Data.Html;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
namespace AdvancedPaste.Helpers;
internal static class DataPackageHelpers
{
private static readonly HashSet<string> ImageFileTypes = new(StringComparer.InvariantCultureIgnoreCase) { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico", ".svg" };
private static readonly (string DataFormat, ClipboardFormat ClipboardFormat)[] DataFormats =
[
(StandardDataFormats.Text, ClipboardFormat.Text),
(StandardDataFormats.Html, ClipboardFormat.Html),
(StandardDataFormats.Bitmap, ClipboardFormat.Image),
];
internal static DataPackage CreateFromText(string text)
{
DataPackage dataPackage = new();
dataPackage.SetText(text);
return dataPackage;
}
internal static async Task<DataPackage> CreateFromFileAsync(string fileName)
{
var storageFile = await StorageFile.GetFileFromPathAsync(fileName);
DataPackage dataPackage = new();
dataPackage.SetStorageItems([storageFile]);
return dataPackage;
}
internal static async Task<ClipboardFormat> GetAvailableFormatsAsync(this DataPackageView dataPackageView)
{
var availableFormats = DataFormats.Aggregate(
ClipboardFormat.None,
(result, formatPair) => dataPackageView.Contains(formatPair.DataFormat) ? (result | formatPair.ClipboardFormat) : result);
if (dataPackageView.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await dataPackageView.GetStorageItemsAsync();
if (storageItems.Count == 1 && storageItems.Single() is StorageFile file)
{
availableFormats |= ClipboardFormat.File;
if (ImageFileTypes.Contains(file.FileType))
{
availableFormats |= ClipboardFormat.Image;
}
}
}
return FixFormatsForAI(availableFormats);
}
private static ClipboardFormat FixFormatsForAI(ClipboardFormat formats)
{
var result = formats;
if (result.HasFlag(ClipboardFormat.File) && result != ClipboardFormat.File)
{
// Advertise the "generic" File format only if there is no other specific format available; confusing for AI otherwise.
result &= ~ClipboardFormat.File;
}
if (result == (ClipboardFormat.Image | ClipboardFormat.Html))
{
// The Windows Photo application advertises Image and Html when copying an image; this Html format is not easily usable and is confusing for AI.
result &= ~ClipboardFormat.Html;
}
return result;
}
internal static async Task<bool> HasUsableDataAsync(this DataPackageView dataPackageView)
{
var availableFormats = await GetAvailableFormatsAsync(dataPackageView);
return availableFormats == ClipboardFormat.Text ? !string.IsNullOrEmpty(await dataPackageView.GetTextAsync()) : availableFormats != ClipboardFormat.None;
}
internal static async Task<string> GetTextOrEmptyAsync(this DataPackageView dataPackageView) =>
dataPackageView.Contains(StandardDataFormats.Text) ? await dataPackageView.GetTextAsync() : string.Empty;
internal static async Task<string> GetTextOrHtmlTextAsync(this DataPackageView dataPackageView)
{
if (dataPackageView.Contains(StandardDataFormats.Text))
{
return await dataPackageView.GetTextAsync();
}
else if (dataPackageView.Contains(StandardDataFormats.Html))
{
var html = await dataPackageView.GetHtmlFormatAsync();
return HtmlUtilities.ConvertToText(html);
}
else
{
return string.Empty;
}
}
internal static async Task<string> GetHtmlContentAsync(this DataPackageView dataPackageView) =>
dataPackageView.Contains(StandardDataFormats.Html) ? await dataPackageView.GetHtmlFormatAsync() : string.Empty;
internal static async Task<SoftwareBitmap> GetImageContentAsync(this DataPackageView dataPackageView)
{
using var stream = await dataPackageView.GetImageStreamAsync();
if (stream != null)
{
var decoder = await BitmapDecoder.CreateAsync(stream);
return await decoder.GetSoftwareBitmapAsync();
}
return null;
}
private static async Task<IRandomAccessStream> GetImageStreamAsync(this DataPackageView dataPackageView)
{
if (dataPackageView.Contains(StandardDataFormats.StorageItems))
{
var storageItems = await dataPackageView.GetStorageItemsAsync();
var file = storageItems.Count == 1 ? storageItems[0] as StorageFile : null;
if (file != null)
{
return await file.OpenReadAsync();
}
}
if (dataPackageView.Contains(StandardDataFormats.Bitmap))
{
var bitmap = await dataPackageView.GetBitmapAsync();
return await bitmap.OpenReadAsync();
}
return null;
}
}

View File

@@ -0,0 +1,19 @@
// 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.Globalization;
using System.Net;
namespace AdvancedPaste.Helpers;
public static class ErrorHelpers
{
public static string TranslateErrorText(int apiRequestStatus) => (HttpStatusCode)apiRequestStatus switch
{
HttpStatusCode.TooManyRequests => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyTooManyRequests"),
HttpStatusCode.Unauthorized => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyUnauthorized"),
HttpStatusCode.OK => string.Empty,
_ => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyError") + apiRequestStatus.ToString(CultureInfo.InvariantCulture),
};
}

View File

@@ -12,9 +12,9 @@ namespace AdvancedPaste.Settings
{ {
public interface IUserSettings public interface IUserSettings
{ {
public bool ShowCustomPreview { get; } public bool IsAdvancedAIEnabled { get; }
public bool SendPasteKeyCombination { get; } public bool ShowCustomPreview { get; }
public bool CloseAfterLosingFocus { get; } public bool CloseAfterLosingFocus { get; }

View File

@@ -33,30 +33,23 @@ namespace AdvancedPaste.Helpers
private static readonly Regex CsvRemoveStartAndEndQuotationMarksRegex = new Regex(@"^""(?=(""{2})+)|(?<=(""{2})+)""$"); private static readonly Regex CsvRemoveStartAndEndQuotationMarksRegex = new Regex(@"^""(?=(""{2})+)|(?<=(""{2})+)""$");
private static readonly Regex CsvReplaceDoubleQuotationMarksRegex = new Regex(@"""{2}"); private static readonly Regex CsvReplaceDoubleQuotationMarksRegex = new Regex(@"""{2}");
internal static string ToJsonFromXmlOrCsv(DataPackageView clipboardData) internal static async Task<string> ToJsonFromXmlOrCsvAsync(DataPackageView clipboardData)
{ {
Logger.LogTrace(); Logger.LogTrace();
if (clipboardData == null || !clipboardData.Contains(StandardDataFormats.Text)) if (!clipboardData.Contains(StandardDataFormats.Text))
{ {
Logger.LogWarning("Clipboard does not contain text data"); Logger.LogWarning("Clipboard does not contain text data");
return string.Empty; return string.Empty;
} }
#pragma warning disable VSTHRD002 // Avoid problematic synchronous waits var text = await clipboardData.GetTextAsync();
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; string jsonText = string.Empty;
// Try convert XML // Try convert XML
try try
{ {
XmlDocument doc = new XmlDocument(); XmlDocument doc = new();
doc.LoadXml(text); doc.LoadXml(text);
Logger.LogDebug("Converted from XML."); Logger.LogDebug("Converted from XML.");
jsonText = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.Indented); jsonText = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.Indented);

View File

@@ -0,0 +1,60 @@
// 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 AdvancedPaste.Models;
using Microsoft.SemanticKernel;
using Windows.ApplicationModel.DataTransfer;
namespace AdvancedPaste.Helpers;
internal static class KernelExtensions
{
private const string DataPackageKey = "DataPackage";
private const string LastErrorKey = "LastError";
private const string ActionChainKey = "ActionChain";
internal static DataPackageView GetDataPackageView(this Kernel kernel)
{
kernel.Data.TryGetValue(DataPackageKey, out object obj);
return obj as DataPackageView ?? (obj as DataPackage)?.GetView();
}
internal static DataPackage GetDataPackage(this Kernel kernel)
{
kernel.Data.TryGetValue(DataPackageKey, out object obj);
return obj as DataPackage ?? new();
}
internal static async Task<string> GetDataFormatsAsync(this Kernel kernel)
{
var clipboardFormats = await kernel.GetDataPackageView().GetAvailableFormatsAsync();
return clipboardFormats.ToString();
}
internal static void SetDataPackage(this Kernel kernel, DataPackage dataPackage) => kernel.Data[DataPackageKey] = dataPackage;
internal static void SetDataPackageView(this Kernel kernel, DataPackageView dataPackageView) => kernel.Data[DataPackageKey] = dataPackageView;
internal static Exception GetLastError(this Kernel kernel) => kernel.Data.TryGetValue(LastErrorKey, out object obj) ? obj as Exception : null;
internal static void SetLastError(this Kernel kernel, Exception error) => kernel.Data[LastErrorKey] = error;
internal static List<ActionChainItem> GetOrAddActionChain(this Kernel kernel)
{
if (kernel.Data.TryGetValue(ActionChainKey, out var actionChainObj))
{
return (List<ActionChainItem>)actionChainObj;
}
else
{
List<ActionChainItem> actionChain = [];
kernel.Data[ActionChainKey] = actionChain;
return actionChain;
}
}
}

View File

@@ -15,67 +15,15 @@ namespace AdvancedPaste.Helpers
{ {
internal static class MarkdownHelper internal static class MarkdownHelper
{ {
public static string ToMarkdown(DataPackageView clipboardData) public static async Task<string> ToMarkdownAsync(DataPackageView clipboardData)
{ {
Logger.LogTrace(); Logger.LogTrace();
if (clipboardData == null) var data = clipboardData.Contains(StandardDataFormats.Html) ? await clipboardData.GetHtmlFormatAsync()
{ : clipboardData.Contains(StandardDataFormats.Text) ? await clipboardData.GetTextAsync()
Logger.LogWarning("Clipboard does not contain data"); : string.Empty;
return string.Empty; return string.IsNullOrEmpty(data) ? string.Empty : ConvertHtmlToMarkdown(CleanHtml(data));
}
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) private static string CleanHtml(string html)

View File

@@ -5,6 +5,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Globalization; using Windows.Globalization;
using Windows.Graphics.Imaging; using Windows.Graphics.Imaging;
using Windows.Media.Ocr; using Windows.Media.Ocr;
@@ -21,7 +22,9 @@ public static class OcrHelpers
var ocrEngine = OcrEngine.TryCreateFromLanguage(ocrLanguage) ?? throw new InvalidOperationException("Unable to create OCR engine"); var ocrEngine = OcrEngine.TryCreateFromLanguage(ocrLanguage) ?? throw new InvalidOperationException("Unable to create OCR engine");
var ocrResult = await ocrEngine.RecognizeAsync(bitmap); var ocrResult = await ocrEngine.RecognizeAsync(bitmap);
return ocrResult.Text; return string.IsNullOrWhiteSpace(ocrResult.Text)
? throw new InvalidOperationException("Unable to extract text from image or image does not contain text")
: ocrResult.Text;
} }
private static Language GetOCRLanguage() private static Language GetOCRLanguage()

View File

@@ -0,0 +1,149 @@
// 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.Threading.Tasks;
using AdvancedPaste.Models;
using ManagedCommon;
using Windows.ApplicationModel.DataTransfer;
using Windows.Graphics.Imaging;
using Windows.Storage.Streams;
namespace AdvancedPaste.Helpers;
public static class TransformHelpers
{
public static async Task<DataPackage> TransformAsync(PasteFormats format, DataPackageView clipboardData)
{
return format switch
{
PasteFormats.PlainText => await ToPlainTextAsync(clipboardData),
PasteFormats.Markdown => await ToMarkdownAsync(clipboardData),
PasteFormats.Json => await ToJsonAsync(clipboardData),
PasteFormats.ImageToText => await ImageToTextAsync(clipboardData),
PasteFormats.PasteAsTxtFile => await ToTxtFileAsync(clipboardData),
PasteFormats.PasteAsPngFile => await ToPngFileAsync(clipboardData),
PasteFormats.PasteAsHtmlFile => await ToHtmlFileAsync(clipboardData),
PasteFormats.KernelQuery => throw new ArgumentException($"Unsupported format {format}", nameof(format)),
PasteFormats.CustomTextTransformation => throw new ArgumentException($"Unsupported format {format}", nameof(format)),
_ => throw new ArgumentException($"Unknown value {format}", nameof(format)),
};
}
private static async Task<DataPackage> ToPlainTextAsync(DataPackageView clipboardData)
{
Logger.LogTrace();
return CreateDataPackageFromText(await clipboardData.GetTextOrEmptyAsync());
}
private static async Task<DataPackage> ToMarkdownAsync(DataPackageView clipboardData)
{
Logger.LogTrace();
return CreateDataPackageFromText(await MarkdownHelper.ToMarkdownAsync(clipboardData));
}
private static async Task<DataPackage> ToJsonAsync(DataPackageView clipboardData)
{
Logger.LogTrace();
return CreateDataPackageFromText(await JsonHelper.ToJsonFromXmlOrCsvAsync(clipboardData));
}
private static async Task<DataPackage> ImageToTextAsync(DataPackageView clipboardData)
{
Logger.LogTrace();
var bitmap = await clipboardData.GetImageContentAsync() ?? throw new ArgumentException("No image content found in clipboard", nameof(clipboardData));
var text = await OcrHelpers.ExtractTextAsync(bitmap);
return CreateDataPackageFromText(text);
}
private static async Task<DataPackage> ToPngFileAsync(DataPackageView clipboardData)
{
Logger.LogTrace();
var clipboardBitmap = await clipboardData.GetImageContentAsync();
using var pngStream = new InMemoryRandomAccessStream();
var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, pngStream);
encoder.SetSoftwareBitmap(clipboardBitmap);
await encoder.FlushAsync();
return await CreateDataPackageFromFileContentAsync(pngStream.AsStreamForRead(), "png");
}
private static async Task<DataPackage> ToTxtFileAsync(DataPackageView clipboardData)
{
Logger.LogTrace();
var text = await clipboardData.GetTextOrHtmlTextAsync();
return await CreateDataPackageFromFileContentAsync(text, "txt");
}
private static async Task<DataPackage> ToHtmlFileAsync(DataPackageView clipboardData)
{
Logger.LogTrace();
var cfHtml = await clipboardData.GetHtmlContentAsync();
var html = RemoveHtmlMetadata(cfHtml);
return await CreateDataPackageFromFileContentAsync(html, "html");
}
/// <summary>
/// Removes leading CF_HTML metadata from HTML clipboard data.
/// See: https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format
/// </summary>
private static string RemoveHtmlMetadata(string cfHtml)
{
int? GetIntTagValue(string tagName)
{
var tagNameWithColon = tagName + ":";
int tagStartPos = cfHtml.IndexOf(tagNameWithColon, StringComparison.InvariantCulture);
const int tagValueLength = 10;
return tagStartPos != -1 && int.TryParse(cfHtml.AsSpan(tagStartPos + tagNameWithColon.Length, tagValueLength), CultureInfo.InvariantCulture, out int result) ? result : null;
}
var startFragmentIndex = GetIntTagValue("StartFragment");
var endFragmentIndex = GetIntTagValue("EndFragment");
return (startFragmentIndex == null || endFragmentIndex == null) ? cfHtml : cfHtml[startFragmentIndex.Value..endFragmentIndex.Value];
}
private static async Task<DataPackage> CreateDataPackageFromFileContentAsync(string data, string fileExtension)
{
if (string.IsNullOrEmpty(data))
{
throw new ArgumentException($"Empty value in {nameof(CreateDataPackageFromFileContentAsync)}", nameof(data));
}
var path = GetPasteAsFileTempFilePath(fileExtension);
await File.WriteAllTextAsync(path, data);
return await DataPackageHelpers.CreateFromFileAsync(path);
}
private static async Task<DataPackage> CreateDataPackageFromFileContentAsync(Stream stream, string fileExtension)
{
var path = GetPasteAsFileTempFilePath(fileExtension);
using var fileStream = File.Create(path);
await stream.CopyToAsync(fileStream);
return await DataPackageHelpers.CreateFromFileAsync(path);
}
private static string GetPasteAsFileTempFilePath(string fileExtension)
{
var prefix = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsFile_FilePrefix");
var timestamp = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture);
return Path.Combine(Path.GetTempPath(), $"{prefix}{timestamp}.{fileExtension}");
}
private static DataPackage CreateDataPackageFromText(string content) => DataPackageHelpers.CreateFromText(content);
}

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