WezTerm 渐变配色 API 详解:掌握wezterm.gradient_colors与wezterm.color.gradient
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
本文围绕 WezTerm 提供的 Lua APIwezterm.gradient_colors(gradient, num_colors)展开,系统讲解如何在终端配置脚本中程序化生成一组沿渐变均匀分布的 RGB 颜色,并将其用于标签页配色、随时间变化的动态主题等场景。读完本文,你将掌握该 API 的参数约定、返回类型、与window_background_gradient配置项共享的渐变规格,以及 20220807 版本之后推荐使用的wezterm.color.gradient新入口,并理解背后的 Rust 源码实现。
函数签名与版本沿革
wezterm.gradient_colors自版本20210814-124438-54e29167起提供,其签名如下:
wezterm.gradient_colors(gradient, num_colors)gradient:一个渐变规格(gradient spec),可以是任何被 window_background_gradient 配置项接受的渐变描述表。num_colors:期望返回的颜色数量(正整数)。
函数返回一个 Lua 表,表中包含恰好num_colors个颜色,这些颜色沿渐变范围均匀间隔分布。官方文档给出的典型用途有两个:为标签页(tabs)生成配色,或者在一天中的不同时刻基于渐变插值出颜色,实现"随时间变化"的炫酷效果。
自版本20220807-113146-c2fee766起,该函数正式迁移到wezterm.color.gradient,官方文档明确建议使用新名称替代旧名称:
wezterm.color.gradient(gradient, num_colors)同时,新版本返回的颜色不再是纯字符串,而是 Color 对象,可以直接调用颜色对象的各类方法(详见下文"返回类型:从字符串到 Color 对象"一节)。旧名称wezterm.gradient_colors仍然可用,只是被标记为历史入口。
快速上手:在调试覆盖层中试验
官方文档给出了一个非常直观的体验方式——打开 调试覆盖层(debug overlay)(默认快捷键Ctrl+Shift+L),在 REPL 中直接输入:
> wezterm.gradient_colors({preset="Rainbow"}, 4) ["#6e40aa", "#ff8c38", "#5dea8d", "#6e40aa"]输出结果表示:使用Rainbow预设渐变,生成 4 个均匀分布的色值。注意首尾颜色一致,说明 Rainbow 是一个闭合的色环渐变,第 1 个颜色与第 4 个颜色恰好重合。
新版用法完全一致,只是换成了wezterm.color.gradient:
> wezterm.color.gradient({preset="Rainbow"}, 4)在新版本中,返回的每个元素都是 Color 对象,因此你可以直接对它们做进一步处理,例如取出 RGB 分量、调整饱和度或计算对比度。
渐变规格:与 window_background_gradient 完全一致
gradient参数接受任何window_background_gradient允许的渐变规格。这意味着你需要了解的配置结构在 window_background_gradient 文档中有完整定义。一个典型规格如下:
local gradient = { -- 方向:"Vertical"(垂直)或 "Horizontal"(水平,默认,从左到右) -- 也支持 Linear(线性)与 Radial(径向),见下文 orientation = 'Vertical', -- 参与插值的颜色集合,接受 CSS 风格颜色: -- 命名色、rgb 字符串等均可 colors = { '#0f0c29', '#302b63', '#24243e', }, -- 也可以不指定 colors,改用预设渐变 -- preset = "Warm", -- 插值方式:"Linear"、"Basis"、"CatmullRom",默认 "Linear" interpolation = 'Linear', -- 颜色混合空间:"Rgb"、"LinearRgb"、"Hsv"、"Oklab",默认 "Rgb" blend = 'Rgb', -- 为避免水平渐变的垂直色带(banding),每像素的渐变位置会随机偏移至多 noise 值 -- 值越小(或为 0)色带越明显;默认 64 noise = 64, -- 通过 segment_size 与 segment_smoothness 调整过渡锐利程度 -- segment_size 控制分段数量 -- segment_smoothness 控制边缘硬度:0.0 为硬边,1.0 为软边 -- segment_size = 11, -- segment_smoothness = 0.0, }Linear(线性)渐变
自版本20220624-141144-bd1b7c5d起支持线性渐变,它沿一条穿过窗口中心的直线渐变,可围绕窗口中心旋转,角度以度为单位、逆时针方向为正:
0度等价于Horizontal(从左到右);90度等价于Vertical(从下到上);180度等价于Horizontal但方向为从右到左;270度等价于Vertical但方向为从上到下;- 负角度等价于顺时针方向,例如
-45等价于315度,渐变从左上角延伸到右下角。
local gradient = { colors = { '#EEBD89', '#D13ABD' }, -- 从左上角开始的线性渐变 orientation = { Linear = { angle = -45.0 } }, }Radial(径向)渐变
径向渐变基于一个假想的正圆,该圆随后被拉伸以填满窗口尺寸:
local gradient = { colors = { 'deeppink', 'gold' }, orientation = { Radial = { -- 圆心 x 坐标,范围 0.0 ~ 1.0,默认 0.5(水平居中) cx = 0.75, -- 圆心 y 坐标,范围 0.0 ~ 1.0,默认 0.5(垂直居中) cy = 0.75, -- 假想圆半径,默认 0.5(配合默认 cx/cy 时圆位于窗口正中、 -- 边缘恰好接触窗口边缘),允许大于 1 radius = 1.25, }, }, }可用预设列表
当使用preset而非手写colors时,以下预设可用(该列表与源码中的枚举一一对应):
| 预设 | 预设 | 预设 |
|---|---|---|
| Blues | Greys | RdPu |
| BrBg | Inferno | RdYlBu |
| BuGn | Magma | RdYlGn |
| BuPu | OrRd | Reds |
| Cividis | Oranges | Sinebow |
| Cool | PiYg | Spectral |
| CubeHelixDefault | Plasma | Turbo |
| GnBu | PrGn | Viridis |
| Greens | PuBu | Warm |
| PuBuGn | YlGn | |
| PuOr | YlGnBu | |
| Purples | YlOrBr | |
| Rainbow | YlOrRd |
实战:为标签页生成配色
wezterm.gradient_colors最常见的实战场景是为标签页生成一组风格统一、又彼此区分的颜色。你可以把生成的色表放进配置文件中,供UpdateTabTitle、SetTabTitle或自定义 status bar 逻辑使用。
例如,根据标签索引从渐变中取色:
local wezterm = require 'wezterm' -- 生成 8 个沿 Warm 渐变均匀分布的颜色 local tab_colors = wezterm.color.gradient({ preset = 'Warm' }, 8) -- 简单工具:根据 tab 序号取色 local function color_for_tab(index) return tab_colors[(index - 1) % #tab_colors + 1] end注意在新版本中,返回的元素是 Color 对象,其默认tostring输出就是十六进制色值字符串,因此直接拼接到 ANSI 转义序列或格式化字符串中即可。
更进一步,你还可以让标签配色"随时间漂移"——例如按当前小时数选择渐变偏移,从而在白天与夜晚呈现不同的色彩氛围,这正是官方文档所提示的"基于一天中的时间在渐变上插值颜色"的玩法。
返回类型:从字符串到 Color 对象
在20220807-113146-c2fee766版本之前,wezterm.gradient_colors返回的是十六进制颜色字符串;该版本之后,迁移后的wezterm.color.gradient返回的是 Color 对象数组。
根据 color 模块 的文档,Color 对象支持以下方法,可用于对取出的颜色做二次加工:
complement()/complement_ryb():取补色(RGB 或 RYB 色轮);triad()/square():取三色组、四色组;saturate(factor)/desaturate(factor)/saturate_fixed(amount)/desaturate_fixed(amount):按比例或固定量调整饱和度;lighten(factor)/darken(factor)/lighten_fixed(amount)/darken_fixed(amount):按比例或固定量调整明度;adjust_hue_fixed(amount)/adjust_hue_fixed_ryb(amount):固定量旋转色相;srgba_u8():获取 8 位 sRGB 分量;linear_rgba():获取线性空间 RGBA;hsla():转换为 HSLA;laba():转换为 Lab 颜色;contrast_ratio(other)/delta_e(other):与其他颜色计算对比度、色差。
一个组合示例:把生成的标签色统一调亮 20% 再使用:
local c = wezterm.color.gradient({ preset = 'Inferno' }, 5) local lighter = {} for i, color in ipairs(c) do lighter[i] = color:lighten(0.2) end源码视角:这个函数在底层做了什么
要理解wezterm.gradient_colors的完整行为,可以深入阅读其 Lua 注册与实现源码 lua-api-crates/color-funcs/src/lib.rs。该文件同时向 Lua 环境注册了两个入口:
let wezterm_mod = get_or_create_module(lua, "wezterm")?; wezterm_mod.set("gradient_colors", lua.create_function(gradient_colors)?)?; color.set("gradient", lua.create_function(gradient_colors)?)?;也就是说,wezterm.gradient_colors与wezterm.color.gradient在底层指向同一个Rust 函数,只是注册在模块树的不同位置。这解释了为什么两者行为完全一致。
核心实现如下:
fn gradient_colors<'lua>( _lua: &'lua Lua, (gradient, num_colors): (Gradient, usize), ) -> mlua::Result<Vec<ColorWrap>> { let g = gradient.build().map_err(mlua::Error::external)?; Ok(g.colors(num_colors) .into_iter() .map(|c| { let tuple = SrgbaTuple::from(c); ColorWrap(tuple.into()) }) .collect()) }从中可以确认三点:
- 参数解析:
gradient参数被反序列化为configcrate 中的Gradient结构体(config/src/background.rs),num_colors是usize类型; - 构建渐变:
gradient.build()负责根据规格构造colorgrad渐变对象; - 均匀取色:
g.colors(num_colors)返回沿渐变均匀分布的num_colors个颜色,再逐个转换为SrgbaTuple并包装为ColorWrap(即 Lua 侧的 Color 对象)返回。
Gradient 结构体与 colorgrad 的映射
Gradient结构体定义在 config/src/background.rs,字段与 Lua 侧的渐变规格一一对应:
pub struct Gradient { pub orientation: GradientOrientation, // Horizontal / Vertical / Linear / Radial pub colors: Vec<String>, // CSS 风格颜色字符串 pub preset: Option<GradientPreset>, // 预设渐变名 pub interpolation: Interpolation, // Linear / Basis / CatmullRom pub blend: BlendMode, // Rgb / LinearRgb / Hsv / Oklab pub segment_size: Option<usize>, pub segment_smoothness: Option<f64>, pub noise: Option<usize>, }Gradient::build()是核心转换逻辑:
- 若指定了
preset,则直接调用colorgrad对应的预设构造函数(如colorgrad::rainbow()、colorgrad::viridis()等); - 否则用
colorgrad::CustomGradient接收colors字符串列表,并映射blend与interpolation; - 若
segment_size与segment_smoothness同时给出,则调用g.sharp(size, smoothness)实现分段锐化过渡;若只给出其中一个,build()会直接报错并提示 "Gradient must either specify both segment_size and segment_smoothness, or neither"。
源码中的GradientPreset枚举与文档列出的 37 个预设名称一一对应,因此只要枚举中存在的名字都可以放心作为preset使用。
与 window_background_gradient 的关系小结
虽然wezterm.gradient_colors与window_background_gradient是两个不同的 API——前者是运行时 Lua 函数、返回颜色表;后者是配置项、直接生成窗口背景图——但两者共享同一套渐变规格。事实上,window_background_gradient配置项的类型就是 config/src/config.rs 中声明的window_background_gradient: Option<Gradient>,与传入wezterm.gradient_colors的是同一个结构体。
因此,你在window_background_gradient上学到的所有参数知识(orientation、colors、preset、interpolation、blend、noise、segment_size、segment_smoothness)都能 100% 复用到wezterm.gradient_colors中。你可以先在某处试验好一组渐变参数,再同时用于窗口背景与标签配色,保持整体视觉一致。
兼容性注意事项
wezterm.gradient_colors在20220807-113146-c2fee766之后仍然可用,但新代码应优先使用wezterm.color.gradient;- 旧 API 返回字符串色值,新 API 返回 Color 对象;如果你的旧配置代码把返回值当作纯字符串拼接,升级版本后注意用
tostring()或字符串插值显式转换; gradient参数缺省值遵循Gradient结构体的默认值:orientation默认Horizontal,interpolation默认Linear,blend默认Rgb;preset与colors二选一:源码中若指定了preset会直接忽略colors;若两者都未给出,CustomGradient缺少颜色会构造失败,因此至少要提供其中之一;- 该函数依赖
colorgradcrate 实现渐变插值,涉及的全部预设名均可从 config/src/background.rs 的枚举定义中核实。
掌握了这些要点,你就可以把wezterm.color.gradient灵活地嵌入自己的配置脚本,为标签页、状态栏、动态主题等场景生成风格统一的配色方案。
【免费下载链接】weztermA GPU-accelerated cross-platform terminal emulator and multiplexer written by @wez and implemented in Rust项目地址: https://gitcode.com/GitHub_Trending/we/wezterm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考