mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-02 16:39:14 +02:00
Compare commits
3 Commits
copilot/mi
...
dev/crutka
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4939de08f4 | ||
|
|
0ef68e4cd4 | ||
|
|
0afe525f31 |
11
.github/policies/resourceManagement.yml
vendored
11
.github/policies/resourceManagement.yml
vendored
@@ -163,7 +163,7 @@ configuration:
|
||||
association: Collaborator
|
||||
then:
|
||||
- addReply:
|
||||
reply: We've identified this issue as a duplicate of an existing one and are closing this thread so discussion stays in one place.<br/><br/>Please see the comment above for the link to the original tracking issue, and feel free to subscribe there for updates.
|
||||
reply: We've identified this issue as a duplicate of an existing one and are closing this thread so discussion stays in one place.<br/><br/>Please see the comment above for the link to the original tracking issue, and feel free to subscribe there for updates.
|
||||
- closeIssue
|
||||
- removeLabel:
|
||||
label: Needs-Triage
|
||||
@@ -257,5 +257,14 @@ configuration:
|
||||
- addReply:
|
||||
reply: "To help debug your layout, please run [this script](https://github.com/microsoft/PowerToys/blob/main/src/modules/MouseUtils/CursorWrap/CursorWrapTests/Capture-MonitorLayout.ps1) and attach the generated JSON output to this thread.\n\nThis allows us to better understand the issue and investigate potential fixes."
|
||||
description:
|
||||
- if:
|
||||
- payloadType: Issue_Comment
|
||||
- commentContains:
|
||||
pattern: "I(( would|'d) (like|love|be happy)| want) (to help|helping|to contribute|contributing|to implement|implementing|to fix|fixing)"
|
||||
isRegex: True
|
||||
then:
|
||||
- addReply:
|
||||
reply: Hi! Your last comment indicates to our system, that you might want to contribute to this feature/fix this bug. Thank you! Please make us aware on our ["Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769), as we don't see all the comments. <br /><br />_I'm a bot (beep!) so please excuse any mistakes I may make_
|
||||
description:
|
||||
onFailure:
|
||||
onSuccess:
|
||||
|
||||
60
.github/workflows/contribution-intent-reply.md
vendored
60
.github/workflows/contribution-intent-reply.md
vendored
@@ -1,60 +0,0 @@
|
||||
---
|
||||
description: Reply when a new issue comment indicates intent to contribute
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
roles: all
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
models: read
|
||||
tools:
|
||||
github:
|
||||
toolsets: [issues]
|
||||
safe-outputs:
|
||||
add-comment:
|
||||
max: 1
|
||||
noop:
|
||||
---
|
||||
|
||||
# Contribution Intent Reply
|
||||
|
||||
You handle newly created issue comments in microsoft/PowerToys.
|
||||
|
||||
## Goal
|
||||
|
||||
Detect whether the new comment indicates that the author wants to contribute to fixing or implementing work for the issue.
|
||||
|
||||
## Scope
|
||||
|
||||
- Process **issue comments only**.
|
||||
- If `${{ github.event.issue.pull_request }}` is present, this is a PR comment context and you must do nothing.
|
||||
- Ignore comments from bot accounts.
|
||||
|
||||
## Decision rule
|
||||
|
||||
Reply only when the new comment clearly expresses first-person contribution intent for this issue, such as:
|
||||
|
||||
- "I want to contribute"
|
||||
- "I'd like to help"
|
||||
- "I can implement this"
|
||||
- "I want to fix this"
|
||||
|
||||
Do **not** reply when the comment is only:
|
||||
|
||||
- agreement or feedback without volunteering
|
||||
- third-person suggestions ("someone should fix this")
|
||||
- requests for others to contribute
|
||||
|
||||
When uncertain, do not reply.
|
||||
|
||||
## Required reply text
|
||||
|
||||
When contribution intent is detected, post exactly this comment on the same issue:
|
||||
|
||||
Hi! Your last comment indicates to our system, that you might want to contribute to this feature/fix this bug. Thank you! Please make us aware on our ["Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769), as we don't see all the comments. <br /><br />_I'm a bot (beep!) so please excuse any mistakes I may make_
|
||||
|
||||
## Safe output behavior
|
||||
|
||||
- If contribution intent is detected: use `add-comment` once on issue `${{ github.event.issue.number }}` with the required reply text above.
|
||||
- If no reply is needed: use `noop` with a short reason.
|
||||
@@ -2,6 +2,34 @@
|
||||
<Project ToolsVersion="4.0"
|
||||
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
Cold "Rebuild All" ordering fix for native PackageReference projects.
|
||||
|
||||
A handful of native projects use <RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||
(Microsoft.CommandPalette.Extensions, Microsoft.Terminal.UI, PowerToys.MeasureToolCore,
|
||||
FindMyMouse, PowerRenameUILib, runner). For those, the CppWinRT configuration - most importantly
|
||||
the MIDL default <EnableWindowsRuntime>true</EnableWindowsRuntime> that makes IInspectable /
|
||||
IUnknown resolvable - only arrives via the restore-generated obj\*.nuget.g.props.
|
||||
|
||||
On a cold "Rebuild All", MSBuild evaluates the project (resolving imports) before the in-session
|
||||
NuGet restore regenerates those obj props, so MIDL runs in non-WinRT mode and fails with
|
||||
"MIDL2009: undefined symbol IInspectable". Because Microsoft.CommandPalette.Extensions and
|
||||
Microsoft.Terminal.UI are winmd producers consumed (by file path) across the entire Command
|
||||
Palette graph, that single race cascades into dozens of downstream CS0006/CS0234 failures. A
|
||||
second build "works" only because restore has since written the obj props. This is why the
|
||||
RestoreThenBuild PowerShell helper (two separate msbuild invocations) already avoids the issue.
|
||||
|
||||
Statically importing the committed CppWinRT props here makes the WinRT MIDL configuration present
|
||||
at evaluation time regardless of restore ordering, so it is durable across Visual Studio, the
|
||||
build scripts, and CI. It is scoped to PackageReference-style vcxproj only (the ~100 other native
|
||||
projects already import this same committed package directly), and the Exists() guard plus
|
||||
CppWinRT's own import guards keep it a safe no-op once restore has run.
|
||||
-->
|
||||
<Import Project="$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props"
|
||||
Condition="'$(MSBuildProjectExtension)' == '.vcxproj'
|
||||
and '$(RestoreProjectStyle)' == 'PackageReference'
|
||||
and Exists('$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
|
||||
<!-- Skip building C++ test projects when BuildTests=false -->
|
||||
<PropertyGroup Condition="'$(_IsSkippedTestProject)' == 'true'">
|
||||
<UsePrecompiledHeaders>false</UsePrecompiledHeaders>
|
||||
|
||||
@@ -13,4 +13,25 @@
|
||||
-->
|
||||
<Import Project="$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.targets"
|
||||
Condition="'$(MSBuildProjectExtension)' == '.vcxproj' and '$(VcpkgEnabled)' == 'true' and Exists('$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg.targets')" />
|
||||
|
||||
<!--
|
||||
Cold "Rebuild All" ordering fix (matching partner to the CppWinRT props import in Cpp.Build.props).
|
||||
|
||||
Native PackageReference projects (Microsoft.CommandPalette.Extensions, Microsoft.Terminal.UI,
|
||||
PowerToys.MeasureToolCore, FindMyMouse, PowerRenameUILib, runner) receive the CppWinRT MIDL
|
||||
metadata wiring (CppWinRTMidlResponseFile, AdditionalMetadataDirectories, the MdMerge step that
|
||||
produces the .winmd) only from the restore-generated obj\*.nuget.g.targets. On a cold "Rebuild
|
||||
All" that file has not been regenerated yet when the project is evaluated, so MIDL cannot resolve
|
||||
IInspectable / IUnknown and the Command Palette graph fails with MIDL2009 - until a second build.
|
||||
|
||||
Importing the committed CppWinRT targets here - after Microsoft.Cpp.targets, exactly where the
|
||||
statically-wired native projects put their own CppWinRT ExtensionTargets import - makes that wiring
|
||||
present regardless of restore ordering. Scoped to PackageReference-style vcxproj only; the Exists()
|
||||
guard and CppWinRT's own import guards make it a safe no-op once restore has run or for the ~100
|
||||
native projects that already import this committed package directly.
|
||||
-->
|
||||
<Import Project="$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets"
|
||||
Condition="'$(MSBuildProjectExtension)' == '.vcxproj'
|
||||
and '$(RestoreProjectStyle)' == 'PackageReference'
|
||||
and Exists('$(MSBuildThisFileDirectory)packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</Project>
|
||||
|
||||
@@ -94,6 +94,7 @@ internal sealed partial class HttpCachingClient : IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
_httpClient.Dispose();
|
||||
_cacheHandler.Dispose();
|
||||
}
|
||||
|
||||
private static bool IsSupportedHttpUri(Uri resourceUri)
|
||||
|
||||
@@ -146,13 +146,7 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
// The all apps page will kick off a BG thread to start loading apps.
|
||||
// We just want to know when it is done.
|
||||
var allApps = AllAppsCommandProvider.Page;
|
||||
allApps.PropChanged += (s, p) =>
|
||||
{
|
||||
if (p.PropertyName == nameof(allApps.IsLoading))
|
||||
{
|
||||
IsLoading = ActuallyLoading();
|
||||
}
|
||||
};
|
||||
allApps.PropChanged += AllApps_PropChanged;
|
||||
|
||||
WeakReferenceMessenger.Default.Register<ClearSearchMessage>(this);
|
||||
WeakReferenceMessenger.Default.Register<UpdateFallbackItemsMessage>(this);
|
||||
@@ -172,6 +166,14 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
}
|
||||
}
|
||||
|
||||
private void AllApps_PropChanged(object? sender, IPropChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(AllAppsCommandProvider.Page.IsLoading))
|
||||
{
|
||||
IsLoading = ActuallyLoading();
|
||||
}
|
||||
}
|
||||
|
||||
private void PinnedCommands_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
_defaultViewDirty = true;
|
||||
@@ -782,6 +784,8 @@ public sealed partial class MainListPage : DynamicListPage,
|
||||
_tlcManager.TopLevelCommands.CollectionChanged -= Commands_CollectionChanged;
|
||||
_tlcManager.PinnedCommands.CollectionChanged -= PinnedCommands_CollectionChanged;
|
||||
|
||||
AllAppsCommandProvider.Page.PropChanged -= AllApps_PropChanged;
|
||||
|
||||
if (_settingsService is not null)
|
||||
{
|
||||
_settingsService.SettingsChanged -= SettingsChangedHandler;
|
||||
|
||||
@@ -541,6 +541,9 @@ public partial class WinRTExtensionService : IExtensionService, IDisposable
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_catalog.PackageInstalling -= Catalog_PackageInstalling;
|
||||
_catalog.PackageUninstalling -= Catalog_PackageUninstalling;
|
||||
_catalog.PackageUpdating -= Catalog_PackageUpdating;
|
||||
_getInstalledExtensionsLock.Dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ internal sealed partial class BlurImageControl : Control
|
||||
private SpriteVisual? _effectVisual;
|
||||
private CompositionEffectBrush? _effectBrush;
|
||||
private CompositionSurfaceBrush? _imageBrush;
|
||||
private LoadedImageSurface? _lastLoadedSurface;
|
||||
|
||||
public BlurImageControl()
|
||||
{
|
||||
@@ -379,10 +380,20 @@ internal sealed partial class BlurImageControl : Control
|
||||
}
|
||||
|
||||
Logger.LogDebug($"Starting load of BlurImageControl from '{bitmapImage.UriSource}'");
|
||||
|
||||
// Each call to LoadImageAsync creates a new LoadedImageSurface backed by native
|
||||
// composition resources. The old surface becomes unrooted once the brush points at
|
||||
// the new one, so it isn't leaked, but dispose it explicitly so the unmanaged
|
||||
// resources are released deterministically instead of waiting for finalization.
|
||||
var previousSurface = _lastLoadedSurface;
|
||||
|
||||
var loadedSurface = LoadedImageSurface.StartLoadFromUri(bitmapImage.UriSource);
|
||||
_lastLoadedSurface = loadedSurface;
|
||||
loadedSurface.LoadCompleted += OnLoadedSurfaceOnLoadCompleted;
|
||||
SetLoadedSurfaceToBrush(loadedSurface);
|
||||
_effectBrush?.SetSourceParameter(ImageSourceParameterName, _imageBrush);
|
||||
|
||||
previousSurface?.Dispose();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -119,8 +119,9 @@ public sealed partial class ContentFormControl : UserControl
|
||||
|
||||
private void OnFrameworkElementLayoutUpdated(object? sender, object e)
|
||||
{
|
||||
// Only fix once — unhook after first layout pass
|
||||
if (_renderedCard?.FrameworkElement is FrameworkElement element)
|
||||
// Only fix once — unhook from sender (not _renderedCard, which may have been
|
||||
// reassigned by the time this fires).
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
element.LayoutUpdated -= OnFrameworkElementLayoutUpdated;
|
||||
FixToggleAccessibilityNames(element);
|
||||
|
||||
@@ -192,7 +192,7 @@ public sealed partial class MainWindow : WindowEx,
|
||||
App.Current.Services.GetRequiredService<ISettingsService>().SettingsChanged += SettingsChangedHandler;
|
||||
|
||||
// Make sure that we update the acrylic theme when the OS theme changes
|
||||
RootElement.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateBackdrop);
|
||||
RootElement.ActualThemeChanged += RootElement_ActualThemeChanged;
|
||||
|
||||
// Hardcoding event name to avoid bringing in the PowerToys.interop dependency. Event name must match CMDPAL_SHOW_EVENT from shared_constants.h
|
||||
NativeEventWaiter.WaitForEventLoop("Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a", () =>
|
||||
@@ -222,6 +222,11 @@ public sealed partial class MainWindow : WindowEx,
|
||||
UpdateBackdrop();
|
||||
}
|
||||
|
||||
private void RootElement_ActualThemeChanged(FrameworkElement sender, object args)
|
||||
{
|
||||
DispatcherQueue.TryEnqueue(UpdateBackdrop);
|
||||
}
|
||||
|
||||
private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e)
|
||||
{
|
||||
if (e.Key == VirtualKey.GoBack)
|
||||
@@ -1683,6 +1688,9 @@ public sealed partial class MainWindow : WindowEx,
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_themeService.ThemeChanged -= ThemeServiceOnThemeChanged;
|
||||
App.Current.Services.GetRequiredService<ISettingsService>().SettingsChanged -= SettingsChangedHandler;
|
||||
|
||||
_localKeyboardListener.Dispose();
|
||||
_windowThemeSynchronizer.Dispose();
|
||||
DisposeAcrylic();
|
||||
|
||||
@@ -22,6 +22,7 @@ public sealed partial class ExtensionsPage : Page
|
||||
|
||||
private readonly SettingsViewModel? viewModel;
|
||||
private readonly Dictionary<string, WeakReference<SettingsCard>> _vmToCardMap = new();
|
||||
private readonly Dictionary<SettingsCard, ProviderSettingsViewModel> _cardToVmMap = new();
|
||||
|
||||
public ExtensionsPage()
|
||||
{
|
||||
@@ -31,6 +32,23 @@ public sealed partial class ExtensionsPage : Page
|
||||
var themeService = App.Current.Services.GetService<IThemeService>()!;
|
||||
var settingsService = App.Current.Services.GetRequiredService<ISettingsService>();
|
||||
viewModel = new SettingsViewModel(topLevelCommandManager, _mainTaskScheduler, themeService, settingsService);
|
||||
|
||||
Unloaded += ExtensionsPage_Unloaded;
|
||||
}
|
||||
|
||||
private void ExtensionsPage_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// ProviderSettingsViewModel subscribes to its CommandProviderWrapper (owned by the
|
||||
// singleton TopLevelCommandManager), so a live VM roots this page through the
|
||||
// PropertyChanged handler below. Drain any VMs still hooked when the page is torn
|
||||
// down; SettingsCard_DataContextChanged only unhooks the ones that get recycled.
|
||||
foreach (var vm in _cardToVmMap.Values)
|
||||
{
|
||||
vm.PropertyChanged -= ProviderViewModel_PropertyChanged;
|
||||
}
|
||||
|
||||
_cardToVmMap.Clear();
|
||||
_vmToCardMap.Clear();
|
||||
}
|
||||
|
||||
private void SettingsCard_Click(object sender, RoutedEventArgs e)
|
||||
@@ -46,16 +64,28 @@ public sealed partial class ExtensionsPage : Page
|
||||
|
||||
private void SettingsCard_DataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
|
||||
{
|
||||
// Store the card reference keyed by Id (not the VM itself) to avoid leaking VM references
|
||||
if (sender is SettingsCard card && card.DataContext is ProviderSettingsViewModel newVm)
|
||||
if (sender is SettingsCard card)
|
||||
{
|
||||
_vmToCardMap[newVm.Id] = new WeakReference<SettingsCard>(card);
|
||||
newVm.PropertyChanged += ProviderViewModel_PropertyChanged;
|
||||
|
||||
// Immediately update automation name in case DisplayName is already available
|
||||
if (card.Content is ToggleSwitch toggle && !string.IsNullOrEmpty(newVm.DisplayName))
|
||||
// Unsubscribe from the previous ViewModel to prevent handler accumulation
|
||||
// when virtualization recycles items with a new DataContext.
|
||||
if (_cardToVmMap.TryGetValue(card, out var oldVm))
|
||||
{
|
||||
AutomationProperties.SetName(toggle, newVm.DisplayName);
|
||||
oldVm.PropertyChanged -= ProviderViewModel_PropertyChanged;
|
||||
_cardToVmMap.Remove(card);
|
||||
}
|
||||
|
||||
// Store the card reference keyed by Id (not the VM itself) to avoid leaking VM references
|
||||
if (card.DataContext is ProviderSettingsViewModel newVm)
|
||||
{
|
||||
_vmToCardMap[newVm.Id] = new WeakReference<SettingsCard>(card);
|
||||
_cardToVmMap[card] = newVm;
|
||||
newVm.PropertyChanged += ProviderViewModel_PropertyChanged;
|
||||
|
||||
// Immediately update automation name in case DisplayName is already available
|
||||
if (card.Content is ToggleSwitch toggle && !string.IsNullOrEmpty(newVm.DisplayName))
|
||||
{
|
||||
AutomationProperties.SetName(toggle, newVm.DisplayName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public partial class ShowFileInFolderCommand : InvokableCommand
|
||||
try
|
||||
{
|
||||
var argument = "/select, \"" + _path + "\"";
|
||||
Process.Start("explorer.exe", argument);
|
||||
using var process = Process.Start("explorer.exe", argument);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@@ -26,6 +26,31 @@
|
||||
<ProjectReference Include="..\ui\ImageResizerUI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
MSB3030 parallel-build race hardening.
|
||||
|
||||
ImageResizerUI is a self-contained WinUI 3 app whose PowerToys.ImageResizer.runtimeconfig.json
|
||||
and PowerToys.ImageResizer.deps.json are written late into the shared WinUI3Apps output folder.
|
||||
This unit test project references ImageResizerUI only for its types (see InternalsVisibleTo
|
||||
"ImageResizer.Test" in ImageResizerUI.csproj); it never launches the app, so it does not need
|
||||
those two app runtime json files. During a highly parallel "Build All" the test's
|
||||
copy-to-output step can run before those files exist, producing:
|
||||
MSB3030: Could not copy the file "...\PowerToys.ImageResizer.runtimeconfig.json" because it was not found.
|
||||
Removing just those two never-needed files from this project's copy-to-output lists makes the
|
||||
race impossible. The referenced PowerToys.ImageResizer.dll is still copied, so tests run normally.
|
||||
-->
|
||||
<Target Name="ExcludeReferencedAppRuntimeJsonFromCopy"
|
||||
AfterTargets="GetCopyToOutputDirectoryItems">
|
||||
<ItemGroup>
|
||||
<_SourceItemsToCopyToOutputDirectory
|
||||
Remove="@(_SourceItemsToCopyToOutputDirectory)"
|
||||
Condition="'%(Filename)%(Extension)' == 'PowerToys.ImageResizer.runtimeconfig.json' or '%(Filename)%(Extension)' == 'PowerToys.ImageResizer.deps.json'" />
|
||||
<_SourceItemsToCopyToOutputDirectoryAlways
|
||||
Remove="@(_SourceItemsToCopyToOutputDirectoryAlways)"
|
||||
Condition="'%(Filename)%(Extension)' == 'PowerToys.ImageResizer.runtimeconfig.json' or '%(Filename)%(Extension)' == 'PowerToys.ImageResizer.deps.json'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Test.gif">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
Reference in New Issue
Block a user