Skip to content

Commit e0c487b

Browse files
Merge pull request #5232 from MicrosoftDocs/cinnamon/pt-cmd-pal
Add Command Palette to PowerToys
2 parents 80b5961 + 4abffa4 commit e0c487b

File tree

281 files changed

+9121
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

281 files changed

+9121
-0
lines changed

hub/dev-environment/toc.yml

Lines changed: 774 additions & 0 deletions
Large diffs are not rendered by default.
212 KB
Loading
14.2 KB
Loading

hub/images/pt-cmdpal-overview1.gif

8.64 MB
Loading

hub/images/pt-cmdpal-overview2.gif

4.92 MB
Loading

hub/images/pt-cmdpal.png

115 KB
Loading
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
---
2+
title: Add top-level commands to your extension
3+
description: Learn how to add new top-level commands to your Command Palette extension.
4+
ms.date: 3/23/2025
5+
ms.topic: how-to
6+
no-loc: [PowerToys, Windows, Insider]
7+
# Customer intent: As a Windows developer, I want to learn how to develop an extension for the Command Palette.
8+
---
9+
10+
# Adding top-level commands to your extension
11+
12+
**Previous**: [Update a list of commands](update-a-list-of-commands.md).
13+
14+
So far, you've only added commands to a single page within your extension. You can also add more commands directly to the top-level list of commands too.
15+
16+
## Adding the top-level commands
17+
18+
To do that, head on over to the `ExtensionNameCommandsProvider.cs` file. This file is where you'll add commands that should be shown at the top-level of the Command Palette. As you can see, there's currently only a single item there:
19+
20+
```csharp
21+
public ExtensionNameCommandsProvider()
22+
{
23+
DisplayName = "My sample extension";
24+
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
25+
_commands = [
26+
new CommandItem(new ExtensionNamePage()) { Title = DisplayName },
27+
];
28+
}
29+
30+
public override ICommandItem[] TopLevelCommands()
31+
{
32+
return _commands;
33+
}
34+
```
35+
36+
This sample extension creates a list of commands when the extension is created and returns that list whenever it's asked for the top-level commands. This prevents the extension from re-creating the list of commands every time the top-level commands are requested. This is a performance optimization.
37+
38+
If you want to add another command to the top-level list of commands, you can add another **CommandItem**:
39+
40+
```csharp
41+
public ExtensionNameCommandsProvider()
42+
{
43+
DisplayName = "My sample extension";
44+
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
45+
_commands = [
46+
new CommandItem(new ExtensionNamePage()) { Title = DisplayName },
47+
new CommandItem(new ShowMessageCommand()) { Title = "Send a message" },
48+
];
49+
}
50+
```
51+
52+
There you have it. Now you can add additional top-level commands to your extension.
53+
54+
If you'd like to update the list of top-level commands dynamically, you can do so in the same way as you would update a list page. This can be useful for cases like an extension that might first require the user to log in, before showing certain commands. In that case, you can show the "log in" command at the top level initially. Then, once the user logs in successfully, you can update the list of top-level commands to include the commands that required authentication.
55+
56+
Once you've determined that you need to change the top level list, call [RaiseItemsChanged](./microsoft-commandpalette-extensions-toolkit/commandprovider_raiseitemschanged.md) on your **CommandProvider**. Command Palette will then request the top-level commands via **TopLevelCommands** again, and you can return the updated list.
57+
58+
> [!TIP]
59+
> Create the **CommandItem** objects for the top-level commands before calling **RaiseItemsChanged**. This will ensure that the new commands are available when Command Palette requests the top-level commands. This will ensure that the work being executed in each call to **TopLevelCommands** method to a minimum.
60+
61+
### Next up: [Command Results](command-results.md)
62+
63+
## Related content
64+
65+
- [PowerToys Command Palette utility](overview.md)
66+
- [Extensibility overview](extensibility-overview.md)
67+
- [Extension samples](samples.md)
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
---
2+
title: Adding commands to your extension
3+
description: Learn how to add new commands to your Command Palette extension.
4+
ms.date: 3/23/2025
5+
ms.topic: how-to
6+
no-loc: [PowerToys, Windows, Insider]
7+
# Customer intent: As a Windows developer, I want to learn how to develop an extension for the Command Palette.
8+
---
9+
10+
# Adding commands to your extension
11+
12+
**Previous**: [Creating an extension](creating-an-extension.md). We'll be starting with the project created in that article.
13+
14+
Now that you've created your extension, it's time to add some commands to it.
15+
16+
## Add some commands
17+
18+
We can start by navigating to the `ExtensionNamePage.cs` file. This file is the [ListPage](./microsoft-commandpalette-extensions-toolkit/listpage.md) that will be displayed when the user selects your extension. In there you should see:
19+
20+
```csharp
21+
public ExtensionNamePage()
22+
{
23+
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
24+
Title = "My sample extension";
25+
Name = "Open";
26+
}
27+
public override IListItem[] GetItems()
28+
{
29+
return [
30+
new ListItem(new NoOpCommand()) { Title = "TODO: Implement your extension here" }
31+
];
32+
}
33+
```
34+
35+
Here you can see that we've set the icon for the page, the title, and the name that's shown at the top-level when you have the command selected. The **GetItems** method is where you'll return the list of commands that you want to show on this page. Right now, that's just returning a single command that does nothing. Let's instead try making that command open _this_ page in the user's default web browser.
36+
37+
We can change the implementation of **GetItems** to the following:
38+
39+
```csharp
40+
public override IListItem[] GetItems()
41+
{
42+
var command = new OpenUrlCommand("https://learn.microsoft.com/windows/powertoys/command-palette/adding-commands");
43+
return [
44+
new ListItem(command)
45+
{
46+
Title = "Open the Command Palette documentation",
47+
}
48+
];
49+
}
50+
```
51+
52+
Re-deploy your app, run the "reload" command to refresh the extensions in the palette, and head to your extension. You should see that the command now opens the Command Palette documentation.
53+
54+
The **OpenUrlCommand** is a helper for opening a URL in the user's default web browser. You can also implement an extension however you want. Let's instead make a new command, that shows a **MessageBox**. To do that, we need to create a new class that implements **IInvokableCommand**.
55+
56+
```csharp
57+
using System.Runtime.InteropServices;
58+
59+
namespace ExtensionName;
60+
61+
internal sealed partial class ShowMessageCommand : InvokableCommand
62+
{
63+
public override string Name => "Show message";
64+
public override IconInfo Icon => new("\uE8A7");
65+
66+
public override CommandResult Invoke()
67+
{
68+
// 0x00001000 is MB_SYSTEMMODAL, which will display the message box on top of other windows.
69+
_ = MessageBox(0, "I came from the Command Palette", "What's up?", 0x00001000);
70+
return CommandResult.KeepOpen();
71+
}
72+
73+
74+
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
75+
public static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);
76+
}
77+
```
78+
79+
Now we can add this command to the list of commands in the `ExtensionNamePage.cs` file:
80+
81+
```csharp
82+
public override IListItem[] GetItems()
83+
{
84+
var command = new OpenUrlCommand("https://learn.microsoft.com/windows/powertoys/command-palette/creating-an-extension");
85+
var showMessageCommand = new ShowMessageCommand();
86+
return [
87+
new ListItem(command)
88+
{
89+
Title = "Open the Command Palette documentation",
90+
},
91+
new ListItem(showMessageCommand),
92+
];
93+
}
94+
```
95+
96+
Deploy and reload, and presto - a command to show a message box!
97+
98+
> [!TIP]
99+
> At about this point, you'll probably want to initialize a git repo / {other source control method of your choice} for your project. This will make it easier to track changes, and to share your extension with others.
100+
>
101+
> We recommend using GitHub, as it's easy to collaborate on your extension with others, and get feedback, and share it with the world.
102+
103+
## Adding more pages
104+
105+
So far, we've only worked with commands that "do something". However, you can also add commands that show additional pages withing the Command Palette. There are basically two types of "Commands" in the Palette:
106+
- **IInvokableCommand** - These are commands that *do something*.
107+
- **IPage** - These are commands that *show something*.
108+
109+
Because **IPage** implementations are **ICommand**'s, you can use them anywhere you can use commands. This means you can add them to the top-level list of commands, or to a list of commands on a page, the context menu on an item, etc.
110+
111+
There are two different kinds of pages you can show:
112+
- [ListPage](./microsoft-commandpalette-extensions-toolkit/listpage.md) - This is a page that shows a list of commands. This is what we've been working with so far.
113+
- [ContentPage](./microsoft-commandpalette-extensions-toolkit/contentpage.md) - This is a page that shows rich content to the user. This allows you to specify abstract content, and let Command Palette worry about rendering the content in a native experience. There are two different types of content supported so far:
114+
- [Markdown content](./using-markdown-content.md) - This is content that's written in Markdown, and is rendered in the Command Palette. See [MarkdownContent](./microsoft-commandpalette-extensions-toolkit/markdowncontent.md) for details.
115+
- [Form content](./using-form-pages.md) - This is content that shows a form to the user, and then returns the results of that form to the extension. These are powered by [Adaptive Cards](https://aka.ms/adaptive-cards) This is useful for getting user input, or displaying more complex layouts of information. See [FormContent](./microsoft-commandpalette-extensions-toolkit/formcontent.md) for details.
116+
117+
118+
Start by adding a new page that shows a list of commands. Create a new class that implements **ListPage**:
119+
120+
```csharp
121+
using Microsoft.CommandPalette.Extensions.Toolkit;
122+
using System.Linq;
123+
124+
namespace ExtensionName;
125+
126+
internal sealed partial class MySecondPage : ListPage
127+
{
128+
public MySecondPage()
129+
{
130+
Icon = new("\uF147"); // Dial2
131+
Title = "My second page";
132+
Name = "Open";
133+
}
134+
135+
public override IListItem[] GetItems()
136+
{
137+
// Return 100 CopyText commands
138+
return Enumerable
139+
.Range(0, 100)
140+
.Select(i => new ListItem(new CopyTextCommand($"{i}"))
141+
{
142+
Title = $"Copy text {i}"
143+
}).ToArray();
144+
}
145+
}
146+
```
147+
148+
Next, update the `ExtensionNamePage.cs` to include this new page:
149+
150+
```diff
151+
public override IListItem[] GetItems()
152+
{
153+
OpenUrlCommand command = new("https://learn.microsoft.com/windows/powertoys/command-palette/creating-an-extension");
154+
return [
155+
new ListItem(command)
156+
{
157+
Title = "Open the Command Palette documentation",
158+
},
159+
new ListItem(new ShowMessageCommand()),
160+
+ new ListItem(new MySecondPage()) { Title = "My second page", Subtitle = "A second page of commands" },
161+
];
162+
}
163+
```
164+
165+
Deploy, reload, and you should now see a new page in your extension that shows 100 commands that copy a number to the clipboard.
166+
167+
### Next up: [Update a list of commands](update-a-list-of-commands.md)
168+
169+
## Related content
170+
171+
- [PowerToys Command Palette utility](overview.md)
172+
- [Extensibility overview](extensibility-overview.md)
173+
- [Extension samples](samples.md)
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
title: Command results
3+
description: Learn what the different kinds of Command Palette command results do.
4+
ms.date: 3/23/2025
5+
ms.topic: concept-article
6+
no-loc: [PowerToys, Windows, Insider]
7+
# Customer intent: As a Windows developer, I want to learn how to develop an extension for the Command Palette.
8+
---
9+
10+
# Command results
11+
12+
**Previous**: [Add top-level commands to your extension](add-top-level-commands-to-your-extension.md)
13+
14+
An [IInvokableCommand](./microsoft-commandpalette-extensions/iinvokablecommand.md) is a fundamental unit of *do something* in the Command Palette. The [Invoke](./microsoft-commandpalette-extensions/iinvokablecommand.md) method is called when the user selects the command, and it's where you *do something* in your extension. The **Invoke** method returns an **ICommandResult**, which tells the Command Palette what to do after the command has been invoked. This page details what's possible with each type of command result.
15+
16+
The toolkit provides a number of helper methods to create command results. These are all static methods on the **CommandResult** class. Calling these methods on their own won't do anything. You must return those objects as the result of a **Invoke** method, for Command Palette to handle them.
17+
18+
<!-- GoToPage currently omitted from these docs, because it's not remotely implemented -->
19+
20+
## KeepOpen command result
21+
22+
The **KeepOpen** command result does nothing. It leaves the palette in its current state, with the current page stack and query. This can be useful for commands that want to keep the the user in the Command Palette, to keep working with the current page.
23+
24+
> [!NOTE]
25+
> Even when returing **KeepOpen**, launching a new app or window from the Command Palette will automatically hide the palette the next window recieves focus.
26+
27+
## Hide command result
28+
29+
This command result keeps the current page open, but hides the Command Palette. This can be useful for commands that want to take the user briefly out of the Command Palette, but then come back to this context.
30+
31+
## GoBack command result
32+
33+
This result takes the user back a page in the Command Palette, and keeps the window visible. This is perfect for form pages, where doing the command should take you the user back to the previous context.
34+
35+
## GoHome command result
36+
37+
This result takes the user back to the main page of the Command Palette. It will leave the Palette visible (unless the palette otherwise loses focus). Consider using this for scenarios where you've changed your top-level commands.
38+
39+
## Dismiss command result
40+
41+
This result hides the Command Palette after the action is executed, and takes it back to the hme page. On the next launch, the Command Palette will start from the main page with a blank query. This is useful for commands that are one-off actions, or that don't need to keep the Command Palette open.
42+
43+
If you don't know what else to use, this should be your default. Ideally, users should come into the palette, find what they need, and be done with it.
44+
45+
## ShowToast command result
46+
47+
This result displays a transient desktop-level message to the user. This is especially useful for displaying confirmation that an action took place when the palette will be closed.
48+
49+
Consider the [CopyTextCommand](./microsoft-commandpalette-extensions-toolkit/copytextcommand.md) in the helpers - this command will show a toast with the text "Copied to clipboard", then dismiss the palette.
50+
51+
By default, [CommandResult.ShowToast(string)](./microsoft-commandpalette-extensions-toolkit/commandresult_showtoast_string.md) helper will have a **Result** of `CommandResult.Dismiss`. However, you can instead change the result to any of the other results if you want. This allows you to display a toast and keep the palette open, if you'd like.
52+
53+
## Confirm command result
54+
55+
This result displays a confirmation dialog to the user. If the user confirms the dialog, then the **PrimaryCommand** of the *ConfirmationArgs* will be performed.
56+
57+
This is useful for commands that might have destructive actions, or that need to confirm user intent.
58+
59+
## Example
60+
61+
As an example, here's a page with one command for each kind of command result:
62+
63+
```csharp
64+
65+
using Microsoft.CommandPalette.Extensions.Toolkit;
66+
67+
internal sealed partial class CommandResultsPage : ListPage
68+
{
69+
public CommandResultsPage()
70+
{
71+
Icon = IconHelpers.FromRelativePath("Assets\\StoreLogo.png");
72+
Title = "Example command results";
73+
Name = "Open";
74+
}
75+
76+
public override IListItem[] GetItems()
77+
{
78+
ConfirmationArgs confirmArgs = new()
79+
{
80+
PrimaryCommand = new AnonymousCommand(
81+
() =>
82+
{
83+
ToastStatusMessage t = new("The dialog was confirmed");
84+
t.Show();
85+
})
86+
{
87+
Name = "Confirm",
88+
Result = CommandResult.KeepOpen(),
89+
},
90+
Title = "You can set a title for the dialog",
91+
Description = "Are you really sure you want to do the thing?",
92+
};
93+
94+
return
95+
[
96+
new ListItem(new AnonymousCommand(null) { Result = CommandResult.KeepOpen() }) { Title = "Keep the palette open" },
97+
new ListItem(new AnonymousCommand(null) { Result = CommandResult.Hide() }) { Title = "Hide the palette" },
98+
new ListItem(new AnonymousCommand(null) { Result = CommandResult.GoBack() }) { Title = "Go back" },
99+
new ListItem(new AnonymousCommand(null) { Result = CommandResult.GoHome() }) { Title = "Go home" },
100+
new ListItem(new AnonymousCommand(null) { Result = CommandResult.Dismiss() }) { Title = "Dismiss the palette" },
101+
new ListItem(new AnonymousCommand(null) { Result = CommandResult.ShowToast("What's up") }) { Title = "Show a toast" },
102+
new ListItem(new AnonymousCommand(null) { Result = CommandResult.Confirm(confirmArgs) }) { Title = "Confirm something" },
103+
];
104+
}
105+
}
106+
```
107+
108+
### Next up: [Display markdown content](using-markdown-content.md)
109+
110+
## Related content
111+
112+
- [PowerToys Command Palette utility](overview.md)
113+
- [Extensibility overview](extensibility-overview.md)
114+
- [Extension samples](samples.md)

0 commit comments

Comments
 (0)