Windows Terminal Actions Page 设计规格:设置 UI 如何表达键盘快捷键与命令
本文基于仓库内的设计规格文档 [doc/specs/#6900 - Actions Page/spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/spec.md),讲解 Windows Terminal 设置编辑器中 Actions(操作)页面的设计动机、两个候选方案及其取舍结论,并结合 TerminalSettingsEditor 的实际源码,还原从"规格设计"到"MVVM 实现"的完整链路。读完后,你将理解为什么设置 UI 最终采用"Keyboard/Actions 页 + 未来 Command Palette 页"的分页策略,以及当前 Actions 页在命令列表构建、按键冲突处理、in-box 命令复制等方面的实现原理。
背景:为什么设置 UI 需要专门的 Actions 页
规格的出发点是一个明确的需求:在设置 UI 中表达 actions(可绑定的操作)。原文的 Abstract 写道:
We need to represent actions inside the settings UI. This spec goes through the possible use cases and reasoning for including specific features for actions inside the settings UI.
理想情况是设置 UI 与 JSON 配置文件(settings.json 的 actions 与 keyBindings)保持完全对等(parity),但这会带来大量设计工作量;另一个选择是放弃完全对等,用更简单的 UX 呈现。规格随后列出了 JSON 文件中全部可实现的用户故事,作为 UI 设计的对照清单:
- 为一个尚无按键绑定的 action 添加快捷键
- 编辑某个 action 的快捷键
- 从某个 action 上移除快捷键
- 为同一个 action 添加多个快捷键绑定
- 创建可迭代的 action(iterable action)
- 创建嵌套 action(nested action)
- 选择哪些 action 出现在命令面板(command palette)中
- 查看所有可能的 action,无论其是否分配了按键
规格还特别列出了带属性(properties)的命令,这些属性是 UI 设计时无法回避的复杂度来源:
| 命令 | 属性 |
|---|---|
sendInput |
input |
closeOtherTabs |
index |
closeTabsAfter |
index |
renameTab |
title* |
setTabColor |
color* |
newWindow |
commandline、startingDirectory、tabTitle、index、profile |
splitPane |
split、commandline、startingDirectory、tabTitle、index、profile、splitMode、size |
copy |
singleLine、copyFormatting |
scrollUp |
rowsToScroll |
scrollDown |
rowsToScroll |
setColorScheme |
colorScheme |
(带 * 的属性在规格中标注为后来补充。)规格指出:上表中的大多数命令本就面向命令面板,因此给它们分配按键的意义不大。此外,规格在 Future Considerations 中预留了两种未来入口:未来下拉菜单项可触发 action(该设置需要有个存放位置),以及状态栏出现后用户可能希望从状态栏调用 action。
两个候选方案与最终结论
方案一:Keyboard 页 + (未来的)Command Palette 页
用 Keyboard 页替代 Actions 页;如果呼声足够高,未来再规划 Command Palette 页来覆盖缺少的用例。关键交互设计:
- 用户想新增快捷键绑定时,下拉框列出所有 action,无论其是否已有按键;
- 该页展示每个 action 上已分配的全部按键绑定,即使同一 action 有多组绑定也逐条列出;
- 用户如需浏览所有可能的 action,可从命令面板进入。
覆盖的用例:
- 为尚无按键的 action 添加快捷键
- 编辑某 action 的快捷键
- 移除某 action 的快捷键
- 为同一 action 添加多组快捷键
- 查看所有已分配按键的 action
未覆盖的用例:创建 iterable/nested action、选择命令面板中显示的 action、查看所有 action(含无按键者)。
优点:覆盖大多数编辑场景;给团队留出观察空间——先看用户对未覆盖用例的真实需求再决定投入。缺点:并非所有用例都能覆盖;部分命令的属性(properties)无法编辑——不过规格认为这可以接受,因为命令面板内置的默认命令本身就带着属性,例如 "decrease font size" 已内置 delta 属性。
方案二:一个 Actions 页承载一切
一个 Actions 页同时允许创建命令面板 action 与带按键的 action。覆盖全部 8 项用例,与 JSON 完全对等。优点只有"与 JSON 完全对等"一条;缺点是规格作者直言:"I could not come up with a UX design that wasn't too complicated or confusing for this scenario."——设计迅速变得臃肿,违背设置 UI 推崇易用性的初衷。
结论
规格的最终结论是:考虑过方案二,但设计很快变得杂乱(cluttered),因此团队一致决定做两个页面,先落地方案一。
UI/UX 设计:规格中的界面草图
规格附了四张界面草图,展示了 Add/Edit 两个核心交互的完整链路:
| 草图 | 说明 |
|---|---|
| [add-click.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/add-click.png) | 点击"新增"按钮后的界面状态 |
| [add-keys.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/add-keys.png) | 新增快捷键绑定时的按键录入 |
| [edit-click.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/edit-click.png) | 点击某条绑定的编辑入口 |
| [edit-keys.png](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/edit-keys.png) | 编辑现有快捷键绑定 |
其中一条设计细节值得注意:规格明确写道,"Add new" 按钮使用次要颜色(secondary color),以与 Color schemes 页上的同名按钮保持视觉对齐。
从规格到实现:Actions 页的 MVVM 架构
当前仓库中,设置编辑器的 Actions 页由 ActionsViewModel.h 定义。文件头部注释完整描述了四层视图模型的职责划分,与规格的交互设计一一对应:
- ActionsViewModel:持有"当前子页"枚举(顶层 Actions 页 / EditAction 页)、完整命令列表与当前正在编辑的命令,负责命令的增删,并监听各 CommandViewModel 的按键组合(key chord)事件;
- CommandViewModel:由
Model::Command对象构造,是列表中每一项的视图模型,包含命令名称、是否为 user command、快捷键 action 类型等高层信息,并按 action 类型创建对应的 ActionArgsViewModel; - ActionArgsViewModel / ArgWrapper:负责各参数的绑定与回写(bind back)逻辑,按参数类型区分呈现方式;
- KeyChordViewModel:由
Control::KeyChord构造,处理单条快捷键在 UI 中的录入、修改与删除。
命令列表的构建
Actions 页列表并非直接读 JSON,而是从设置模型的 ActionMap 聚合而来。在 ActionsViewModel.cpp 的 _MakeCommandVMsHelper 中可以看到核心逻辑:遍历 ActionMap().AllCommands(),对每条命令用 AllKeyBindingsForAction(cmd.ID()) 取出它的全部按键绑定(对应规格中"同一 action 多组绑定逐条列出"的要求),按显示名排序后生成 ObservableVector。这也解释了规格中"该页不 1:1 对应 JSON"的取舍在实现里的落点:列表项以"命令 + 其全部按键"为单位呈现。
值得注意的是,列表构建时会过滤掉 UnimplementedShortcutActions 中的 action——目前包括 MultipleActions(即规格中"nested action"对应的 multiple actions)与 ColorSelection。源码注释(TODO: GH 19056)说明这两类参数的 UI 绑定尚未实现,因此这些内置命令暂不出现在新操作编辑器中——这正是规格"未覆盖用例"在实现层的直接体现。
新增命令
点击页面上的 Add new 按钮触发 AddNewCommand:创建一个 Model::Command::NewUserCommand()(即 Command.cpp 中标记 OriginTag::User 的新命令),取可用 action 列表的第一项、用 ActionArgFactory::GetEmptyArgsForAction 填充空参数,随后加入 ActionMap、生成视图模型并跳转到 Edit 子页。注意这里创建出的命令可以不配任何按键——实现实际上超出了规格"潜在问题"一节中"当前设计下无法新增无按键 action"的限制,因为 Edit 子页允许直接编辑 action 类型与参数。
按键冲突的检测与确认弹窗
规格强调"编辑/删除/新增绑定"是核心用例,而多组绑定必然引出冲突问题。AttemptAddOrModifyKeyChord 展示了完整的处理链:
- 先调
ActionMap().GetActionByKeyChord(newKeys)检查新按键是否已被占用; - 若存在冲突命令,弹出 Flyout 确认对话框:显示冲突提示文本、冲突命令名称(无名命令时回退为"未命名命令"的本地化文本)、确认问题与"接受覆盖"按钮——对应资源键
Actions_RenameConflictConfirmationMessage等; - 用户接受后(或无冲突时),
applyChangesToSettingsModel先DeleteKeyBinding(oldKeys)再AddKeyBinding(newKeys, commandID),实现"改键 = 删旧 + 加新",随后收起 Flyout 并退出按键录入的编辑模式。
按键录入本身由 KeyChordListener 控件承担:它监听 KeyDown 事件并把按键组合写回 Keys 依赖属性,这正是规格草图中"点击后直接敲键"交互的实现。
in-box 命令的"写时复制"
规格只讨论用户可编辑的 JSON 命令,而当前实现要处理一个更微妙的问题:编辑内置(in-box)命令时怎么办。CommandViewModel 的源码注释给出了答案:当用户修改一条 in-box action 的参数或 action 类型时,_ReplaceCommandWithUserCopy 会调用 Command::CopyAsUserCommand 生成一份带相同 ID、但 Origin 标记为 OriginTag::User 的副本,并通过 AddCopiedCommand 写入 ActionMap。IsUserAction() 则通过 _command.Origin() == OriginTag::User 判定。从源码结构看,这是"设置 UI 与 JSON 对等"目标的一个工程化落地:内置命令保持只读,用户改动以覆盖形式落为用户自己的命令副本。
已知限制、取舍与后续演进
规格在 Potential Issues 一节如实记录了三个限制,可以作为理解当前实现边界的基线:
- 该设计与 JSON 不是 1:1:没有按键的 action 不会出现在页面上(此限制在后续实现中已被放宽——用户命令可以无按键,见上文
AddNewCommand); - 无法通过 UI 给命令指定属性(如
newTab的属性),只能走 JSON 文件。规格的判断是"这样的命令很少,且我们计划迭代出 Command Palette 页,所以可以接受"。从当前源码结构看,Edit 子页已经能通过 ArgWrapper/ActionArgsViewModel 编辑split、index等参数,属于对这一限制的持续迭代; - 嵌套(multiple)action 与 ColorSelection 暂不可编辑,对应上面提到的 UnimplementedShortcutActions 过滤清单,相关 TODO 指向问题单 GH 19056。
规格中"未来考虑"的另外两个方向(下拉菜单触发 action、状态栏调用 action)在仓库的 设置模型规格 及 Action IDs 相关规格(见 [doc/specs/#6899 - Action IDs/#6899 - Action IDs.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6899 - Action IDs/#6899 - Action IDs.md))中有延续讨论。
相关源码与文档索引
| 路径 | 说明 |
|---|---|
| [doc/specs/#6900 - Actions Page/spec.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#6900 - Actions Page/spec.md) | 本文主体设计规格(作者 Kayla Cinnamon,2021-03) |
| src/cascadia/TerminalSettingsEditor/ActionsViewModel.h | Actions 页四层视图模型定义 |
| src/cascadia/TerminalSettingsEditor/ActionsViewModel.cpp | 命令列表构建、新增命令、按键冲突处理等实现 |
| src/cascadia/TerminalSettingsEditor/Actions.xaml | Actions 页 XAML:Add new 按钮、命令列表模板 |
| src/cascadia/TerminalSettingsEditor/EditAction.xaml | Edit 子页:编辑 action 类型、名称与参数 |
| src/cascadia/TerminalSettingsEditor/KeyChordListener.h | 按键组合录入控件 |
| src/cascadia/TerminalSettingsModel/Command.cpp | NewUserCommand / CopyAsUserCommand 数据模型实现 |
适用前提:以上分析基于当前仓库源码,针对的是 Windows Terminal 设置编辑器(CascadiaSettings / TerminalSettingsEditor 组件)中的 Actions 页;规格文档写于 2021 年 3 月,实现细节(如无按键用户命令、参数编辑、写时复制)是规格结论之后的演进,文中已逐处标注出处,引用时请以源码为准。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00