首页
/ PowerToys Command Palette 扩展开发完全指南:Root View、Page 体系与 ICommandProvider 扩展机制

PowerToys Command Palette 扩展开发完全指南:Root View、Page 体系与 ICommandProvider 扩展机制

2026-09-07 22:15:05作者:董灵辛Dennis

Microsoft PowerToys 内置的 Command Palette(命令面板) 是一个以“命令”为核心的可扩展启动器:按下 Win+Ctrl+. 即可呼出,在其中搜索并执行内置命令与第三方扩展提供的命令。本篇技术指南以仓库中的 command-palette-anatomy.md 为骨架,系统讲解 Command Palette 的界面结构(Root View 与各类 Page)、扩展 SDK 的核心接口(ICommandProviderICommand 等),并完整演示如何基于扩展模板从零构建一个可运行、可部署的 SSH Keychain 扩展。读完本文,你将掌握 Command Palette 扩展的完整开发流程,并能在 src/modules/cmdpal 的源码中快速定位每一个机制背后的真实实现。

Root View:命令面板的根视图

Root View(根视图)是 Command Palette 首次被打开时显示的那个视图,也是用户见到的第一个界面。从 Command Palette 的角度看,它本质上是一种特殊的 ListPage

  • 它特殊在展示的是所有顶层命令,包括内置命令(built-in commands)与已安装扩展通过 ICommandProvider 提供的命令;
  • 在这个语境下,所谓“命令”其实就是在根视图中显示的 ListItem。换句话说,根视图里的所有 ListItem 都被称为“命令”;
  • 用户可以通过顶部的 FilterBox 输入文字来搜索/过滤命令,面板会基于用户的输入实时过滤根视图中展示的命令。

当用户点击某个命令时,会执行该命令关联的默认命令(default command);当命令被选中后,用户直接按下 Enter 键也会执行同样的默认命令。

在 UI 工程中,根视图与搜索框的实际渲染位于 Microsoft.CmdPal.UI 工程(例如 SearchBar.xamlListPage.xamlListItemsView.xaml),负责承载顶层命令逻辑的 ViewModel 则在 Microsoft.CmdPal.UI.ViewModels(如 TopLevelViewModel.csListItemViewModel.cs)。

嵌套视图(Page)体系

当一条命令被执行时,它可以在 Command Palette 内部打开一个“嵌套”的新视图,这些视图被称为 Page(页面)。所有 Page 都派生自 IPage 基础契约,并在此之上根据展示形态扩展为若干具体类型。

ListPage(列表页)

列表页在命令面板内展示一个条目列表。文档中给出的设计级接口如下:

interface IListPage requires IPage {
    String SearchText { get; };
    String PlaceholderText { get; };
    Boolean ShowDetails{ get; };
    IFilters Filters { get; };
    IGridProperties GridProperties { get; };

    ISection[] GetItems(); // DevPal 负责对这些条目进行过滤
}

注意 GetItems() 的注释:列表页自身的过滤工作(模糊搜索)由 Command Palette 宿主完成,扩展作者只需要把条目数据交给它。这一点在实战章节会再次验证。

在仓库当前源码中,SDK 的 Toolkit 层提供了对应的可继承基类 ListPage.cs

  • PlaceholderText:搜索框占位文本,默认空字符串;
  • SearchText:当前搜索关键字(宿主回写,扩展可读取);
  • ShowDetails:是否展示详情面板(绑定到设置项);
  • HasMoreItems / LoadMore():是否还有更多条目、以及加载更多的回调,用于分页/惰性加载场景;
  • FiltersIFilters)与 GridPropertiesIGridProperties):分别用于声明页面的筛选器集合与网格布局参数;
  • EmptyContent:列表为空时展示的占位内容;
  • GetItems():返回需要展示的条目,默认返回空数组;
  • ItemsChanged 事件与 RaiseItemsChanged():条目集合变化时通知宿主刷新 UI。

ISection

一个列表页往往需要把条目组织成多个“分组/分节”。ISection 就是这样一个分节抽象:每一节拥有自己的标题,并承载一组条目。对应 Toolkit 实现为 Section.cs,典型的构造方式是在 GetItems() 中返回一个或多个 new ListSection() { Title = "分组标题", Items = [...] }(下文 SSH Keychain 例子中 "SSH Hosts" 一节即为典型用法)。

GridProperties

GridProperties 控制列表页以网格/宫格布局展示时的属性(列数、尺寸等),对应 IGridProperties 接口。Toolkit 提供 SmallGridLayout.csMediumGridLayout.csGalleryGridLayout.cs 等预设布局实现。若未设置网格属性,列表页按常规列表渲染。

ShowDetails

ShowDetails 是布尔开关:开启后,列表页允许对条目展示“详情”(details)内容。详情的面板模型由 IDetails 支撑,Toolkit 对应 Details.cs;详情承载的元素(链接、分隔符、标签等)分别有 DetailsLink.csDetailsSeparator.csDetailsTags.cs

PlaceholderText

PlaceholderText 即搜索框(FilterBox)在没有输入时显示的灰色提示文字。默认值为空字符串,扩展可按需覆盖,例如设置成“搜索 SSH 主机…”。

SearchText

SearchText 是用户在 FilterBox 中实时输入的搜索关键字,由宿主写入列表页对象。普通 ListPage 无需关心它(宿主按此关键字对 GetItems() 结果做模糊过滤);只有 DynamicListPage 需要接管它来亲自实现过滤。

MarkdownPage

Markdown 页面在命令面板内部渲染 Markdown 内容,其设计级接口为:

interface IMarkdownPage requires IPage {
    String[] Bodies();
    IDetails Details();
    IContextItem[] Commands { get; };
}

即:若干 Markdown 正文(Bodies)、可选详情(Details)以及一组上下文命令(Commands)。Toolkit 侧对应的页面基类与内容抽象可参考 ContentPage.csMarkdownContent.cs

FormPage

表单页在命令面板内部渲染一个表单(如参数填写、设置编辑),其设计级接口为:

interface IFormPage requires IPage {
    IForm[] Forms();
}

表单的能力由 IForm 系列接口承担;Toolkit 中既有面向“设置表单”的 SettingsForm.cs,也有通用的 FormContent(见 FormContent.cs)。表单提交动作通过 IFormContent2.SubmitForm / SubmitAction 等契约返回 ICommandResult(参见 CommandProvider.cs 中用于注册接口的存根实现)。

DynamicListPage(动态列表页)与内置模糊过滤

动态列表页与普通列表页相似,但它把过滤控制权交给开发者,其设计级接口为:

interface IDynamicListPage requires IListPage {
   ISection[] GetItems(String query); // DevPal 不再对这些条目做任何过滤
}

也就是说,宿主不会替它过滤,而是把查询字符串透传给扩展,由扩展自己决定展示什么。Toolkit 的 DynamicListPage.cs 在实现上覆盖了 SearchText 的 setter:当宿主写入新的搜索词时,会调用抽象方法 UpdateSearchText(string oldSearch, string newSearch),这正是扩展实现自定义过滤/检索的挂钩点。

与之相对,普通 ListPage 的内置模糊过滤(fuzzy search)由宿主侧完成,其底层实现可在 FuzzyMatcherProvider.cs 等文本匹配组件中看到——这也是为何实战中“搜索 SSH 主机”无需写一行过滤代码即可工作。

附加组件:ListItem、Tags 与 FilterBox

ListItem

ListItem 是显示在 ListPage 上的条目对象,可以被点击以执行命令。根视图中的每个顶层命令本质上也是一个 ListItem。

ListItem 的核心可展示属性包括:

  • Title:主标题,例如 “Search SSH Keys”;
  • Subtitle:副标题,通常用来补充说明,例如 “Quickly find and launch into hosts from your SSH config file”;
  • Tags:标签数组(见下文);
  • Icon:图标;
  • Command(/关联命令):当该条目被点击或处于选中态按下 Enter 时所执行的默认命令;
  • MoreCommands:附加的上下文命令列表。

需要说明的是,Command Palette 的 SDK 正在演进中:较早设计中顶层命令以“命令即 ListItem”表达,而当前仓库模板(TemplateCmdPalExtensionCommandsProvider.cs)使用 CommandItem 包装一个命令或页面作为顶层条目,同时 Toolkit 也提供了独立的 ListItem.csCommandItem.cs。在开发中以“根视图入口用 CommandProvider 的 TopLevelCommands() 返回,页面条目用列表项承载命令”为理解主线即可。文档中的 SSH 示例同时展示了两种表达。

Tags

Tag(标签)用于在条目上渲染一个短文本徽标,帮助用户快速识别条目的类别或状态。Toolkit 中的 Tag.cs 即对应实现;构造时主要设置 Text。例如:

Tags = [new Tag() { Text = "Example Tag" }]

宿主 UI 的标签控件渲染位于 Microsoft.CmdPal.UI/Controls/Tag.xaml

FilterBox

FilterBox 是命令面板顶部的搜索输入框。它的作用是把用户输入的关键字实时应用于当前页面:

  • 在 Root View 中,它过滤顶层命令列表;
  • 在普通 ListPage 中,它触发宿主的模糊过滤,并把关键字写入页面的 SearchText
  • 在 DynamicListPage 中,它把关键字传给扩展的 UpdateSearchText

其 UI 实现在 SearchBar.xaml,占位文案的绑定与转换可参考 PlaceholderTextConverter(见 Converters 目录)。

扩展解剖:ICommandProvider 与 ICommand

ICommandProvider:所有扩展进入 Command Palette 的入口

这是扩展进入 Command Palette 的唯一入口点。 扩展实现该接口即可向命令面板提供顶层命令:

public interface ICommandProvider
{
    IEnumerable<ICommand> GetCommands();
}

在 Toolkit 中,扩展通常继承抽象基类 CommandProvider.cs,该基类实现了 ICommandProvider 系列接口并额外提供了常用成员:

  • DisplayName:提供给用户看到的扩展/命令提供者名称;
  • Id:稳定标识符;
  • Icon:在根视图/设置中显示的图标;
  • TopLevelCommands()抽象方法,必须实现,返回将在根视图展示的顶层命令数组;
  • FallbackCommands():返回“兜底命令”(当其它命令无法匹配时展示),默认返回 null
  • GetCommand(string id) / GetCommandItem(string id):按 id 精确取回命令/命令项;
  • Settings:扩展自身的设置对象;
  • Frozen:标记该提供者是否已冻结(不再变更命令集);
  • InitializeWithHost(IExtensionHost host):初始化时由宿主注入 IExtensionHost,扩展可用它访问宿主能力(见 ExtensionHost.cs);
  • RaiseItemsChanged():命令集变化时通知宿主刷新。

ICommand:命令面板的“骨干”

ICommand 表示“一个可由命令面板执行的单元工作”,是整条命令链路的骨干。ListItem 与 ContextItem 都通过 ICommand 接口来执行命令:

public interface ICommand
{
    string Name { get; }
    void Execute();
}

Toolkit 中面向实际开发的两种派生形态值得关注:

  1. 页面型命令:命令本身就是一个 Page(例如把 SSHHostsListPage 直接当作条目的命令),执行它即在该页面中导航;
  2. 可执行型命令InvokableCommand.csInvokableCommand.Invoke() 返回一个 ICommandResult,可用于发起外部进程、执行动作,并通过返回值告诉宿主“执行后是否保持面板打开”。其默认实现为 CommandResult.KeepOpen()(保持打开)。

Command 基类(Command.cs)提供了 NameIcon 等通用属性;而像 NoOpCommand 这样的空操作命令则常被用来先“占位”再逐步替换。

默认命令(Default Command)

每个可交互条目可以关联一个默认命令:用户点击该条目,或在选中该条目后按下 Enter,宿主就执行这个默认命令。若条目关联的是页面型命令,则表现为在命令面板内打开新页面;若关联的是 InvokableCommand,则表现为执行一个具体动作。根视图入口、列表页条目均遵循这一统一交互模型。

实战:一步步构建 SSH Keychain 扩展

需求概述

我们要构建一个名为 “SSH Keychain” 的扩展,需要满足:

  1. 在命令面板提供一条顶层命令 “Search SSH Keys”
  2. 执行该命令后,命令面板内出现一个新页面,列出本机 SSH 配置文件中的所有 SSH 主机;
  3. 用户点击某个 SSH 主机、或选中后按下 Enter(执行其默认命令),即可用 Windows Terminal 打开一个到该主机的 SSH 会话

脚手架:用模板生成扩展工程

仓库把可复用的扩展脚手架放在 ExtensionTemplate/TemplateCmdPalExtension(内含模板工程、CommandsProviderPages 目录、打包用的 pubxml 等)。文档描述的初始步骤为:

  1. 进入 src\modules\cmdpal 目录;
  2. 运行 ext 目录下的 NewExtension.ps1 PowerShell 脚手架脚本。其中 -name 参数对应扩展(类)名,-DisplayName 参数对应将在根视图显示的顶层命令名
.\ext\NewExtension.ps1 -name MastodonExtension -DisplayName "Mastodon extension for cmdpal"
  1. 在 Visual Studio 中打开解决方案;
  2. 右键点击 “SampleExtensions” 目录,选择 “添加” → “现有项目”;
  3. 进入 Exts 文件夹,找到脚本生成的 *.csproj(它位于与 PowerShell 脚本提供的扩展名同名的目录下);
  4. 右键点击刚加入的新项目并选择 “部署”(Deploy);
  5. 点击 Visual Studio 的“播放/启动”按钮进行构建。

如果一切顺利,新命令就会出现在 Root View 中(此时通常还没有图标)。之后修改代码必须重新构建并再次 Deploy 才能看到效果

模板生成出的工程结构与当前仓库模板 TemplateCmdPalExtension.csproj 保持一致:程序入口 Program.cs 负责初始化扩展宿主,*CommandsProvider.cs 负责声明顶层命令,Pages/Data/Commands/Helpers/ 分别存放页面、数据模型、命令与辅助类。文档约定:所有页面放进 Pages 文件夹,数据类放进 Data 文件夹,命令放进 Commands 文件夹

Step 1:提供一个顶层命令

要让用户在命令面板中使用你的扩展,至少需要一个顶层命令。做法是在模板生成的 *CommandsProvider.cs 中继承 ICommandProvider 并实现 TopLevelCommands()(Toolkit 中继承 CommandProvider 重写该方法):

public class SSHKeychainCommandsProvider : CommandProvider
{
    public SSHKeychainCommandsProvider()
    {
        DisplayName = "SSH Keychain Commands";
    }

    private readonly ICommandItem[] _commands = [
       new CommandItem(new NoOpCommand())
        {
            Title = "Search SSH Keys",
            Subtitle = "Quickly find and launch into hosts from your SSH config file",
        },
    ];

    public override ICommandItem[] TopLevelCommands()
    {
        return _commands;
    }
}

这里用 NoOpCommand() 占位——它“什么都不做”,只是为了先让命令出现在面板里。按模板约定,把该实现放进独立文件 SSHKeychainCommandProvider.cs。重新构建并部署后,按 Win+Ctrl+. 打开命令面板搜索 “Search SSH Keys”,即可在 Root View 中看到这条命令。可以尝试修改 Title/Subtitle 来观察它对展示效果的影响。

Step 2:实现命令执行(页面导航)

当前命令没有动作,是因为条目绑定的是 NoOpCommand

private readonly IListItem[] _commands = [
       new ListItem(new NoOpCommand())
        {
            Title = "Search SSH Keys",
            Subtitle = "Quickly find and launch into hosts from your SSH config file",
        },
    ];

我们现在要实现执行逻辑:当用户点击/回车触发 “Search SSH Keys” 时,在命令面板内打开一个列出 SSH 主机的新 ListPage

Pages 文件夹新建 SSHHostsListPage

internal sealed class SSHHostsListPage : ListPage
{
    public SSHHostsListPage()
    {
        Icon = new(string.Empty);
        Name = "SSH Keychain";
    }

    public override ISection[] GetItems()
    {
        return [
            new ListSection()
            {
                Title = "SSH Hosts",
                Items = [
                    new ListItem(new NoOpCommand()) { Title = "TODO: Implement your extension here" }
                ],
            }
        ];
    }
}

列表页要求从 GetItems() 返回 ISection 数组。作为对照,可以查看仓库中 SamplePagesExtensionext/SamplePagesExtension)以及文档提到的 HackerNews 页示例——它们示范了一个带分节标题、页面图标与页面名的列表页。

要让根视图的 “Search SSH Keys” 命令默认打开这个新页面,只需把该 ListItem 的默认命令从 NoOpCommand 换成 SSHHostsListPage

// 更新前:条目没有真正可执行的默认命令
private readonly IListItem[] _commands = [
       new ListItem(new NoOpCommand())
        {
            Title = "Search SSH Keys",
            Subtitle = "Quickly find and launch into hosts from your SSH config file",
        },
    ];

// 更新后:默认命令指向我们新创建的页面
private readonly IListItem[] _commands = [
       new ListItem(new SSHHostsListPage())
        {
            Title = "Search SSH Keys",
            Subtitle = "Quickly find and launch into hosts from your SSH config file",
        },
    ];

重新构建后,根视图仍显示 “Search SSH Keys”,但触发其默认命令时,命令面板中会出现带有 “TODO: Implement your extension here” 消息的新列表页——第一步需求达成:

  • 命令执行后打开新页面 ✅
  • 剩余工作:解析 SSH 配置文件以真正列出主机。

接下来先用几条示例条目丰富 SSHHostsListPage,观察 Subtitle 与 Tag 的展示效果:

internal sealed class SSHHostsListPage : ListPage
{
    public SSHHostsListPage()
    {
        Icon = new(string.Empty);
        Name = "SSH Keychain";
    }

    public override ISection[] GetItems()
    {
        return [
            new ListSection()
            {
                Title = "SSH Hosts",
                Items = [
                    new ListItem(new NoOpCommand()) { Title = "TODO: Implement your extension here" },
                    new ListItem(new NoOpCommand()) { Title = "Another list item", Subtitle = "this one has a subtitle" },
                    new ListItem(new NoOpCommand())
                    {
                        Title = "A list item with a tag",
                        Subtitle = "this one with tags",
                        Tags = [new Tag()
                               {
                                   Text = "Example Tag",
                               }
                               ],
                    }
                ],
            }
        ];
    }
}

到这里我们已经具备:顶层命令 ✅、命令执行后打开新 ListPage ✅、在新页面内展示一组条目 ✅。

解析真实 SSH 主机

先补充“真实数据”。在 Data 文件夹新建 SSHKeychainItem.cs,表示单个 SSH 主机所需展示的信息:

internal class SSHKeychainItem
{
    internal string HostName { get; init; }

    internal string EscapedHost => JsonEncodedText.Encode(HostName).ToString();
}

说明:关联文档的草稿中该数据类使用了 Host 属性名,而后续解析与渲染代码统一使用 HostName,为保证代码可编译这里按后文用法统一为 HostName

随后改写 SSHHostsListPage,让它解析 %USERPROFILE%\.ssh\config 并渲染真实主机:

internal sealed partial class SSHHostsListPage : ListPage
{
    private static readonly string _defaultConfigFile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\.ssh\\config";

    private static readonly Regex _hostRegex = new(@"^Host\s+(?:(\S*) ?)*?\s*$", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);

    public SSHHostsListPage()
    {
        Icon = new(string.Empty);
        Name = "SSH Keychain";
    }

    private static async Task<List<SSHKeychainItem>> GetSSHHosts()
    {
        var hosts = new List<SSHKeychainItem>();
        var configFile = _defaultConfigFile;

        if (!File.Exists(configFile))
        {
            return hosts;
        }

        var options = new FileStreamOptions()
        {
            Access = FileAccess.Read,
        };

        using var reader = new StreamReader(configFile, options);
        var fileContent = await reader.ReadToEndAsync();

        if (!string.IsNullOrEmpty(fileContent))
        {
            var matches = _hostRegex.Matches(fileContent);
            hosts = matches.Select(match => new SSHKeychainItem { HostName = match.Groups[1].Value }).ToList();
        }

        return hosts;
    }

    public override ISection[] GetItems()
    {
        var t = DoGetItems();
        t.ConfigureAwait(false);
        return t.Result;
    }

    private async Task<ISection[]> DoGetItems()
    {
        List<SSHKeychainItem> items = await GetSSHHosts();
        var s = new ListSection()
        {
            Title = "SSH Hosts",
            Items = items.Select((host) => new ListItem(new NoOpCommand())
            {
                Title = host.HostName,
                Subtitle = host.EscapedHost,
            }).ToArray(),
        };
        return [s];
    }
}

逐段解读这段实现:

  1. GetSSHHosts() 负责读取 SSH 配置文件并用正则 ^Host\s+(?:(\S*) ?)*?\s*$ 解析出各 Host 条目后的主机名,返回 List<SSHKeychainItem>注意这是一个 async 操作(文件 IO);
  2. GetItems() 改为调用 DoGetItems()——一个异步获取主机并组装 ListSection 的方法。如果你打算在 GetItems() 中做任何异步操作,请遵循这个“同步外壳调用异步实现”的模式
  3. DoGetItems() 中拿到主机列表后,为每个主机创建带 Title/SubtitleListItem(此处 Subtitle 展示了经 JSON 转义的主机名,避免特殊字符破坏列表渲染)。

重新构建并查看效果。前提:本机必须存在 %USERPROFILE%\.ssh\config 文件,否则列表为空。成功之后,主机列表会出现在 “SSH Hosts” 分节中。

搜索某个具体主机时你会发现:过滤是 Command Palette 的内置能力。用户只需在 FilterBox 中输入,宿主就会基于模糊匹配自动过滤当前页面条目——无需任何额外代码。只有当你想自己接管过滤逻辑时,才需要改用 DynamicListPage

Step 3:为 SSH 主机实现启动命令

列表已经就绪,最后要让点击某个主机(或选中后按 Enter)真正“连接到该主机”。思路:给每个主机的 ListItem 绑定一个能够启动 Windows Terminal + SSH 会话的命令对象。

按照模板约定,在 Commands 文件夹新建 LaunchSSHHostCommand,继承 InvokableCommand

internal sealed partial class LaunchSSHHostCommand : InvokableCommand
{
    private readonly SSHKeychainItem _host;

    internal LaunchSSHHostCommand(SSHKeychainItem host)
    {
        this._host = host;
        this.Name = "Connect";
        this.Icon = new("\uE8A7");
    }

    public override CommandResult Invoke()
    {
        try
        {
            Process.Start("cmd.exe", $"/k ssh {_host.HostName}");
        }
        catch
        {
            Process.Start(new ProcessStartInfo("cmd.exe", $"/k ssh {_host.HostName}") { UseShellExecute = true });
        }

        return CommandResult.KeepOpen();
    }
}

其工作方式是:用 cmd.exe /k ssh <host> 新开一个持久的命令行会话(即 Windows Terminal 会话窗口),并把 Invoke() 的返回值设为 CommandResult.KeepOpen()(保持命令面板打开)。NameIcon(Segoe MDL2 字体的连接图标字形 \uE8A7)决定了该命令在界面上的呈现。

最后,把 SSHHostsListPage.DoGetItems() 中每个 ListItem 的命令从 NoOpCommand 换成 LaunchSSHHostCommand

    private async Task<ISection[]> DoGetItems()
    {
        List<SSHKeychainItem> items = await GetSSHHosts();
        var s = new ListSection()
        {
            Title = "SSH Hosts",
            Items = items.Select((host) => new ListItem(new LaunchSSHHostCommand(host))
            {
                Title = host.HostName,
                Subtitle = host.EscapedHost,
            }).ToArray(),
        };
        return [s];
    }

整条链路只改了一处:把条目默认命令从 NoOpCommand 换成 LaunchSSHHostCommand。至此,用户点击主机或选中后按 Enter,Windows Terminal 就会以 SSH 会话连接到该主机,SSH Keychain 扩展的三个需求全部达成。

代码仓库导航:从文档走向源码

理解本文的所有概念后,可以沿着下面这些路径在仓库中继续深入:

  • 扩展 SDK / Toolkit 基类IListPageICommandProviderICommand 的契约声明位于 Microsoft.CommandPalette.Extensions.Toolkit,其中 ListPageDynamicListPageCommandProviderInvokableCommandCommandItemListItemTagSectionCommandResult 等文件是绝大多数扩展的直接父类;
  • 扩展模板TemplateCmdPalExtension 是脚手架的权威样例,其 TemplateCmdPalExtensionCommandsProvider.cs 展示了 TopLevelCommands()DisplayNameMoreCommandsSettings 的标准用法;
  • 内置扩展范本ext 目录下几十个内置扩展(Apps、Shell、WebSearch、WindowWalker、RemoteDesktop 等)以及 SamplePagesExtension 提供了各类页面形态的真实实现,可作为 HackerNews 页、动态列表页等场景的对照参考;
  • 宿主 UI 与模糊过滤Microsoft.CmdPal.UI(渲染与交互)、Microsoft.CmdPal.UI.ViewModels(列表/页面 ViewModel)、FuzzyMatcherProvider.cs(内置模糊匹配);
  • 单元测试Tests 下按扩展划分的 UnitTests 工程(如 Microsoft.CmdPal.Ext.Shell.UnitTestsMicrosoft.CmdPal.UI.ViewModels.UnitTests)验证了命令解析、列表组装等核心行为。

据此即可开始把 “Mastodon 扩展”“SSH Keychain 扩展”替换成你自己的创意,让它们在 PowerToys Command Palette 的 Root View 中占有一席之地。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
897
5.8 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388