Compare commits

...

12 Commits

Author SHA1 Message Date
Yu Leng (from Dev Box)
5075610831 Enhance Host Settings Awareness feature
- Added new properties to `HostSettingsManager.Current`:
  `ShowSystemTrayIcon`, `IgnoreShortcutWhenFullscreen`,
  `DisableAnimations`, and `SummonOn` for better extension
  configuration access.
- Improved `SettingsChanged` event handling with examples for
  dynamic UI updates.
- Updated `RaiseItemsChanged` in `HostSettingsPage` to include
  an optional parameter for better UI refresh control.
- Added detailed logging to `OnSettingsChanged` in
  `HostSettingsPage` for improved debugging.
- Updated README with Host Settings Awareness details, examples,
  and links to full documentation.
- Refined helper SDK class descriptions and ensured consistency
  in documentation formatting.
- Maintained backwards compatibility with older hosts.
2025-11-26 17:10:19 +08:00
Yu Leng
ffe69f7a99 Replace ASCII diagrams with Mermaid in documentation
Updated `host-settings-awareness.md` to replace outdated ASCII art diagrams with modern `mermaid` diagrams for improved readability and maintainability.

- Replaced the high-level architecture overview with a `mermaid` flowchart to better illustrate component relationships and cross-process communication.
- Replaced the initial settings delivery process diagram with a `mermaid` sequence diagram, detailing the flow for both OOP extensions and built-in commands.
- Replaced the settings change notification diagram with a `mermaid` sequence diagram, showing the propagation of settings changes and UI updates in extensions.

These changes enhance the clarity and visual appeal of the documentation, making it easier for developers to understand the processes.
2025-11-26 16:23:36 +08:00
Yu Leng
a75d9fb9a0 Merge branch 'yuleng/cmdpal/settings2' of https://github.com/moooyo/PowerToys into yuleng/cmdpal/settings2 2025-11-26 16:18:59 +08:00
Yu Leng
26846772b6 Refactor host settings delivery logic
Refactored `CommandProviderWrapper` to centralize and clarify the logic for sending initial host settings by introducing the `SendInitialHostSettings()` method. This method handles both out-of-process (OOP) extensions and built-in commands, ensuring proper timing and consistent behavior. Updated `TopLevelCommandManager` to call this method after provider initialization.

Enhanced documentation in `host-settings-awareness.md` with updated explanations, sequence diagrams, and metadata. Removed the `Initialize` method from `HostSettingsManager` to simplify the API, retaining the `Update` method for settings updates.

Improved logging and error handling to provide detailed feedback during the settings delivery process. Consolidated logic and improved code readability for better maintainability.
2025-11-26 16:18:29 +08:00
Yu Leng (from Dev Box)
efb25be698 Refactor ExtensionWrapper and HostSettingsManager
Removed unnecessary pragma directives in ExtensionWrapper.cs to clean up the code. Simplified HostSettingsManager.cs by removing the `Initialize` method and debug logging from the `Update` method, streamlining the logic and eliminating conditional compilation blocks.
2025-11-26 15:30:48 +08:00
Yu Leng
209abd834e Add Host Settings Awareness for extensions
Introduced the Host Settings Awareness feature, enabling Command Palette extensions to access and respond to global configuration settings. Extensions can now read settings like hotkeys, animation preferences, and monitor behavior, and receive notifications on changes.

Key changes:
- Added `IHostSettings` and `IHostSettingsChanged` interfaces.
- Implemented `HostSettingsManager` for centralized settings access.
- Updated `IExtensionService` and `IExtensionWrapper` to propagate settings changes.
- Enhanced `AppExtensionHost` and `ExtensionService` to notify extensions.
- Added `HostSettingsConverter` for internal-to-external settings conversion.
- Introduced `HostSettingsPage` in samples to demonstrate usage.
- Updated documentation and IDL definitions for the new feature.
- Ensured backwards compatibility with older hosts.

This feature is designed to be extensible for future settings and capabilities.
2025-11-25 16:12:00 +08:00
leileizhang
09c8c1d79a [Hot Fix] Fix Image Resizer not working on Win10 (#43763)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Windows 10 can’t launch the app using the Sparse Package. Remove the app
manifest so that Image Resizer can start properly on Windows 10.

We will figure out how to support Sparse Packages on Windows 10 in the
next release.
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #43747 #43734 #43722 #43759
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-24 10:42:35 +08:00
leileizhang
95c8a83f79 [Hotfix] Remove the properties in Prompt Execution Settings for OpenAI (#43766)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Remove the properties in Prompt Execution Settings for OpenAI, as the
new models may not support them.

Will try to expose them in the UI so users can add them on their own in
the next release.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-24 10:08:12 +08:00
Kai Tao
2830ea919c Advanced Paste: Adjust model parameter to make the result longer (#43768)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Adjust model parameter to make the result longer
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] Closes: #xxx
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-11-24 09:51:27 +08:00
Dave Rayment
725ad21952 [Awake] Fix issue with timed mode not expiring correctly (#43785)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Resolves an issue with the timed mode's expiry not completing correctly.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #43775
<!-- - [ ] Closes: #yyy (add separate lines for additional resolved
issues) -->
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
This was because of my recent change to the timed mode. The `Subscribe`
method on the `Observable` interval accidentally wired the completion
logic to the **Error** handler instead of the **Completion** handler
because of the use of a discard `_` instead of an empty parameter list
`()`. As a result of the incorrect overload being called, Awake stayed
in the Timed state despite the timer reaching zero.

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Confirmed that the timed mode times out and exits upon expiry.
2025-11-24 09:12:54 +08:00
Erik Anderson
ebc3a139c5 Fix typo in AI settings card description (#43757)
## Summary of the Pull Request
The word "cloud" does not use a vowel sound, so the preceding word
should be "A" instead of "An".

## PR Checklist

- [X] Closes: #43756
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [X] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

Co-authored-by: Erik Anderson <erikan@ntdev.microsoft.com>
2025-11-21 18:34:34 +08:00
Jaylyn Barbee
28dba2633e [Light Switch][Dev Docs] Clarify LightSwitchService and LightSwitchStateManager roles (#43748)
Updated LightSwitch module documentation to clarify the role of
LightSwitchService and LightSwitchStateManager.

---------

Co-authored-by: Niels Laute <niels.laute@live.nl>
2025-11-20 15:22:40 -08:00
24 changed files with 519 additions and 16 deletions

View File

@@ -33,9 +33,12 @@ The **Light Switch** module lets users automatically transition between light an
> **Note:** Using the shortcut overrides the current schedule until the next transition event.
* **LightSwitchService**
Reads settings and applies theming. Runs a check every minute to ensure the state is correct.
* **LightSwitchService.cpp**
is the heart beat of the module. Controls ticking every minute and depending on user actions (manual override, settings changing, etc) triggers the state manager to perform the corresponding operation.
* **LightSwitchStateManager.cpp**
handles updating the state based on the signals sent by LightSwitchService.
* **SettingsXAML/LightSwitch**
Provides the settings UI for configuring schedules, syncing location, and customizing shortcuts.

View File

@@ -64,7 +64,7 @@ public sealed class FoundryLocalModelProvider : ILanguageModelProvider
return new OpenAIClient(
new ApiKeyCredential("none"),
new OpenAIClientOptions { Endpoint = endpointUri })
new OpenAIClientOptions { Endpoint = endpointUri, NetworkTimeout = TimeSpan.FromMinutes(5) })
.GetChatClient(modelId)
.AsIChatClient();
}

View File

@@ -215,7 +215,6 @@ public sealed class AdvancedAIKernelService : KernelServiceBase
return new OpenAIPromptExecutionSettings
{
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(),
Temperature = 0.01,
};
}
}

View File

@@ -146,6 +146,7 @@ public sealed class FoundryLocalPasteProvider : IPasteAIProvider
var options = new ChatOptions
{
ModelId = modelReference,
MaxOutputTokens = 2048,
};
if (!string.IsNullOrWhiteSpace(systemPrompt))

View File

@@ -157,8 +157,6 @@ namespace AdvancedPaste.Services.CustomActions
{
AIServiceType.OpenAI or AIServiceType.AzureOpenAI => new OpenAIPromptExecutionSettings
{
Temperature = 0.01,
MaxTokens = 2000,
FunctionChoiceBehavior = null,
},
_ => new PromptExecutionSettings(),

View File

@@ -350,7 +350,7 @@ namespace Awake.Core
TrayHelper.TimedIcon,
TrayIconAction.Update);
},
_ => HandleTimerCompletion("timed"),
() => HandleTimerCompletion("timed"),
_tokenSource.Token);
}

View File

@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CommandPalette.Extensions;
using Windows.Foundation;
namespace Microsoft.CmdPal.Core.Common.Services;
@@ -27,6 +28,12 @@ public interface IExtensionService
void DisableExtension(string extensionUniqueId);
/// <summary>
/// Notifies all extensions that support the HostSettings capability about a settings change.
/// </summary>
/// <param name="settings">The updated host settings.</param>
void NotifyHostSettingsChanged(IHostSettings settings);
///// <summary>
///// Gets a boolean indicating whether the extension was disabled due to the corresponding Windows optional feature
///// being absent from the machine or in an unknown state.

View File

@@ -110,4 +110,10 @@ public interface IExtensionWrapper
/// <returns>Nullable instance of the provider</returns>
Task<IEnumerable<T>> GetListOfProvidersAsync<T>()
where T : class;
/// <summary>
/// Notifies the extension that host settings have changed.
/// </summary>
/// <param name="settings">The updated host settings.</param>
void NotifyHostSettingsChanged(IHostSettings settings);
}

View File

@@ -5,6 +5,7 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using Microsoft.CmdPal.Core.Common;
using Microsoft.CmdPal.Core.Common.Services;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation;
@@ -13,6 +14,12 @@ namespace Microsoft.CmdPal.Core.ViewModels;
public abstract partial class AppExtensionHost : IExtensionHost
{
/// <summary>
/// Gets or sets a function that returns the current IHostSettings.
/// This is set by the application to provide settings to extensions.
/// </summary>
public static Func<IHostSettings?>? GetHostSettingsFunc { get; set; }
private static readonly GlobalLogPageContext _globalLogPageContext = new();
private static ulong _hostingHwnd;

View File

@@ -214,10 +214,52 @@ public sealed class CommandProviderWrapper
Logger.LogDebug($"Provider supports {apiExtensions.Length} extensions");
foreach (var a in apiExtensions)
{
if (a is IExtendedAttributesProvider command2)
if (a is IExtendedAttributesProvider)
{
Logger.LogDebug($"{ProviderId}: Found an IExtendedAttributesProvider");
}
if (a is IHostSettingsChanged)
{
Logger.LogDebug($"{ProviderId}: Found an IHostSettingsChanged");
}
}
}
/// <summary>
/// Sends the initial host settings to the provider.
/// For extensions, this goes through ExtensionWrapper (cross-process).
/// For built-in commands, this calls the handler directly (in-proc).
/// </summary>
public void SendInitialHostSettings()
{
var settings = AppExtensionHost.GetHostSettingsFunc?.Invoke();
if (settings == null)
{
return;
}
try
{
if (Extension != null)
{
// OOP extension: send through ExtensionWrapper (cross-process)
Extension.NotifyHostSettingsChanged(settings);
Logger.LogDebug($"{ProviderId}: Sent initial host settings via ExtensionWrapper");
}
else
{
// Built-in command: call directly (in-proc)
if (_commandProvider.Unsafe is IHostSettingsChanged handler)
{
handler.OnHostSettingsChanged(settings);
Logger.LogDebug($"{ProviderId}: Sent initial host settings directly");
}
}
}
catch (Exception e)
{
Logger.LogDebug($"Failed to send initial settings to {ProviderId}: {e.Message}");
}
}

View File

@@ -0,0 +1,49 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.UI.ViewModels;
/// <summary>
/// Provides conversion between SettingsModel and IHostSettings for extension communication.
/// </summary>
public static class HostSettingsConverter
{
/// <summary>
/// Converts a SettingsModel to an IHostSettings object that can be passed to extensions.
/// </summary>
/// <param name="settings">The settings model to convert.</param>
/// <returns>An IHostSettings object with the current settings values.</returns>
public static IHostSettings ToHostSettings(this SettingsModel settings)
{
return new HostSettings
{
Hotkey = settings.Hotkey?.ToString() ?? string.Empty,
ShowAppDetails = settings.ShowAppDetails,
HotkeyGoesHome = settings.HotkeyGoesHome,
BackspaceGoesBack = settings.BackspaceGoesBack,
SingleClickActivates = settings.SingleClickActivates,
HighlightSearchOnActivate = settings.HighlightSearchOnActivate,
ShowSystemTrayIcon = settings.ShowSystemTrayIcon,
IgnoreShortcutWhenFullscreen = settings.IgnoreShortcutWhenFullscreen,
DisableAnimations = settings.DisableAnimations,
SummonOn = ConvertSummonTarget(settings.SummonOn),
};
}
private static SummonTarget ConvertSummonTarget(MonitorBehavior behavior)
{
return behavior switch
{
MonitorBehavior.ToMouse => SummonTarget.ToMouse,
MonitorBehavior.ToPrimary => SummonTarget.ToPrimary,
MonitorBehavior.ToFocusedWindow => SummonTarget.ToFocusedWindow,
MonitorBehavior.InPlace => SummonTarget.InPlace,
MonitorBehavior.ToLast => SummonTarget.ToLast,
_ => SummonTarget.ToMouse,
};
}
}

View File

@@ -400,6 +400,29 @@ public partial class ExtensionService : IExtensionService, IDisposable
_enabledExtensions.Remove(extension.First());
}
/// <summary>
/// Notifies all extensions about a settings change.
/// </summary>
/// <param name="settings">The updated host settings.</param>
public void NotifyHostSettingsChanged(IHostSettings settings)
{
Task.Run(async () =>
{
var extensions = await GetInstalledExtensionsAsync();
foreach (var extension in extensions)
{
try
{
extension.NotifyHostSettingsChanged(settings);
}
catch (Exception ex)
{
Logger.LogError($"Failed to notify extension {extension.ExtensionUniqueId} of settings change", ex);
}
}
});
}
/*
///// <inheritdoc cref="IExtensionService.DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper)"/>
//public async Task<bool> DisableExtensionIfWindowsFeatureNotAvailable(IExtensionWrapper extension)

View File

@@ -201,4 +201,37 @@ public class ExtensionWrapper : IExtensionWrapper
public void AddProviderType(ProviderType providerType) => _providerTypes.Add(providerType);
public bool HasProviderType(ProviderType providerType) => _providerTypes.Contains(providerType);
/// <summary>
/// Notifies the extension that host settings have changed.
/// </summary>
/// <param name="settings">The updated host settings.</param>
public void NotifyHostSettingsChanged(IHostSettings settings)
{
try
{
var provider = GetExtensionObject()?.GetProvider(ProviderType.Commands);
if (provider is not ICommandProvider2 provider2)
{
return;
}
// Get the IHostSettingsChanged handler from GetApiExtensionStubs().
// This pattern works reliably in OOP scenarios because the stub objects
// are properly marshalled across the process boundary.
var apiExtensions = provider2.GetApiExtensionStubs();
foreach (var stub in apiExtensions)
{
if (stub is IHostSettingsChanged handler)
{
handler.OnHostSettingsChanged(settings);
return;
}
}
}
catch (Exception e)
{
Logger.LogDebug($"Failed to notify {ExtensionDisplayName} of settings change: {e.Message}");
}
}
}

View File

@@ -103,6 +103,9 @@ public partial class TopLevelCommandManager : ObservableObject,
await commandProvider.LoadTopLevelCommands(_serviceProvider, weakSelf);
// Send initial host settings after the provider is fully initialized
commandProvider.SendInitialHostSettings();
var commands = await Task.Factory.StartNew(
() =>
{

View File

@@ -160,10 +160,23 @@ public partial class App : Application
services.AddSingleton(sm);
var state = AppStateModel.LoadState();
services.AddSingleton(state);
services.AddSingleton<IExtensionService, ExtensionService>();
var extensionService = new ExtensionService();
services.AddSingleton<IExtensionService>(extensionService);
services.AddSingleton<TrayIconService>();
services.AddSingleton<IRunHistoryService, RunHistoryService>();
// Set up host settings for extensions
AppExtensionHost.GetHostSettingsFunc = () => sm.ToHostSettings();
// Subscribe to settings changes and notify extensions
sm.SettingsChanged += (sender, _) =>
{
if (sender is SettingsModel settings)
{
extensionService.NotifyHostSettingsChanged(settings.ToHostSettings());
}
};
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
services.AddSingleton<IAppHostService, PowerToysAppHostService>();
services.AddSingleton<ITelemetryService, TelemetryForwarder>();

View File

@@ -68,6 +68,7 @@ functionality.
- [Fallback commands](#fallback-commands)
- [`GetCommand`](#getcommand)
- [Settings](#settings)
- [Host Settings Awareness](#host-settings-awareness)
- [Helper SDK Classes](#helper-sdk-classes)
- [Default implementations](#default-implementations)
- [Using the Clipboard](#using-the-clipboard)
@@ -1639,6 +1640,59 @@ We're then additionally going to provide a collection of
should allow developers to quickly work to add settings, without mucking around
in building the form JSON themselves.
##### Host Settings Awareness
Extensions can access the Command Palette's global configuration through the
`HostSettingsManager` class provided by the Toolkit. This allows extensions to
read user preferences like the global hotkey, animation settings, and more.
**Reading Current Settings**
Use `HostSettingsManager.Current` to get the current host settings:
```cs
using Microsoft.CommandPalette.Extensions.Toolkit;
// Get current settings - this is the primary way to access host configuration
var settings = HostSettingsManager.Current;
// Access individual settings
var hotkey = settings.Hotkey; // e.g., "Win+Alt+Space"
var disableAnimations = settings.DisableAnimations;
var singleClick = settings.SingleClickActivates;
var summonTarget = settings.SummonOn; // ToMouse, ToPrimary, etc.
```
Available properties on `HostSettingsManager.Current`:
| Property | Type | Description |
|----------|------|-------------|
| `Hotkey` | `string` | Global summon hotkey |
| `ShowAppDetails` | `bool` | Show app details in UI |
| `HotkeyGoesHome` | `bool` | Hotkey returns to home page |
| `BackspaceGoesBack` | `bool` | Backspace navigates back |
| `SingleClickActivates` | `bool` | Single-click activation |
| `HighlightSearchOnActivate` | `bool` | Highlight search on activate |
| `ShowSystemTrayIcon` | `bool` | Show system tray icon |
| `IgnoreShortcutWhenFullscreen` | `bool` | Ignore hotkey in fullscreen |
| `DisableAnimations` | `bool` | Animations disabled |
| `SummonOn` | `SummonTarget` | Monitor positioning behavior |
**Responding to Changes (Optional)**
If your extension needs to update when settings change, subscribe to
`HostSettingsManager.SettingsChanged`:
```cs
HostSettingsManager.SettingsChanged += () =>
{
// Re-read settings and update your UI
var settings = HostSettingsManager.Current;
RaiseItemsChanged();
};
```
## Helper SDK Classes
As a part of the `Microsoft.CommandPalette.Extensions` namespace, we'll provide a set of

View File

@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace SamplePagesExtension;
/// <summary>
/// A sample page that displays the current Host Settings values.
/// This demonstrates how extensions can access and display the Command Palette's global settings.
/// </summary>
internal sealed partial class HostSettingsPage : ListPage
{
public HostSettingsPage()
{
Icon = new IconInfo("\uE713"); // Settings icon
Name = "Host Settings";
Title = "Current Host Settings";
// Subscribe to settings changes to refresh the page when settings are updated
HostSettingsManager.SettingsChanged += OnSettingsChanged;
ExtensionHost.LogMessage($"[HostSettingsPage] Constructor called, subscribed to SettingsChanged");
}
private void OnSettingsChanged()
{
ExtensionHost.LogMessage($"[HostSettingsPage] OnSettingsChanged called, invoking RaiseItemsChanged");
// Notify the UI to refresh the items list
RaiseItemsChanged(0);
ExtensionHost.LogMessage($"[HostSettingsPage] RaiseItemsChanged completed");
}
public override IListItem[] GetItems()
{
ExtensionHost.LogMessage($"[HostSettingsPage] GetItems called");
var settings = HostSettingsManager.Current;
if (settings == null)
{
return [
new ListItem(new NoOpCommand())
{
Title = "Host Settings not available",
Subtitle = "Settings have not been received from the host yet",
Icon = new IconInfo("\uE7BA"), // Warning icon
},
];
}
return [
CreateSettingItem("Hotkey", settings.Hotkey, "\uE765"), // Keyboard icon
CreateSettingItem("Show App Details", settings.ShowAppDetails, "\uE946"), // View icon
CreateSettingItem("Hotkey Goes Home", settings.HotkeyGoesHome, "\uE80F"), // Home icon
CreateSettingItem("Backspace Goes Back", settings.BackspaceGoesBack, "\uE72B"), // Back icon
CreateSettingItem("Single Click Activates", settings.SingleClickActivates, "\uE8B0"), // Mouse icon
CreateSettingItem("Highlight Search On Activate", settings.HighlightSearchOnActivate, "\uE8D6"), // Highlight icon
CreateSettingItem("Show System Tray Icon", settings.ShowSystemTrayIcon, "\uE8A5"), // System icon
CreateSettingItem("Ignore Shortcut When Fullscreen", settings.IgnoreShortcutWhenFullscreen, "\uE740"), // Fullscreen icon
CreateSettingItem("Disable Animations", settings.DisableAnimations, "\uE916"), // Play icon
CreateSettingItem("Summon On", settings.SummonOn.ToString(), "\uE7C4"), // Position icon
];
}
private static ListItem CreateSettingItem(string name, object value, string iconGlyph)
{
var displayValue = value switch
{
bool b => b ? "Enabled" : "Disabled",
string s when string.IsNullOrEmpty(s) => "(not set)",
_ => value?.ToString() ?? "null",
};
return new ListItem(new NoOpCommand())
{
Title = name,
Subtitle = displayValue,
Icon = new IconInfo(iconGlyph),
};
}
}

View File

@@ -101,6 +101,14 @@ public partial class SamplesListPage : ListPage
Subtitle = "A demo of the settings helpers",
},
// Host Settings
new ListItem(new HostSettingsPage())
{
Title = "Host Settings",
Subtitle = "View current Command Palette host settings",
Icon = new IconInfo("\uE713"), // Settings icon
},
// Evil edge cases
// Anything weird that might break the palette - put that in here.
new ListItem(new EvilSamplesPage())

View File

@@ -6,7 +6,7 @@ using Windows.Foundation;
namespace Microsoft.CommandPalette.Extensions.Toolkit;
public abstract partial class CommandProvider : ICommandProvider, ICommandProvider2
public abstract partial class CommandProvider : ICommandProvider, ICommandProvider2, IHostSettingsChanged
{
public virtual string Id { get; protected set; } = string.Empty;
@@ -30,6 +30,16 @@ public abstract partial class CommandProvider : ICommandProvider, ICommandProvid
public virtual void InitializeWithHost(IExtensionHost host) => ExtensionHost.Initialize(host);
/// <summary>
/// Called when the host settings have changed. Override this method to respond to settings changes.
/// The base implementation updates the HostSettingsManager cache.
/// </summary>
/// <param name="settings">The updated host settings.</param>
public virtual void OnHostSettingsChanged(IHostSettings settings)
{
HostSettingsManager.Update(settings);
}
#pragma warning disable CA1816 // Dispose methods should call SuppressFinalize
public virtual void Dispose()
{
@@ -57,7 +67,7 @@ public abstract partial class CommandProvider : ICommandProvider, ICommandProvid
/// <returns>an array of objects that implement all the leaf interfaces we support</returns>
public object[] GetApiExtensionStubs()
{
return [new SupportCommandsWithProperties()];
return [new SupportCommandsWithProperties(), new HostSettingsChangedHandler(this)];
}
/// <summary>
@@ -69,4 +79,18 @@ public abstract partial class CommandProvider : ICommandProvider, ICommandProvid
{
public IDictionary<string, object>? GetProperties() => null;
}
/// <summary>
/// A stub class which implements IHostSettingsChanged. This is marshalled across
/// the ABI so that the host can call OnHostSettingsChanged on this object,
/// which then delegates to the CommandProvider's OnHostSettingsChanged method.
/// </summary>
private sealed partial class HostSettingsChangedHandler : IHostSettingsChanged
{
private readonly CommandProvider _provider;
public HostSettingsChangedHandler(CommandProvider provider) => _provider = provider;
public void OnHostSettingsChanged(IHostSettings settings) => _provider.OnHostSettingsChanged(settings);
}
}

View File

@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CommandPalette.Extensions.Toolkit;
/// <summary>
/// Concrete implementation of IHostSettings for the Command Palette host settings.
/// This class is used by the host to create settings objects that can be passed to extensions.
/// </summary>
public partial class HostSettings : IHostSettings
{
/// <summary>
/// Gets or sets the global hotkey for summoning the Command Palette.
/// </summary>
public string Hotkey { get; set; } = string.Empty;
/// <summary>
/// Gets or sets a value indicating whether to show app details in the UI.
/// </summary>
public bool ShowAppDetails { get; set; }
/// <summary>
/// Gets or sets a value indicating whether pressing the hotkey goes to the home page.
/// </summary>
public bool HotkeyGoesHome { get; set; }
/// <summary>
/// Gets or sets a value indicating whether backspace navigates back.
/// </summary>
public bool BackspaceGoesBack { get; set; }
/// <summary>
/// Gets or sets a value indicating whether single click activates items.
/// </summary>
public bool SingleClickActivates { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to highlight the search box on activation.
/// </summary>
public bool HighlightSearchOnActivate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to show the system tray icon.
/// </summary>
public bool ShowSystemTrayIcon { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to ignore the shortcut when in fullscreen mode.
/// </summary>
public bool IgnoreShortcutWhenFullscreen { get; set; }
/// <summary>
/// Gets or sets a value indicating whether animations are disabled.
/// </summary>
public bool DisableAnimations { get; set; }
/// <summary>
/// Gets or sets the target monitor behavior when summoning the Command Palette.
/// </summary>
public SummonTarget SummonOn { get; set; }
}

View File

@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Microsoft.CommandPalette.Extensions.Toolkit;
/// <summary>
/// Provides static access to the current Command Palette host settings.
/// Extensions can use this class to read the current settings values and respond to changes.
/// </summary>
public static class HostSettingsManager
{
private static IHostSettings? _current;
/// <summary>
/// Occurs when the host settings have changed.
/// Extensions can subscribe to this event to refresh their UI when settings are updated.
/// </summary>
public static event Action? SettingsChanged;
/// <summary>
/// Gets the current host settings, or null if not yet initialized.
/// </summary>
public static IHostSettings? Current => _current;
/// <summary>
/// Gets a value indicating whether host settings are available.
/// Returns false if the extension is running with an older host that doesn't support settings.
/// </summary>
public static bool IsAvailable => _current != null;
/// <summary>
/// Updates the cached host settings. Called internally when settings change.
/// </summary>
/// <param name="settings">The updated host settings.</param>
internal static void Update(IHostSettings settings)
{
_current = settings;
SettingsChanged?.Invoke();
}
}

View File

@@ -391,6 +391,53 @@ namespace Microsoft.CommandPalette.Extensions
{
Object[] GetApiExtensionStubs();
};
// =========================================================================
// Host Settings and Capability System
// =========================================================================
/// <summary>
/// Represents the position behavior when summoning the Command Palette.
/// </summary>
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
enum SummonTarget
{
ToMouse = 0,
ToPrimary = 1,
ToFocusedWindow = 2,
InPlace = 3,
ToLast = 4,
};
/// <summary>
/// Interface for host settings that can be passed to extensions across process boundaries.
/// Extensions can access these settings to adapt their behavior accordingly.
/// The Toolkit provides a concrete HostSettings class that implements this interface.
/// </summary>
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
interface IHostSettings
{
String Hotkey { get; };
Boolean ShowAppDetails { get; };
Boolean HotkeyGoesHome { get; };
Boolean BackspaceGoesBack { get; };
Boolean SingleClickActivates { get; };
Boolean HighlightSearchOnActivate { get; };
Boolean ShowSystemTrayIcon { get; };
Boolean IgnoreShortcutWhenFullscreen { get; };
Boolean DisableAnimations { get; };
SummonTarget SummonOn { get; };
}
/// <summary>
/// Interface for extensions to receive notifications when host settings change.
/// Extensions implementing this interface will be notified whenever the user
/// modifies Command Palette settings, including initial settings on startup.
/// </summary>
[contract(Microsoft.CommandPalette.Extensions.ExtensionsContract, 1)]
interface IHostSettingsChanged
{
void OnHostSettingsChanged(IHostSettings settings);
}
}

View File

@@ -24,13 +24,13 @@
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<!-- <PropertyGroup>
<ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(CIBuild)'=='true'">
<ApplicationManifest>ImageResizerUI.prod.manifest</ApplicationManifest>
</PropertyGroup>
</PropertyGroup> -->
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">

View File

@@ -3410,7 +3410,7 @@ Activate by holding the key for the character you want to add an accent to, then
<value>An AI powered tool to put your clipboard content into any format you need, focused towards developer workflows.</value>
</data>
<data name="AdvancedPaste_EnableAISettingsCardDescription.Text" xml:space="preserve">
<value>Transform your clipboard content with the power of AI. An cloud or local endpoint is required.</value>
<value>Transform your clipboard content with the power of AI. A cloud or local endpoint is required.</value>
</data>
<data name="AdvancedPaste_EnableAISettingsCardDescriptionLearnMore.Content" xml:space="preserve">
<value>Learn more</value>