Skip to content

第 12 章 · Themes 主题定制

本章目标:理解 pi 主题的 JSON 结构与 51 个必需颜色 token,学会安装/切换主题,并从零编写一个可分享的完整自定义主题。

12.1 主题是什么、放在哪里

pi 的主题就是定义 TUI 配色的 JSON 文件。加载位置按优先级覆盖:

来源路径说明
内置darklight 两套
全局~/.pi/agent/themes/*.json所有项目可用
项目.pi/themes/*.json项目被信任后才加载
包内 themes/ 目录或 package.jsonpi.themes随包分发
设置settings.jsonthemes 数组指定文件或目录
CLI--theme <路径>(可重复)临时追加

--no-themes 可以禁用所有发现机制,只留内置主题。

12.2 切换主题的三种方式

bash
# 方式一:交互内切换(立即生效并保存)
/settings   # 在菜单中选择主题

# 方式二:写进 settings.json
{
  "theme": "my-theme"
}

# 方式三:仅本次运行生效(不改保存的设置)
pi --use-theme light

一个贴心细节——跟随终端外观自动选择:

bash
pi --use-theme light/dark
# 斜杠语法:浅色终端用 light,深色终端用 dark

首次运行时 pi 会检测终端背景色,默认选 darklight

12.3 从零做一个完整主题

bash
mkdir -p ~/.pi/agent/themes

创建 ~/.pi/agent/themes/solar-mint.json

json
{
  "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
  "name": "solar-mint",
  "vars": {
    "mint": "#2dd4a7",
    "ink": "#1b2430",
    "paper": "#f5f3ec",
    "gray": 242,
    "amber": "#d97706"
  },
  "colors": {
    "accent": "mint",
    "border": "mint",
    "borderAccent": "#14b8a6",
    "borderMuted": "gray",
    "success": "#16a34a",
    "error": "#dc2626",
    "warning": "amber",
    "muted": "gray",
    "dim": 240,
    "text": "",
    "thinkingText": "gray",
    "selectedBg": "#e7f6f0",
    "userMessageBg": "#eef2f7",
    "userMessageText": "",
    "customMessageBg": "#f0fdf9",
    "customMessageText": "",
    "customMessageLabel": "mint",
    "toolPendingBg": "#fffbeb",
    "toolSuccessBg": "#f0fdf4",
    "toolErrorBg": "#fef2f2",
    "toolTitle": "mint",
    "toolOutput": "",
    "mdHeading": "amber",
    "mdLink": "mint",
    "mdLinkUrl": "gray",
    "mdCode": "#0f766e",
    "mdCodeBlock": "",
    "mdCodeBlockBorder": "gray",
    "mdQuote": "gray",
    "mdQuoteBorder": "mint",
    "mdHr": "gray",
    "mdListBullet": "mint",
    "toolDiffAdded": "#16a34a",
    "toolDiffRemoved": "#dc2626",
    "toolDiffContext": "gray",
    "syntaxComment": "gray",
    "syntaxKeyword": "#0369a1",
    "syntaxFunction": "#7c3aed",
    "syntaxVariable": "amber",
    "syntaxString": "#15803d",
    "syntaxNumber": "#be185d",
    "syntaxType": "#0369a1",
    "syntaxOperator": "#475569",
    "syntaxPunctuation": "gray",
    "thinkingOff": "gray",
    "thinkingMinimal": "mint",
    "thinkingLow": "#0ea5e9",
    "thinkingMedium": "#06b6d4",
    "thinkingHigh": "#8b5cf6",
    "thinkingXhigh": "#d946ef",
    "bashMode": "amber"
  }
}

要点解析:

  • $schema 让编辑器获得自动补全与校验
  • vars 定义可复用颜色变量,colors 里用名字引用;
  • colors 必须定义全部 51 个必需 token;可选 token(thinkingMaxscrollbarThumbsearchMatchBg/Text)缺省时有回退值(如 scrollbarThumb 回退到 selectedBg)。

热重载

编辑当前激活的自定义主题文件后,pi 会自动重新加载,改色即时可见,非常适合调配色。

12.4 颜色值的四种格式与终端兼容性

格式示例说明
Hex"#ff0000"6 位十六进制 RGB
256 色39xterm 256 色板索引(0-255)
变量引用"primary"指向 vars 中的条目
终端默认""使用终端默认前景/背景

256 色索引计算:基础 ANSI 色 0-15;RGB 立方体 16 + 36×R + 6×G + B(R/G/B 取 0-5);232-255 是灰阶渐变。

bash
echo $COLORTERM
# 输出 truecolor 或 24bit 说明终端支持真彩

pi 使用 24-bit RGB;老终端只有 256 色时会自动降级为最近似颜色。VS Code 用户建议把 terminal.integrated.minimumContrastRatio 设为 1,避免 VS Code 私自调整你的配色。

12.5 分享主题与编程式操作

分享就是把 JSON 放进 pi 包(下一章详解):包内放 themes/ 目录,或在 package.json 里声明:

json
{
  "name": "my-themes-pack",
  "keywords": ["pi-package"],
  "pi": {
    "themes": ["./themes"]
  }
}

扩展里也可以编程式查询和切换:

typescript
export default function (pi: ExtensionAPI) {
  pi.registerCommand("theme-light", {
    description: "切到浅色主题",
    handler: async (_args, ctx) => {
      const themes = ctx.ui.getAllThemes();          // [{ name, path? }, ...]
      const light = ctx.ui.getTheme("light");        // 加载但不切换
      const result = ctx.ui.setTheme(light!);        // 或 setTheme("light") 按名切换
      if (!result.success) {
        ctx.ui.notify(`切换失败: ${result.error}`, "error");
      }
      ctx.ui.theme.fg("accent", "已切换");           // 当前主题对象可直接取色渲染
    },
  });
}

注意 RPC 模式下主题 API 会降级(getAllThemes() 返回空数组等),这是官方文档明确列出的限制。

本章小结

  • 主题 = 带 $schema 的 JSON;51 个必需 token + 少量可选 token(有回退规则);
  • 三处安装位置(全局/项目/包)+ --use-theme 单次生效 + light/dark 跟随终端语法;
  • 四种颜色格式:hex / 256 色索引 / vars 引用 / 终端默认 ""
  • 当前主题文件支持热重载,配色调试体验极佳;
  • 扩展可通过 ctx.ui.getAllThemes()/getTheme()/setTheme() 编程式管理主题。

🧪 随堂测验

点击你认为正确的选项。答错时会展示正确答案与原因解析。

1. 一个合法的 pi 自定义主题必须定义多少个必需颜色 token?

2. 正在使用的自定义主题文件被修改后会发生什么?

3. pi --use-theme light/dark 的含义是?

4. 主题 JSON 中颜色值写成 39 是什么含义?

🛠️ 动手实践

  1. 以你喜欢的配色体系(Nord/Gruvbox/Tokyo Night 等)为基础,用 vars 组织出一套完整主题并通过 /settings 启用。
  2. 故意删掉主题里的 mdHeading token,观察 pi 报什么错,再补回来验证校验逻辑。
  3. 编写一个小扩展:注册 /theme-cycle 命令,每次调用就在 getAllThemes() 列表里循环切换主题并用 notify 提示当前主题名。

完成后继续第 13 章:Pi Packages 包生态与分发