Advanced Paste: Add Python Script Extension Support#49034
Conversation
Enable users to write custom clipboard transformation scripts in Python that integrate directly into the Advanced Paste menu. Scripts are auto- discovered from a configurable folder and can run on native Windows Python or inside WSL. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ion, docs - Restore FuzzTests to use UTF-8 decoding and GetAwaiter().GetResult() - Restore defensive try/catch in JsonHelper.ToJsonFromXmlOrCsvAsync - Add null guard for ReadMetadata in PasteFormatExecutor - Restore is_enabled_by_default() override in dllmain.cpp - Localize hard-coded strings in AdvancedPastePage.xaml via x:Uid - Fix _runner.py docstring to include audio/video input types - Fix typos in UITestAdvancedPaste.md - Filter Settings script list to match runtime discovery behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…emove unused file write - Register Python script settings types in SettingsSerializationContext for AOT safety - Align Settings discovery with runtime: scan entire file, reject multi-function scripts - Remove unused ap_input.json file write on Windows execution path (stdin is used) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s format matching - Remove KernelFunctionDescription from PythonScript to prevent broken AI kernel registration - Normalize file/files format names in _runner.py so files-input scripts match correctly Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…base) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8a07a6e to
950ee6a
Compare
There was a problem hiding this comment.
Pull request overview
Adds a Python-based extension mechanism to the Advanced Paste module so users can drop .py scripts into a folder and have them appear as additional paste actions (Windows Python or WSL), with trust prompting, dependency install prompts, and Settings UI integration.
Changes:
- Introduces a Python script discovery/execution pipeline (C# service + bundled
_runner.py) with Windows/WSL runtimes, timeout/error handling, and dependency installation. - Extends Settings UI + settings schema to configure Python script mode, paths, WSL distro selection, and to list discovered scripts.
- Adds new paste formats plumbing to surface discovered scripts in the Advanced Paste UI, plus unit tests and documentation.
Reviewed changes
Copilot reviewed 38 out of 38 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs | Adds Settings-side Python scripts mode/config, WSL distro enumeration, and script discovery for the Settings page. |
| src/settings-ui/Settings.UI/Strings/en-us/Resources.resw | Adds localized strings for the new Python scripts Settings UI. |
| src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs | Wires new UI actions (browse/open/refresh) and WSL distro refresh on page load. |
| src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml | Adds the Python scripts settings group (mode dropdown, folder/path pickers, distro picker, discovered list). |
| src/settings-ui/Settings.UI.UnitTests/ViewModelTests/AdvancedPaste.cs | Adds a unit test validating Settings-side metadata parsing from .py headers/function name. |
| src/settings-ui/Settings.UI.Library/SettingsSerializationContext.cs | Registers new settings types for source-generated JSON serialization. |
| src/settings-ui/Settings.UI.Library/PythonScriptWslSettings.cs | Adds WSL-specific settings payload (folder + distro). |
| src/settings-ui/Settings.UI.Library/PythonScriptWindowsSettings.cs | Adds Windows-specific settings payload (folder + python path). |
| src/settings-ui/Settings.UI.Library/AdvancedPastePythonScriptSettings.cs | Adds the new python-scripts settings schema + legacy migration helper. |
| src/settings-ui/Settings.UI.Library/AdvancedPastePythonScriptAction.cs | Adds the persisted per-script settings/action model (path, hotkey, show/enabled, formats, etc.). |
| src/settings-ui/Settings.UI.Library/AdvancedPasteProperties.cs | Adds python-scripts to module settings and removes ShowAIPaste. |
| src/modules/AdvancedPaste/UITest-AdvancedPaste/TestFiles/settings.json | Updates UI test settings payload for removed ShowAIPaste. |
| src/modules/AdvancedPaste/UITest-AdvancedPaste/AdvancedPaste-UITests.csproj | Adds additional package references for the UI test project. |
| src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp | Refactors auto-copy logic (WM_COPY + SendInput fallback) and reduces logging. |
| src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs | Adds python-script paste formats to the menu and integrates script discovery via IPythonScriptService. |
| src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw | Adds user-facing error/trust/dependency-install strings for Python script execution. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PythonScripts/PythonScriptTrustService.cs | Implements trust + package-install consent dialogs and script hashing. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PythonScripts/PythonScriptService.cs | Core runtime: metadata parsing, dependency detection/install, Windows+WSL execution, path translation, error parsing. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PythonScripts/PythonScriptMetadata.cs | Adds metadata record describing a discovered script. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PythonScripts/PythonRequirement.cs | Adds requirement record representing import↔pip mapping. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PythonScripts/IPythonScriptTrustService.cs | Defines trust/dependency install consent contract. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PythonScripts/IPythonScriptService.cs | Defines script execution/discovery/dependency management contract. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PythonScripts/_runner.py | Adds the shipped Python runner that loads the user script and performs JSON I/O. |
| src/modules/AdvancedPaste/AdvancedPaste/Services/PasteFormatExecutor.cs | Hooks PasteFormats.PythonScript execution (trust prompt, dependency preflight, invoke service). |
| src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormats.cs | Adds the PythonScript paste format metadata. |
| src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormat.cs | Adds factory method to build Python script paste formats (name + path). |
| src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs | Consumes new python-scripts settings, tracks trust hashes, and watches scripts folder for changes. |
| src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs | Extends user settings contract for python-scripts configuration + trust storage. |
| src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml.cs | Adds “show error details” dialog flow for script errors. |
| src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml | Adds script formats list section and refactors error presentation + prompt footer visibility. |
| src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs | Updates height calculations to include Python script formats. |
| src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml | Removes embedded error grid (moved to MainPage). |
| src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/ClipboardHistoryItemPreviewControl.xaml | Adjusts bindings to OneWay for updated UI behavior. |
| src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs | Registers new Python script services in DI. |
| src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj | Ships _runner.py as content in output. |
| src/modules/AdvancedPaste/AdvancedPaste.UnitTests/ServicesTests/PythonScriptServiceTests.cs | Adds unit tests for import detection, metadata parsing, and error parsing helpers. |
| src/modules/AdvancedPaste/AdvancedPaste.UnitTests/Mocks/IntegrationTestUserSettings.cs | Updates test mock to satisfy new IUserSettings contract. |
| doc/devdocs/modules/advancedpaste-python-scripts.md | Adds developer documentation for the Python scripts feature and script conventions. |
- Fix ScriptsFolder to fall back to default folder when setting is blank - Align Settings tag parsing with runtime: use @advancedpaste:disabled - Remove misleading 'written back to headers' description string - Add modifier release/restore to SendInput Ctrl+C path (matches Ctrl+V) - Sanitize pip package names to prevent shell metacharacter injection - Fix RefreshWslDistros to WaitForExit before ReadToEnd (prevents hang) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d handling - Fix TryParseTag to support presence-based @advancedpaste:disabled tag - Change 'if not input_value' to 'if input_value is None' (empty string is valid) - Wrap HTML output with HtmlFormatHelper.CreateHtmlFormat for CF_HTML compliance - Add try-catch for FileNotFoundException during ComputeHash - Change else-if to sequential ifs in DataPackageFromViewAsync (preserve all formats) - Remove unused System.* PackageReferences from UITest project - Fix '..' typo to '...' in search placeholder text Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lifecycle
- Fix PythonScriptNotFound to format {0} placeholder and use PasteActionException
- Dispose old CancellationTokenSource before creating new one in debounce handler
- Remove .Wait() on UI-scheduled task (fire-and-forget is sufficient)
- Add WaitForExit after Kill() in RefreshWslDistros to prevent ReadToEnd blocking
- Localize '(System default)' WSL distro display name via resource string
- Kill child Python/WSL process on user cancellation to prevent orphaned processes
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…st dialog - Fix early return in RefreshWslDistros to still update with default-only list - Store all PythonScriptActions (not just IsShown=true) so hide logic works - Pass cancellation token to Task.Delay in debounce handler - Use FileNotFoundException(message, fileName) constructor properly - Display script SHA-256 hash in trust dialog for user verification - Validate Python import names before embedding in shell commands Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
This feature needs to have a group policy for the "Disabled/Windows/WSL" setting or at least to force disable the script feature.
If you implement the policy please add in in the Advanced Paste category and as a utility setting like the ai policy for Advanced Paste.
Enterprise administrators can now disable the Python scripts feature via GPO policy 'AllowAdvancedPastePythonScripts' under the Advanced Paste category (HKLM\SOFTWARE\Policies\PowerToys\AllowAdvancedPastePythonScripts). When disabled: - Settings UI mode selector is grayed out and forced to 'Disabled' - Info bar shows that the setting is managed by organization policy - Runtime enforces disabled state even if settings.json is manually edited Implementation follows the same pattern as the existing AI models GPO policy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
htcfreek
left a comment
There was a problem hiding this comment.
gpo commit looks good to me
6ffd10d to
64c6f35
Compare
Summary
This PR adds Python script extension support to Advanced Paste, enabling users to write custom clipboard transformation scripts in Python that appear as new paste format options in the Advanced Paste menu (
Win+Shift+V).Motivation
Advanced Paste currently supports a fixed set of clipboard transformations (plain text, markdown, JSON, etc.). Users frequently request the ability to add their own custom transformations. Python script extensions allow power users to create arbitrarily complex clipboard transformations without modifying PowerToys source code.
Feature Overview
How It Works
.pyscripts in a configurable scripts folderadvanced_paste_from_text_to_markdown(text)- the function name encodes input/output formats_runner.pythat handles JSON I/O between Windows and the Python processScript Interface
Scripts follow a naming convention for automatic format matching:
Supported input formats:
text,html,image,audio,video,filesScripts can output: text, HTML, image paths, audio/video paths, or file lists.
Platform Support
Users choose one of three modes in Settings:
WSL mode features:
wsl --list --quiet)/mnt/path translation between Windows and WSL\\wsl.localhost\...)Dependency Management
Scripts can declare pip dependencies via
# requires:comments:Advanced Paste auto-detects missing packages and installs them before execution.
Settings UI
%LOCALAPPDATA%\Microsoft\PowerToys\AdvancedPaste\Scripts)Error Handling
Architecture
Files Changed
Services/PythonScripts/PythonScriptService.csServices/PythonScripts/_runner.pyIPythonScriptService.cs,IPythonScriptTrustService.csAdvancedPastePythonScriptSettings.cs,PythonScriptWslSettings.cs,PythonScriptWindowsSettings.csAdvancedPasteViewModel.csAdvancedPastePage.xaml/.xaml.csUserSettings.cs,IUserSettings.csdllmain.cppPythonScriptServiceTests.csadvancedpaste-python-scripts.mdHow to Test
.pyfile in the scripts folder with a function like:Win+Shift+V- the new "uppercase" option should appearSecurity Considerations
PythonScriptTrustService) tracks which scripts the user has approvedScreenshots on main functionalities
Settings page (Windows)
Advanced Paste Menu
Example Python extension script