165 lines
5.4 KiB
Markdown
165 lines
5.4 KiB
Markdown
---
|
|
name: add-fallback-commands
|
|
description: >-
|
|
Add fallback commands to your Command Palette extension for catch-all search behavior.
|
|
Use when asked to add search functionality, query matching, direct input handling,
|
|
calculator-style evaluation, URL opening, command execution, or results that appear
|
|
when no other extension matches. Used by 14 of 20 built-in extensions.
|
|
---
|
|
|
|
# Add Fallback Commands
|
|
|
|
Fallback commands are shown in Command Palette when no other results match the user's query. They enable your extension to act as a catch-all handler — perfect for calculators, web search, command execution, file path opening, and more.
|
|
|
|
## When to Use This Skill
|
|
|
|
- Adding search functionality that responds to any user input
|
|
- Creating a calculator that evaluates expressions as the user types
|
|
- Building a web search that triggers on unmatched queries
|
|
- Opening files or URLs typed directly into the palette
|
|
- Executing shell commands from the search bar
|
|
|
|
## How Fallback Commands Work
|
|
|
|
1. User types a query in Command Palette
|
|
2. If no top-level commands match, CmdPal asks extensions for fallback results
|
|
3. Your extension's `FallbackCommands()` provides items that respond to the query
|
|
4. The fallback items can be static (always shown) or dynamic (filtered by query)
|
|
|
|
## Quick Start: Static Fallback
|
|
|
|
Override `FallbackCommands()` in your `CommandProvider`:
|
|
|
|
```csharp
|
|
public partial class MyCommandsProvider : CommandProvider
|
|
{
|
|
private readonly ICommandItem[] _commands;
|
|
private readonly FallbackCommandItem[] _fallbacks;
|
|
|
|
public MyCommandsProvider()
|
|
{
|
|
DisplayName = "Web Search";
|
|
Icon = new IconInfo("\uE721"); // Search icon
|
|
|
|
var searchPage = new WebSearchPage();
|
|
_commands = [new CommandItem(searchPage) { Title = DisplayName }];
|
|
_fallbacks = [new FallbackCommandItem(searchPage) { Title = "Search the web" }];
|
|
}
|
|
|
|
public override ICommandItem[] TopLevelCommands() => _commands;
|
|
public override IFallbackCommandItem[] FallbackCommands() => _fallbacks;
|
|
}
|
|
```
|
|
|
|
## Dynamic Fallback with DynamicListPage
|
|
|
|
For fallbacks that filter results based on the query, use `DynamicListPage`:
|
|
|
|
```csharp
|
|
internal sealed partial class WebSearchPage : DynamicListPage
|
|
{
|
|
private string _query = string.Empty;
|
|
|
|
public WebSearchPage()
|
|
{
|
|
Icon = new IconInfo("\uE721");
|
|
Title = "Web Search";
|
|
Name = "Search";
|
|
PlaceholderText = "Type to search...";
|
|
}
|
|
|
|
public override void UpdateSearchText(string oldSearch, string newSearch)
|
|
{
|
|
_query = newSearch;
|
|
RaiseItemsChanged();
|
|
}
|
|
|
|
public override IListItem[] GetItems()
|
|
{
|
|
if (string.IsNullOrWhiteSpace(_query))
|
|
return [];
|
|
|
|
return [
|
|
new ListItem(new OpenUrlCommand($"https://www.google.com/search?q={Uri.EscapeDataString(_query)}"))
|
|
{
|
|
Title = $"Search Google for \"{_query}\"",
|
|
Icon = new IconInfo("\uE721"),
|
|
},
|
|
new ListItem(new OpenUrlCommand($"https://www.bing.com/search?q={Uri.EscapeDataString(_query)}"))
|
|
{
|
|
Title = $"Search Bing for \"{_query}\"",
|
|
Icon = new IconInfo("\uE721"),
|
|
},
|
|
];
|
|
}
|
|
}
|
|
```
|
|
|
|
## Responsive Fallback with Cancellation
|
|
|
|
For expensive operations (API calls, file searches), use cancellation to stay responsive:
|
|
|
|
```csharp
|
|
internal sealed partial class SmartSearchPage : DynamicListPage
|
|
{
|
|
private CancellationTokenSource? _cts;
|
|
private IListItem[] _results = [];
|
|
|
|
public override void UpdateSearchText(string oldSearch, string newSearch)
|
|
{
|
|
// Cancel any in-flight search
|
|
_cts?.Cancel();
|
|
_cts = new CancellationTokenSource();
|
|
var token = _cts.Token;
|
|
|
|
_ = Task.Run(async () =>
|
|
{
|
|
// Debounce: wait for user to stop typing
|
|
await Task.Delay(300, token);
|
|
if (token.IsCancellationRequested) return;
|
|
|
|
// Perform search
|
|
_results = await SearchAsync(newSearch, token);
|
|
RaiseItemsChanged();
|
|
}, token);
|
|
}
|
|
|
|
public override IListItem[] GetItems() => _results;
|
|
|
|
private async Task<IListItem[]> SearchAsync(string query, CancellationToken token)
|
|
{
|
|
// Your search logic here
|
|
// Check token.IsCancellationRequested periodically
|
|
return [];
|
|
}
|
|
}
|
|
```
|
|
|
|
## Real-World Examples (from built-in extensions)
|
|
|
|
| Extension | Fallback Behavior |
|
|
|-----------|------------------|
|
|
| **Apps** | Search installed applications by name |
|
|
| **Calc** | Evaluate mathematical expressions directly |
|
|
| **Shell** | Execute command-line commands |
|
|
| **WebSearch** | Search the web with configured engine |
|
|
| **Indexer** | Open files by path |
|
|
| **TimeDate** | Parse time/date queries |
|
|
| **WindowsSettings** | Jump to Windows Settings pages |
|
|
| **WinGet** | Search WinGet packages |
|
|
| **WindowWalker** | Find and switch to open windows |
|
|
|
|
## Key Points
|
|
|
|
- `FallbackCommands()` returns `IFallbackCommandItem[]` (not `ICommandItem[]`)
|
|
- Use `FallbackCommandItem` wrapper (not `CommandItem`)
|
|
- Wrap a `DynamicListPage` for query-reactive results
|
|
- Cancel previous searches when new input arrives
|
|
- Keep fallback responses fast — users expect instant results
|
|
- Use `PlaceholderText` on your page to guide users
|
|
|
|
## Documentation
|
|
|
|
- [Extension samples](https://learn.microsoft.com/windows/powertoys/command-palette/samples)
|
|
- [Extensibility overview](https://learn.microsoft.com/windows/powertoys/command-palette/extensibility-overview)
|