Zed 主题扩展开发指南:themes JSON 结构、设计实践与源码解析
Zed 的主题系统允许通过扩展(Extension)分发一套或多套配色方案,本文以官方文档 extensions/themes.md 为骨架,系统讲解主题文件应放在扩展的哪个目录、JSON 的完整结构与每个关键字段的含义,并结合当前仓库内置主题与 extension、theme 两个 crate 的源码佐证实际加载与渲染逻辑。读完后你可以仿照仓库自带的 One / Gruvbox / Ayu 主题,独立编写、本地调试并把自定义主题发布到 Zed 的扩展商店。
主题文件放在扩展的哪个目录
按官方文档的规定,扩展中的 themes 目录应当包含一个或多个主题文件,即结构为:
my-extension/
├── extension.toml
├── languages/
├── snippets/
└── themes/
└── my-theme.json # 主题定义
该目录约定与扩展的整体规范(developing-extensions.md)一致:Zed 扩展是一个包含 extension.toml 的 Git 仓库,可以同时提供语言、调试器、主题、图标主题、代码片段与 MCP 服务器等多种能力。
从源码可以确认,themes 目录并不是运行时随意扫描的,而是在构建扩展清单(manifest)时被一次性收集。在 crates/extension/src/extension_builder.rs 中可以读到如下逻辑:
- 拼接扩展根目录下的
themes子目录; - 遍历该目录的全部条目;
- 只收集扩展名为
.json的文件,并把相对路径追加到manifest.themes列表中(若尚未存在)。
也就是说,扩展的 themes 目录下任何直接放置的 .json 文件都会被当作主题文件收录,文件名不限,只要内容是合法的主题 JSON。收集到的路径会写进扩展的 manifest,随后由主题系统加载并展示给用户选择。
每个文件需要遵循的 JSON Schema
官方文档明确:每个主题文件都应遵循 https://zed.dev/schema/themes/v0.2.0.json 对应的 JSON Schema。该 schema 通过文件顶部的 $schema 字段声明,这一写法同样出现在仓库自带的主题示例中,例如 assets/themes/one/one.json、assets/themes/gruvbox/gruvbox.json、assets/themes/ayu/ayu.json,它们的首行都是:
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
...
}
这三套主题文件是当前仓库中现成的、合法的 v0.2.0 实现样例,编写自定义主题时可以直接照抄其结构骨架再替换颜色值。
主题文件结构小结
一个 themes/*.json 文件由三个顶层部件组成(下文依次详解):
name:主题族(Theme Family)的名称;author:主题族作者的名字;themes:隶属于该主题族的一组主题(Theme)对象数组。
主题 JSON 的整体结构与 Theme Family
以仓库内 One Dark 主题的真实文件片段为例(assets/themes/one/one.json):
{
"$schema": "https://zed.dev/schema/themes/v0.2.0.json",
"name": "One",
"author": "Zed Industries",
"themes": [
{
"name": "One Dark",
"appearance": "dark",
"style": {
"background": "#3b414dff",
"text": "#dce0e5ff",
"text.accent": "#74ade8ff",
"syntax": { ... },
...
}
},
{
"name": "One Light",
"appearance": "light",
"style": { ... }
}
]
}
可以看到:
- 外层对象是一个 Theme Family(主题族),包含
name、author,以及themes数组。 themes数组里每一个元素是 Theme(具体的一套主题)。同一个文件可以声明一个族内多套主题(例如同族下同时发布 Dark 与 Light),用户仍可在设置中单独选用其中任意一套。- 每套 Theme 都包含
name(该套主题的名称)和appearance(外观模式),以及承载全部颜色配置的style对象。
appearance 的取值
appearance 只有两个合法值:"light" 与 "dark"。这一点有源码直接佐证:在 crates/theme/src/schema.rs 中,序列化反序列化使用的枚举 AppearanceContent 只有 Light 与 Dark 两个变体,并以 snake_case 序列化,即对应 JSON 中的 "light" 与 "dark"。
同时 crates/theme/src/theme.rs 也说明:appearance 决定整套主题用于亮色还是暗色界面,并影响 Zed 根据系统外观自动切换时的主题选择。用户在 Zed 设置中的 theme 与 theme_dark 字段分别指定外观模式下使用的主题。
style 对象:UI 色彩体系详解
官方文档将 style 下的属性归纳为五大类。需要注意:文档里使用的是概括性说法(如 background / foreground / accent),而实际 v2.0.0 的 JSON 采用扁平的、带命名空间的键,把一套 UI 颜色通过语义化键名精确映射到 Zed 界面中的各种表面、控件与状态。真实主题文件中存在的核心命名空间如下:
基础主色
文档中的“背景 / 前景 / 强调色”对应到真实键名大致是:
background:整体背景色。在 One Dark 中为#3b414dff;- 前景文本色:实际键名为
text(例如#dce0e5ff)及其衍生text.muted、text.placeholder、text.disabled、text.accent; - 强调/高亮色:实际键名为
text.accent或icon.accent,也可通过accents数组向系统提供一组可循环使用的强调色(见下文“accents 与多人协作用色”一节); - 图标色:
icon及icon.muted、icon.accent等,与文字色族并列。
表面与元素(surfaces / elements)
文档举例中的 element.background、border、text 等,对应真实文件里的完整键族:
- 表面层级:
surface.background、elevated_surface.background分别描述普通面板与“抬升表面”(如弹层)的背景,Zed 用background定义窗口基底、用表面色逐层构建深度; - 元素色:
element.background,并配套按交互状态区分的系列:element.hover(悬停)、element.active(按下/激活)、element.selected(选中)、element.disabled(禁用);同族还有ghost_element.*(透明/幽灵按钮在悬停、激活、选中等状态下的色值); - 边框色:
border及其状态变体border.variant、border.focused(聚焦边框)、border.selected、border.transparent、border.disabled; - 拖放目标:
drop_target.background。
面板与窗口组件
- 状态栏与标题栏:
status_bar.background、title_bar.background、title_bar.inactive_background; - 工具栏与标签栏:
toolbar.background、tab_bar.background、tab.active_background、tab.inactive_background; - 面板:
panel.background、panel.focused_border,pane.focused_border(One Dark 中这两个边框值写为null,表示不额外绘制聚焦边框); - 滚动条:
scrollbar.thumb.background、scrollbar.thumb.hover_background、scrollbar.thumb.border、scrollbar.track.background、scrollbar.track.border; - 搜索高亮:
search.match_background、search.active_match_background(One Dark 中使用了带透明度的强调色,如#74ade866)。
状态色族(status colors)
Zed 的 UI 大量使用语义化状态色表示 diff、错误与提示,键名均为 基础色 + 可选 .background/.border 后缀:
- 版本控制:
version_control.added、version_control.modified、version_control.deleted、version_control.word_added、version_control.word_deleted以及version_control.conflict_marker.ours/.theirs; - 通用状态:
created、deleted、error、warning、info、hint、modified、renamed、success、hidden、ignored、unreachable、predictive(AI 预测文本),每个状态又带.background与.border变体,用于状态徽标、面板底色与描边。
关于这些背景色的生成,crates/theme/src/fallback_themes.rs 里有一段值得留意的兜底逻辑:如果主题只自定义了某个状态色的“前景”(主体色)而没有自定义背景色,那么系统会取前景色并叠加 25% 透明度(fg_color.opacity(0.25))来生成背景色。因此编写主题时,状态背景色可以省略,Zed 会自动补齐;而希望精确控制外观时则应显式给出。
编辑器专属颜色(editor.*)
文档列出的 editor.background、editor.gutter、editor.line_number 对应真实键族:
editor.background:编辑器画布底色;editor.foreground:编辑器中的默认前景文本色;editor.gutter.background:行号槽(gutter)背景;editor.line_number/editor.active_line_number/editor.hover_line_number:普通行号、当前行行号与悬停行号颜色;editor.active_line.background:当前行高亮背景;editor.highlighted_line.background:高亮行背景;editor.invisible:空白字符(空格、Tab)等不可见字符的颜色;editor.wrap_guide/editor.active_wrap_guide:折行辅助线颜色;editor.subheader.background:编辑器内子标题背景;editor.document_highlight.read_background/write_background:同一符号的读引用/写引用高亮底色;terminal.*亦属编辑器家族,见下文终端配色节。
值得注意:这些带命名空间的键(editor.xxx)覆盖的是“通用 UI + 编辑器 chrome”,而真正的代码语法着色在 syntax 对象中定义,两者职责分开、互不混淆。
颜色值的书写规则
从仓库的样例主题可以总结出 v2.0.0 schema 中颜色值的书写习惯:
- 以
#开头、十六进制 RGB 值,常见写法为 8 位#RRGGBBAA(末尾两位是 alpha 透明度),例如 One Dark 的"background": "#3b414dff"(完全不透明)、搜索高亮"#74ade866"(66 即 40% 透明度); - 某些键允许省略 alpha,直接写 6 位
#RRGGBB,例如 One Dark 的行号"editor.line_number": "#4e5a5f"; - 值为
null表示“不特别指定/交由默认”,例如"panel.focused_border": null,以及在syntax内各 token 的"font_style": null、"font_weight": null(表示沿用默认字型与字重)。
在加载侧,crates/theme/src/schema.rs 的 try_parse_color 展示了颜色解析链路:先把颜色字符串解析为 RGBA,再转换到 HSLA(色相/饱和度/明度/透明度)色彩空间交给渲染管线使用。
syntax 对象:语法高亮配色
style.syntax 是主题的核心部分——一个以语法元素为键的对象,每个键的值描述该 token 的颜色及可选的字形属性。以 assets/themes/one/one.json 为例,一个语法项包含三个字段:
"syntax": {
"attribute": {
"color": "#74ade8ff",
"font_style": null,
"font_weight": null
},
"boolean": {
"color": "#bf956aff",
"font_style": null,
"font_weight": null
}
}
其中 color 是必填的颜色值;font_style 可填 "italic" 等斜体样式、null 表示不修饰;font_weight 可填具体字重或 null。文档中提到的关键字、字符串、注释分别对应 keyword、string、comment 键。
综合仓库内 One Dark(syntax 区块从 one.json 开始)实际声明出的全部语法键,常见可用键包括:
- 字面量/标识类:
attribute、boolean、constant、number、string、string.escape、string.regex、string.special、string.special.symbol、label、link_text、link_uri、namespace、operator、property; - 代码结构类:
keyword、function、constructor、enum、type、variable、variable.parameter、variable.special、tag、title、text.literal、embedded、preproc、primary、variant、selector、selector.pseudo; - 注释与标记类:
comment、comment.doc、punctuation及其细分punctuation.bracket、punctuation.delimiter、punctuation.list_marker、punctuation.markup、punctuation.special; - 强调类:
emphasis、emphasis.strong; - 特殊能力:
hint、predictive(AI 补全预测文本)、diff.plus、diff.minus(行内 diff 增删字符)、link_text等。
syntax 对象最终被映射为 Zed 的 SyntaxTheme 结构(见 crates/theme/src/styles/syntax.rs 对 syntax_theme::SyntaxTheme 的重导出,其实现位于 crates/syntax_theme/src/syntax_theme.rs),再叠加语言语法树(Tree-sitter)中各 token 的捕获名完成着色。
命名空间式语法键的层级原则
观察上面列表可见,语法键同样支持点分命名空间的继承与回退:例如定义了 punctuation 后,punctuation.bracket 是它的特化;未显式定义的更细 token 会回退到较宽泛的父键颜色。因此设计主题时建议先确立基础键(keyword、string、comment、function、type、constant、variable、number、punctuation、operator)的整体调性,再针对语言差异补充 .special、.escape 等特化键,使各语言渲染保持一致。
players 与 accents:多人协作用色
官方文档虽未展开,但真实 v2.0.0 主题文件中还包含两类与 Zed “multiplayer editor”基因强相关的配色:
players:一个颜色对象数组,用于多人在同一文件中协作时区分不同的参与者光标。每个元素包含cursor(光标颜色)、background、selection(该参与者的选区底色)。One Dark 在 one.json 中为 8 位参与者分别配了互不相同的色调;local()参与者(本机用户)的选区色还参与派生 UI 选中态默认色(见 crates/theme/src/fallback_themes.rs:若未显式设置element_selection_background,则取本地参与者选区色并把全透明改为 25% 透明度);accents:一个颜色数组,提供一组可循环取用的强调色(用于多光标、多选区、语法上色淡彩等需“区分数个实例”的场景)。Gruvbox 在 assets/themes/gruvbox/gruvbox.json 中给出七色列表:["#cc241dff", "#98971aff", "#d79921ff", "#458588ff", "#b16286ff", "#689d6aff", "#d65d0eff"]。如果你设计的主题没有显式提供accents,crates/theme/src/fallback_themes.rs 中同样存在基于主强调色的默认色板兜底。
终端颜色:ANSI 配色
style 下还有一套为内置终端准备的 ANSI 颜色定义,键为 terminal.background、terminal.foreground(普通前景)、terminal.bright_foreground、terminal.dim_foreground,以及 8×3 的 ANSI 基本色:
- 8 个标准色:
terminal.ansi.black、red、green、yellow、blue、magenta、cyan、white; - 每个标准色再派生亮色:
terminal.ansi.bright_black…bright_white; - 以及暗色变体:
terminal.ansi.dim_black…dim_white。
One Dark 的完整 ANSI 定义见 assets/themes/one/one.json,例如 red: #e06c75ff、bright_red: #EA858Bff、dim_red: #a7545aff。这套值决定终端中命令输出、git diff、日志等 ANSI 颜色渲染是否与你的主题协调一致;如果主题忽略它们,终端将退回到系统/默认 ANSI 配色。
设计与制作:从 Theme Builder 到本地参照
官方文档推荐使用 Zed 的 Theme Builder 网页工具来“基于某个现有主题”设计自己的主题:该工具允许你针对各 UI 表面(surfaces)微调并实时预览效果,满意后把结果导出为 JSON,再放入扩展的 themes 目录进行发布。
在没有联网可视化工具的情况下,更可靠的制作路径是以本仓库为参照:
- 打开 assets/themes/one/one.json 或 assets/themes/gruvbox/gruvbox.json,整体复制其骨架;
- 替换
background、text、各element.*、border.*与面板/编辑器系列色,先建立 UI 调性; - 依第一节的键表精简或扩展
syntax对象,逐项替换语法高亮; - 若需要内置终端观感一致,继续调整
terminal.ansi.*;若涉及多人与 AI 功能,再补players与accents; - 对照示例确认键名拼写、颜色值格式(6 位或 8 位 hex、允许
null)与appearance取值无误。
关于本地调试的另一个线索:Zed 自带的默认主题族(zed_default_themes)在 crates/theme/src/fallback_themes.rs 中以代码形式定义,包含默认暗色主题与 default_color_scales() 派生出的完整色板。当你自定义主题漏写某个键时,加载逻辑会先尝试语义推导(如用前景推导背景),再回退到这些默认值,保证任意不完整的主题也不会导致界面颜色缺失。
安装调试与发布到扩展商店
本地作为 Dev Extension 使用
在开发过程中不需要先把主题发布出去即可试用。参考 developing-extensions.md 中的说明:
- 在扩展目录写好
extension.toml与themes/*.json; - 在 Zed 的 Extensions 页面点击
Install Dev Extension(或执行zed::InstallDevExtensionaction),选择扩展根目录; - 若已安装同名已发布版本,它会被自动卸载,安装后 Extensions 页面会显示 “Overridden by dev extension”;
- 安装完成后,通过命令面板的主题选择器(Theme)即可看到该主题族下的各套主题并切换预览。
一个最小可用的 extension.toml 形如(详细字段说明见 developing-extensions.md):
id = "my-theme-extension"
name = "My theme extension"
version = "0.0.1"
schema_version = 1
authors = ["Your Name <you@example.com>"]
description = "A custom theme for Zed"
注意:纯主题扩展一般无需任何 Rust 代码,因为主题只是静态 JSON 资源,Zed 会以 crates/extension/src/extension_builder.rs 中的方式构建 manifest 并加载。
发布到扩展商店
主题验证无误后即可准备发布:维护好仓库中的 extension.toml、themes 目录与 CHANGELOG,再按 Zed 扩展商店的发布流程提交版本(Zed 的官方扩展商店管理着社区主题扩展仓库,相关说明位于仓库的 publishing 文档目录中)。用户侧则通过 Extensions 面板搜索、安装已发布的主题扩展,安装后即可在主题选择器中切换——整体体验与 Zed 内置的 One、Gruvbox、Ayu 等主题一致,因为它们在当前仓库中同样以 assets/themes 下的 v0.2.0 JSON 形式维护,走的正是本文描述的同一套结构。
小结:写一个主题的检查清单
- 文件放在扩展根目录的
themes/下,任意.json都会被 manifest 收录; - 顶层提供
$schema、name、author与themes数组,themes内每个元素有name、appearance(light/dark)和style; style中按需覆盖:background/text/text.accent、surface.*、element.*(含 hover/active/selected/disabled)、border.*、面板与滚动条、editor.*、状态色族(error/warning/info/modified/predictive等)、terminal.ansi.*、players与accents;syntax内用点分命名空间的键(keyword、string、comment、function、type、variable、punctuation.*等)精确着色,每项含color,可选font_style与font_weight;- 颜色值支持 6/8 位十六进制,允许
null委托默认值; - 不确定的键名与色值可随时对照 assets/themes/one/one.json,并使用 Theme Builder 或 Dev Extension 即时预览,最终再发布到扩展商店。
遵循以上步骤与仓库中现成样例,你就能从零产出一套结构正确、可本地预览、可上架发布的 Zed 主题扩展。
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 StartedRust0624
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