@muzi3/pi-ui
Pi extension: 双行仪表盘 footer + 主题化工作指示器 + 折叠 thinking 标签 spinner 动画 + Catppuccin Mocha 层次增强主题
Package details
Install @muzi3/pi-ui from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@muzi3/pi-ui- Package
@muzi3/pi-ui- Version
0.2.5- Published
- Nov 6, 2026
- Downloads
- 358/mo · 339/wk
- Author
- muzi3
- License
- MIT
- Types
- extension, theme
- Size
- 65.8 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"themes": [
"./themes"
],
"extensions": [
"./extensions/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@muzi3/pi-ui
Pi 扩展:把页脚换成双行上下文仪表盘,在编辑器边框上常驻显示模型名与耗时,并给折叠的 thinking 标签加 spinner 动画,主题跟着当前主题走。
替代 pi-catppuccin-tui 的自实现版本,修掉了原实现的两处问题:指示器颜色和多处 token 写死了 Macchiato
的十六进制色(换主题后错色),以及自定义 footer 整体替换内置 footer 后、其他扩展的 setStatus 状态
(如 @muzi3/comate-quota 的额度)变得不可见。
能力
| 能力 | 说明 |
|---|---|
footer |
双行仪表盘页脚,两条进度条置顶,替换内置 footer |
indicator |
盲文 spinner ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ @80ms(与 pi 自带 Loader 逐帧相同、accent 单色);关掉后不显示(内置指示器回退为 pi 默认) |
working |
编辑器边框上常驻显示模型名与耗时,是否在生成只由盲文 spinner 表达 |
thinking |
折叠的 thinking 块标签前加同款盲文 spinner ⠋ Thinking... @80ms(复用 indicator 的帧),只在流式期间转 |
footer 布局
Comate ████░░░░░░░░░░ 25% / ¥50.00 ~/muzi/pi-packages/packages/pi-ui main
Context ██░░░░░░░░░░░░ 1.7% / 272k ↑74.7k ↓3.8k $0.009
色:标签 muted|进度条 按占用率 绿/黄/红|占比 跟条同色|/ 窗口、/ ¥ 与未知占位 dim|
↑ mdLink 蓝、↓ syntaxNumber 桃、$ syntaxOperator 青|工作目录 dim|分支 accent(图标与分支名同色)
- 第 1 行:其他扩展通过
setStatus设置的文本(如@muzi3/comate-quota的额度条)+ 工作目录 + git 分支。 工作目录取ctx.cwd,$HOME前缀缩成~(不在 HOME 下就保留绝对路径);宽度不够时从中间 丢段、保住末段(~/muzi/…/packages/pi-ui,项目名在末尾,左截断会先砍掉最该看的字), 连末段都放不下就整段丢掉,不显示成只有省略号的~/…。 分支前是 Nerd Font 的 git 图标U+E0A0(nf-pl-branch,与 starship 默认提示符同一字形, 分支前是 Nerd Font 的 git 图标U+E0A0(nf-pl-branch,与 starship 默认提示符同一字形,accent色),需要 Nerd Font 字体,否则显示为豆腐块;分支名同色accent(图标与名字 合成一整块,在行尾立成一个锚点) - 第 2 行:上下文进度条(14 格)+
百分比 / 窗口+ 累计用量(↑in ↓out $cost)。 进度条配色按占用率分级:<70%绿(success)、70~90%黄(warning)、>90%红(error); 刚压缩完占用未知时显示为暗色? - 分色规则:语义色(绿/黄/红)只用于占用率档位,其余按“角色”取色,避免整行一片灰或一片绿。
标签
muted;占比跟进度条同色,/ 窗口降为dim;用量三块各一色(见上)。mdLink/syntaxNumber/syntaxOperator是按“颜色”选的 token:pi 的ThemeColor是闭合的 56 个语义 token,没有给 footer 留名字。Mocha 的teal因此取不到——26 色里只有它和 rosewater/flamingo/pink/base/crust 没有任何 token 指向,改主题里这些 token 的映射等于改这里的观感 - 第 1、2 行的标签列固定 8 列(
Comate/Context,含标签后的分隔空格),两条进度条因此左对齐。Context由本包补齐;扩展状态行是别的包产出的文本,要自己把标签补齐到 8 列、标签色用muted(参考@muzi3/comate-quota)——这是跨包视觉契约 - 行尾的工作目录、分支与累计用量是次要信息,放不下就整段丢弃(不截断): 第 1 行先缩路径、再丢分支、最后才丢路径;进度条与百分比不参与降级
- footer 高度不超过 2 行:没装扩展就没有额度条,不在 git 仓库就没有分支, 取不到 cwd 就没有工作目录
- 内核 footer 右侧的“模型名 • 思考等级”两项都不要:模型名在编辑器边框上常驻,思考等级由边框
颜色区分(每个等级一个
thinking*token,主题里已拉开层次) - 累计用量口径与内置 footer 一致:assistant、带 usage 的 toolResult、branch_summary/compaction 求和
编辑器边框标题
╭─ claude-sonnet-4-5 ⏱ 12s ───────────────────────────────╮ 空闲
╭─ ⠼ claude-sonnet-4-5 ⏱ 12s ─────────────────────────────╮ 流式(模型名上有高光带扫过)
│ ❯ │
╰──────────────────────────────────────────────────────────╯
- 用自定义编辑器(
PiUiBorderEditor extends CustomEditor)接管编辑器上边框。pi 自带的WorkingStatusIndicator只在流式期间挂在边框上(turn_start挂、turn_end清),空闲即消失, 承载不了“常驻”;所以子类覆写setWorkingStatusIndicator,忽略 pi 的挂/清,恒挂自己的标题。 边框拼接、截断、↑ N more滚动提示全部沿用CustomEditor.renderTopBorder,没有重写排版 - 输入被滚动时不用取舍:
↑ N more会与标题并列出现在同一行边框上 (── ⠼ model ⏱ 12s ──── ↑ 5 more ────),这是继承来的行为,不是额外实现 - 指示器:流式为
⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏@80ms,帧序列与间隔都对齐 pi 自带 Loader(@earendil-works/pi-tui的DEFAULT_FRAMES/DEFAULT_INTERVAL_MS),颜色跟内置 working 指示器一样用accent单色。 用盲文而不是▘▝▗▖/◰◳◲◱这类整格块:后者在等宽字体下几乎占满整格,在边框上比正文还抢眼; 盲文墨水轻、不挑字体。10 帧在visibleWidth下都是 1 列,边框标题不抖。 早先试过 4 帧矩形(⠁⠈⠠⠄绕四角 +dim→muted→accent→borderAccent明暗递进):帧数翻到 10 帧后 逐帧换色会变成“亮度在扫”,反而不像 spinner,所以回到单色。 不转的时候完全不显示——空闲、或关掉indicator时都不留静止帧(静止的帧反而 更难看出在不在生成);代价只是模型名左右移 1 列,在以─为主的边框上几乎看不出来 - 配色:模型名空闲时直接取编辑器当前的
borderColor(this.borderColor),边框是什么色它 就是什么色。pi 会按 thinking level 换这个函数(getThinkingBorderColor:offoverlay0、lowblue、 mediumsapphire、highmauve……),bash 模式另有getBashModeBorderColor,所以写死任何 token 都会在切换后与边框脱色(例如 medium 下边框是青色而模型名还是灰的)。 流式时模型名换成扫光帧,配色改由shimmer.ts的三档负责(dim/muted/accent+bold), 此时不再跟随borderColor;⏱ 耗时始终为dim - 耗时口径与旧版 footer 一致,只是位置变了:流式为已进行秒数(随 spinner 帧重绘跳秒),
空闲为上一轮总耗时;宽度不足时先丢掉
⏱ 耗时,再逐字截断模型名 - 退出/关闭
working会换回内置编辑器,还原 pi 默认:仅流式期间在边框显示模型名 - 需要 pi
>= 0.85(CustomEditor的embedWorkingStatus与setWorkingStatusIndicator从该版本才有); 本包不依赖@earendil-works/pi-coding-agent的本地副本,运行时由 pi 的扩展加载器别名到自身 - 圆角与
❯:覆写renderTopBorder/renderBottomBorder/render,四角换成╭╮╰╯、左右补│; 圆角只在visibleWidth(line) === width时替换,宁可没圆角也不要把宿主截断过的行改坏 ❯靠预留左内边距实现(用户内边距之外再加 3 列槽位),不接管宿主的文本编辑;输入被滚动时 不画❯(否则图标会漂到半截文字前面),极窄宽度放不下槽位时也不画。颜色取this.borderColor, 所以跟边框、跟 thinking level 同步- 模型名在流式时换成扫光帧(
shimmer.ts:dim → muted → accent+bold三档,高光带 3 列宽, 帧数 = 字符数 + 2),40ms 一帧;盲文仍是 80ms 一帧,两套相位独立,转速不变。空闲回落为纯边框色
模型名覆盖(跨扩展)
边框标题默认显示当前会话模型,其他扩展可以在自己占用会话期间临时把它换掉:
pi.events.emit("pi-ui:model-override", "glm-4.6"); // 覆盖
pi.events.emit("pi-ui:model-override", undefined); // 恢复会话模型
通道是软约定:没扩展发布时什么都不发生,空串与 undefined 都表示恢复,
session_start/session_shutdown 会清空残留值。@muzi3/compaction-custom-model
靠它在压缩期间显示压缩所用模型。
宽度不足时的降级顺序
自定义 footer 会整体接管内置 footer,内置第三行(扩展状态)随之消失,所以扩展状态置于页脚最前。 两行各自独立降级,每行都保住它最有信息量的部分:
- 第 1 行:完整(额度条 + 工作目录 + 分支)→ 路径中段省略 → 丢分支 → 丢工作目录 → 额度条截断加省略号
- 第 2 行:完整(进度条 + 计数 + 累计用量)→ 丢累计用量 → 丢
Context标签 → 丢百分比/窗口 → 只剩进度条;进度条不接受被截断
thinking spinner 动画
折叠 thinking 块的标签(hideThinkingBlock 或 Ctrl+T 收起时显示的那行)前面盲文 spinner 照转,文字上有高光带扫过:
⠋ Thinking... ← 流式中:盲文 @80ms + 文字上 40ms 扫光(复用边框指示器的帧)
✓ Thinking... ← 结束后回落的静态帧(accent 色对勾,同 pi 选择器标记当前项的那个)
帧表不在这里另写一份,直接复用 indicator.ts 的 buildSpinnerFrames 与 shimmer.ts 的
shimmerFrames:两处观感要一致,两份列表迟早漂移。节奏分两套——盲文沿用 SPINNER_INTERVAL_MS
(80ms,与边框 spinner 同速),扫光用 SHIMMER_TICK_MS(40ms,与边框标题同速)。
- pi 只提供静态的
setHiddenThinkingLabel,动画靠定时器反复改写标签实现。每次改写都会重绘 整条 transcript,所以定时器只在 assistant 消息流式期间开表(message_start开、message_end停,agent_settled兜住用户中断);流式期间 pi 本来就在逐 token 重绘,这点 额外开销看不出来,空闲时零成本 - 一轮里可能有多条 assistant 消息、中间夹着工具调用,动画跟消息粒度走而不是跟轮次走: 工具执行期间标签保持静态,不会被误看成还在思考
- 宿主那个 setter 是全局广播(一个标签打给所有行实例),而每行拿自己的 lastMessage 重绘,
所以不做拦截的话,前面已完成的思考块会跟着新一轮一起转回进行态。thinking-rows.ts 在标签落到
实例前拦一道:只有
lastMessage就是当前流式那条消息(同一个对象引用)的行拿动画帧,其余行 换成静止标签。行的身份必须用对象引用——AssistantMessage上没有timestamp之类的字段 (pi-ai/dist/types.d.ts里就只有 role/content/api/provider/model/usage/stopReason 等), 按字段比对在真实场景里恒不成立,补丁会静默退化成什么都不做。没有活动消息时补丁透传, 宿主的内部结构一旦变了也不会抛异常 - 配色:盲文沿用
accent(同边框指示器),扫光三档dim → muted → accent+bold,空闲回落 静态thinkingText。两段都自带完整着色:pi 把整串包进fg("thinkingText", label),内层 fg 的 reset 会让后面的行内文本不再跟随thinkingText,颜色塌成终端默认色 - 帧宽一致(盲文各占 1 列),标签不会左右抖;静止帧用
✓(U+2713)而不是✔️, 后者带 emoji 变体选择符、多数终端占 2 列,结束瞬间会把标签推右一格。帧是每帧现取主题色 (换主题后立刻跟上),不像边框 spinner 那样需要按主题名重建缓存 - 只在 thinking 块折叠时可见;Ctrl+T 展开思考原文时这个标签不显示,动画照旧开着(不额外
探测 pi 内部的
hideThinkingBlock,它没有对外读接口)
关闭 thinking 会调 setHiddenThinkingLabel(undefined) 还原 pi 默认的 Thinking...。
命令
/pi-ui # 查看四块能力的开关状态
/pi-ui footer|indicator|working|thinking on|off # 单独开关
关闭 footer 会恢复 pi 内置 footer;关闭 working 会换回内置编辑器(流式期间才显示模型名);关闭
indicator 会让 spinner 不再显示;关闭 thinking 会把折叠 thinking 标签还原成 pi 默认文案。
开关只在本次会话内有效,不写盘。
主题
themes/pi-ui-catppuccin.json(主题名 pi-ui-catppuccin)在 Catppuccin Mocha 基础上只改层次关系,
色板本身不变:
| token | 原值 | 现值 | 原因 |
|---|---|---|---|
toolErrorBg |
surface0 |
redBg #493446 |
原来与 toolSuccessBg 同为 surface0,成功/失败的工具块看不出区别。Catppuccin 没有暗红底色,#493446 是在 base 上叠 20% red 得到的 |
selectedBg |
surface0 |
surface1 |
原来与 userMessageBg 同色,选中行在列表里几乎不可见 |
searchMatchBg |
未定义(回落 selectedBg) |
surface2 |
搜索命中需要比选中行更强的对比 |
searchMatchText |
未定义(回落 text) |
text |
显式写出,避免跟随 selectedBg 改动漂移 |
scrollbarTrack |
未定义(回落 muted) |
surface0 |
全屏模式下滚动条轨道应当几乎不可见 |
scrollbarThumb |
未定义(回落 text) |
overlay0 |
滑块比轨道亮,但不该抢正文 |
mdCodeBlockBorder |
surface2 |
surface1 |
与主 border 同级时,代码块外框噪音过大 |
mdQuoteBorder |
surface2 |
surface1 |
同上,引用属于次要结构 |
mdHeading |
mauve |
lavender |
mauve 同时是 accent/mdListBullet/syntaxKeyword/thinkingHigh,标题再占用就分不出主次 |
mdListBullet |
mauve |
overlay2 |
列表符号属于次要装饰,降为灰色 |
thinkingOff |
surface1 |
overlay0 |
1.80:1 时编辑框边框(thinking off 档)几乎看不见,模型名也一致地暗;换成 overlay0 后 3.36:1 |
thinkingMinimal |
surface2 |
overlay1 |
随 thinkingOff 上移,否则 off(3.36)会比 minimal(2.46)还亮,暗端递进反转 |
thinkingXhigh |
red |
maroon |
给 thinkingMax 让出 red,形成 mauve → maroon → red 的递进 |
thinkingMax |
未定义 | red |
补上最高档,取代回落的默认色 |
色板与配色思路来自 Catppuccin(MIT)与 pi 主题 catppuccin-tui-mocha(MIT)。
安装
pi install npm:@muzi3/pi-ui
从 pi-catppuccin-tui 迁移时,先装本包、把 ~/.pi/agent/settings.json 的 theme 改成 pi-ui-catppuccin,
再卸载前者。两个包都会调用 setFooter,后加载者生效,顺序不确定;先卸载可避免页脚互相覆盖。
敏感信息
本包不含个人信息、内部工具名与任何凭证。发布前扫描见仓库根 .pi/RULES.md。