Windows Terminal 默认配置文件设计:profiles 的 defaults 与 list 结构是如何确定的
本篇围绕 Windows Terminal(仓库中的 Cascadia / 新一代终端设置模型)的设计规格文档 [#2325 - Default Profile Settings.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#2325 - Default Profile Settings.md) 展开:它记录了终端设置模型中“如何为所有配置文件(Profile)提供公共默认值”这一需求的完整设计过程——从四个候选方案(defaultSettings 全局对象、__default__ 魔法 GUID、把 profiles 改为含 defaults 与 list 的对象、inheritFrom 配置继承)的利弊权衡,到最终被采纳的方案 3,以及该方案在当前仓库源码中的真实落地方式(向后兼容的运行时类型判别、baseLayerProfile 基础层配置、IInheritable 继承树)。读完本文,你可以理解终端 JSON 设置中 profiles 对象结构的由来、四个方案被否决/采纳的具体原因,并能在自己的终端设置文件中正确使用 defaults 公共配置块。
背景:为什么需要“默认 Profile 设置”
规格文档的摘要部分点明了核心诉求:用户经常有一些希望在所有 Profile 上统一生效的公共设置(例如亚克力背景、字体、字号),而不想逐个 Profile 手工重复配置。文档作者为 Mike Griese(@zadjii-msft),创建于 2019-11-13,对应 issue 编号 #2325。
文档还交代了这个设计讨论的由来:在实现该功能的原始 PR(#3369)评审过程中,团队对“如何把这个能力暴露给用户”产生了分歧,于是先写规格、穷举方案、再做决策——这也是本文值得阅读的地方:它完整保留了四个候选方案的 JSON 形态、各自的优势与顾虑,而不只是给出结论。
方案一:全局设置中的 defaultSettings Profile 对象
第一个提案是:在根设置对象里增加一个 defaultSettings 属性,用一个“漂浮”在全局层的 Profile 对象承载所有默认值。完整示例如下:
{
"$schema": "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"defaultSettings":
{
"useAcrylic": true,
"acrylicOpacity": 0.1,
"fontFace": "Cascadia Code",
"fontSize": 10
},
"requestedTheme" : "dark",
"showTabsInTitlebar" : true,
"profiles":
[
{
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "powershell.exe",
"hidden": false
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"name": "cmd",
"commandline": "cmd.exe",
"hidden": false
}
],
"schemes": [],
"keybindings": []
}
优势
- 封装清晰:所有默认 Profile 设置集中在一个对象里,扫一眼设置文件就能定位默认值在哪里。
- 易于理解:只有一个对象作用于其后所有 Profile,语义直白。
顾虑
- 命名困难:团队对属性名反复权衡却始终没有满意答案:
defaultSettings容易与defaults.json概念混淆;defaultProfileSettings会被理解为“默认那个 Profile 的设置”;defaults同样与defaults.json冲突;baseProfileSettings不够直观;profiles.defaults、inheritedSettings、rootSettings、globalSettings、profileSettings、profilePrototype等候选也都不被看好。 - 全局层里为何多出一个“悬浮 Profile”? 用户可能会困惑:这个 Profile 对象为什么会出现在全局设置里?它难道就是默认 Profile 吗?
方案二:用户 profiles 列表中的 __default__ Profile 对象
第二个提案是:不新增全局属性,而是在 profiles 数组里放一个 GUID 为 __default__ 的特殊 Profile,它承载所有公共默认值:
{
"$schema": "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"requestedTheme" : "dark",
"showTabsInTitlebar" : true,
"profiles":
[
{
"guid": "__default__",
"useAcrylic": true,
"acrylicOpacity": 0.1,
"fontFace": "Cascadia Code",
"fontSize": 10
},
{
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "powershell.exe",
"hidden": false
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"name": "cmd",
"commandline": "cmd.exe",
"hidden": false
}
],
"schemes": [],
"keybindings": []
}
优势
- 同样把默认设置封装在一个对象里;不过因为该对象可以位于列表任意位置,清晰度略逊于方案一。
- 默认 Profile 与其他 Profile 归入同一个
profiles列表,结构上“都在一个地方”。
顾虑
- 神秘的
__default__GUID:认定该 Profile 特殊的唯一手段是给它一个常量字符串,而这个字符串并非合法 GUID,本身就值得怀疑。 - 反直觉:靠添加一个带神秘
guid的 Profile 来充当全局默认,这一机制没有任何文档之外的提示能告诉用户“加这个魔法配置块就能影响所有 Profile”。 - 一个 Profile 凭什么作用于其他所有 Profile? 列表中某一项影响其余各项,这在直觉上很难自洽。
方案三:把 profiles 改为含 list 与 defaults 的对象(最终采纳)
第三个提案改变了 profiles 的类型:从数组变为对象,对象内含 defaults(默认配置块)和 list(Profile 数组)两个键:
{
"$schema": "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"requestedTheme" : "dark",
"showTabsInTitlebar" : true,
"profiles":
{
"defaults": {
"useAcrylic": true,
"acrylicOpacity": 0.1,
"fontFace": "Cascadia Code",
"fontSize": 10
},
"list":[
{
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "powershell.exe",
"hidden": false
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"name": "cmd",
"commandline": "cmd.exe",
"hidden": false
}
]
},
"schemes": [],
"keybindings": []
}
优势
- 与 Profile 归组:默认值与 Profile 列表同处一个
profiles对象之下,语义关系一目了然。 - 向后兼容:文档特别指出,借助 Jsoncpp 可以在运行时判断
profiles是数组还是对象——是数组就回退到原有行为(必然没有defaults),是对象就进入新结构取defaults与list。用户既有设置文件因此不会被破坏。
顾虑
- Schema 变更幅度不小:
profiles从 Profile 对象列表变成了内嵌列表的对象。但如上所述可以通过运行时类型判别平滑升级,因此文档认为“并非重大问题”。 - 所有 Profile 多一层缩进:四层缩进让一些人不太舒服——这是纯粹的体验层面顾虑。
方案四:Profile 中的 inheritFrom 继承机制
第四个提案引入了通用的配置继承:给 Profile 增加 inheritFrom 属性,指向父 Profile 的 GUID,支持多层继承链:
{
"$schema": "https://aka.ms/terminal-profiles-schema",
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"requestedTheme" : "dark",
"showTabsInTitlebar" : true,
"profiles":
[
{
"guid": "{11111111-1111-1111-1111-111111111111}",
"hidden": true,
"useAcrylic": true,
"acrylicOpacity": 0.1,
"fontFace": "Cascadia Code",
"fontSize": 10
},
{
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"inheritFrom": "{11111111-1111-1111-1111-111111111111}",
"name": "Windows PowerShell",
"commandline": "powershell.exe",
"hidden": false
},
{
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"inheritFrom": "{11111111-1111-1111-1111-111111111111}",
"name": "cmd",
"commandline": "cmd.exe",
"hidden": false
},
{
"guid": "{0caa0dad-ffff-5f56-a8ff-afceeeaa6101}",
"inheritFrom": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"name": "This is another CMD",
"commandline": "cmd.exe /c myCoolScript.bat",
"hidden": false
}
],
"schemes": [],
"keybindings": []
}
优势
- 无需大改既有设置模型:只是给
Profile加一个新属性,文件结构基本不变。 - 属性名唯一:
inheritFrom相对已有键名非常独特,不易冲突。 - 表达力强:允许任意多层配置分组。用户可以把公共设置拆成多套“准默认”分组,而不被单一的“default”Profile 绑死,例如:一套给所有 WSL Profile(
startingDirectory设为~、fontFace设为 "Ubuntu Mono"),一套给所有 PowerShell Profile,等等。
顾虑
- GUID 对人不友好:
inheritFrom只能引用 GUID 才能保证唯一标识,而示例中手写的{11111111-1111-1111-1111-111111111111}尚且易读,继承自“真实”GUID 的 Profile 时(如“This is another CMD”继承自 “cmd”),其inheritFrom值一眼看上去完全不传达“cmd”的含义。 - 必须防循环引用:多层叠加时要确保继承链不成环,实现上更棘手。
- 与设置 UI 如何协同:用户在 UI 中编辑某个继承 Profile 时,改动应该只写到最上层 Profile 吗?UI 又该如何向用户传达“此 Profile 正在从其他 Profile 继承设置”?
- 心智负担更重:用户需要自己在脑中构建一棵继承树,才能理解某个 Profile 的最终设置从何而来。
结论:采纳方案三,方案四进入特性积压
规格文档给出的最终结论是:团队选择了方案 3,主要卖点为——
- 把新的“默认 Profile 设置”与其余 Profile 设置归组在一起;
- 虽然是 Schema 变更,但不是破坏性变更(运行时类型判别保证旧文件可用);
- 查看设置时容易理解两者的关系。
同时团队表示方案四的理念也不错,但对这样一个相对简单的需求而言过于“重”了,于是将其放入终端特性积压清单(issue #3818)。文档的 Resources 部分还列出了三个关联事项:原始 issue #2325(Default Profile for Common Profile Settings)、原始 PR #3369(Add support for "User Default" settings)以及 #3818(Add support for inheriting and overriding another profile's settings)。
源码印证:defaults / list 结构在当前实现中的落地
以上设计并非纸面方案,当前仓库的终端设置模型(src/cascadia/TerminalSettingsModel/)已经按方案 3 落地,并且向后兼容逻辑与文档描述一致。
关键解析入口:_parseJson 的数组/对象判别
在 CascadiaSettingsSerialization.cpp 中,两个键名常量正是文档方案 3 的产物:
static constexpr std::string_view DefaultSettingsKey{ "defaults" };
static constexpr std::string_view ProfilesListKey{ "list" };
见 CascadiaSettingsSerialization.cpp#L38-L39。而 _parseJson 的实现精确对应了文档中“如果是数组就回退到旧行为”的兼容策略:
const auto& profilesObject = _getJSONValue(root, ProfilesKey);
const auto& profileDefaults = _getJSONValue(profilesObject, DefaultSettingsKey);
const auto& profilesList = profilesObject.isArray() ? profilesObject : _getJSONValue(profilesObject, ProfilesListKey);
见 CascadiaSettingsSerialization.cpp#L1041-L1044:当 profiles 是数组(isArray() 为真)时,整个对象被直接当作 Profile 列表,defaults 取不到即为空;当它是对象时才读取 list 键。这与规格文档 Proposal 3 中 “With Jsoncpp, we can determine at runtime if an object is an array or an object” 的表述完全吻合。
defaults 被解析为“基础层 Profile”
解析出的 defaults 块并不只是简单合并,而是被构造成一个基础层 Profile:
settings.baseLayerProfile = Profile::FromJson(json.profileDefaults);
见 CascadiaSettingsSerialization.cpp#L873。也就是说,profiles.defaults 中的每个属性都走与具体 Profile 相同的反序列化路径(字体、颜色、外观等全部有效),再作为所有 Profile 的“基座”参与继承。
官方 Schema 与实际设置文件
- 官方 JSON Schema profiles.schema.json 中,
profiles被定义为同时包含list与defaults两个属性的对象(见该文件第 3258、3261 行附近),确认了方案 3 的结构已成为正式契约。 - 仓库自带的用户默认设置 userDefaults.json 本身就采用新结构:
"profiles":
{
"list":
[
{
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"name": "Windows PowerShell",
"commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"hidden": false
},
...
]
}
- 系统默认 defaults.json 中的
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"(Windows PowerShell 的 GUID)与规格文档所有示例中使用的 GUID 一致,便于对照阅读。
继承机制在模型层的一般化
defaults 块之所以能“作用于所有 Profile”,底层依赖的是设置模型中更通用的父/子继承体系。IInheritable.h 定义了让设置对象从父对象继承值的接口,其核心属性宏按“用户显式设置的值 → 继承值 → 系统默认值”的优先级回退;Profile.h 的文件头注释还直接画出了 Profile 继承树的形态(Profile 的 defaults 层在树根,各 Profile 作为其子节点)。此外 CascadiaSettingsSerialization.cpp#L1003 附近 “Merge profiles, color schemes, and globals into the user settings (aka inheritance)” 的代码段,展示了内置/动态 Profile 如何以父节点身份挂到用户 Profile 上——方案 4 所描述的“多层继承”在实现层已有基础设施,只是对用户暴露的 JSON 层面当时只采纳了方案 3 的单一 defaults 层。
实战使用要点与适用前提
结合文档结论与当前源码,实际使用上有几点值得注意:
- 在终端设置 JSON 中写公共默认值:把希望统一生效的 Profile 级属性(如
fontFace、fontSize、useAcrylic、acrylicOpacity、startingDirectory等)写入profiles.defaults对象;各具体 Profile 中显式写出的属性会覆盖默认值,未写出的属性则继承默认值。 profiles同时兼容两种形态:从实现看,写成数组(旧格式)仍被支持,此时没有默认值可用;写成{ "defaults": {...}, "list": [...] }对象(新格式)才能启用本特性。迁移时旧文件不会失效,这正对应文档中 “gracefully upgrade” 的设计承诺。defaults中不要写guid:它是配置块而非真实 Profile,示例中所有示例均未为其指定guid;defaultProfile仍应指向list中某个真实 Profile 的 GUID。- 继承的局限:当前对用户暴露的是单层“defaults → profile”继承;方案 4 讨论过的多层 Profile 间继承(
inheritFrom)在当时被明确推迟到 backlog(#3818),不要假设该能力随本文档落地。 - 适用前提:以上内容基于本仓库(Windows 新一代终端及其原始控制台宿主共存的代码库)中
src/cascadia设置模型的当前实现与doc/cascadia下的官方 Schema;defaults/list结构是终端 JSON 设置文件的契约,与 conhost(原控制台宿主)的注册表配置路径无关。
相关文档与延伸阅读
- 设计规格正文:[#2325 - Default Profile Settings.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#2325 - Default Profile Settings.md)
- 设置模型总体设计:[#885 - Terminal Settings Model.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#885 - Terminal Settings Model/#885 - Terminal Settings Model.md)
- 级联默认设置规格(
defaults.json分层的后续演进):[#754 - Cascading Default Settings.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#754 - Cascading Default Settings.md) - 外观配置对象(Profile 级外观属性的结构化扩展):[#3062 - Appearance configuration object for profiles.md](https://gitcode.com/GitHub_Trending/term/terminal/blob/20588130d8ef2ba40eb56bdae88e04cce7fc5b5d/doc/specs/?utm_source=gitcode_repo_files#3062 - Appearance configuration object for profiles.md)
- 官方设置 Schema:profiles.schema.json
- 设置模型实现:CascadiaSettingsSerialization.cpp、IInheritable.h、Profile.h
- 默认配置文件:defaults.json、userDefaults.json
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