@muzi3/pi-ui 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -4
- package/extensions/border-editor.ts +8 -3
- package/extensions/footer.ts +23 -15
- package/package.json +1 -1
- package/themes/pi-ui-catppuccin.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @muzi3/pi-ui
|
|
2
2
|
|
|
3
|
-
Pi
|
|
3
|
+
Pi 扩展:把页脚换成双行上下文仪表盘,并在编辑器边框上常驻显示模型名与耗时,主题跟着当前主题走。
|
|
4
4
|
|
|
5
5
|
替代 `pi-catppuccin-tui` 的自实现版本,修掉了原实现的两处问题:指示器颜色和多处 token 写死了 Macchiato
|
|
6
6
|
的十六进制色(换主题后错色),以及自定义 footer 整体替换内置 footer 后、其他扩展的 `setStatus` 状态
|
|
@@ -10,7 +10,7 @@ Pi 扩展:把页脚换成四行上下文仪表盘,并在编辑器边框上
|
|
|
10
10
|
|
|
11
11
|
| 能力 | 说明 |
|
|
12
12
|
|------|------|
|
|
13
|
-
| `footer` |
|
|
13
|
+
| `footer` | 双行仪表盘页脚,两条进度条置顶,替换内置 footer |
|
|
14
14
|
| `indicator` | 闪烁帧 `· • ● ● • ·` @120ms,颜色取自主题 token;关掉后不显示呼吸灯(内置指示器回退为 pi 默认) |
|
|
15
15
|
| `working` | 编辑器边框上常驻显示模型名与耗时,是否在生成只由呼吸灯表达 |
|
|
16
16
|
|
|
@@ -21,14 +21,23 @@ Comate ████░░░░░░░░░░ 25% / ¥50.00 main
|
|
|
21
21
|
Context ██░░░░░░░░░░░░ 1.7% / 272k ↑74.7k ↓3.8k $0.009
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
色:标签 `muted`|进度条 按占用率 绿/黄/红|占比 跟条同色|`/ 窗口`、`/ ¥` 与未知占位 `dim`|
|
|
25
|
+
`↑` `mdLink` 蓝、`↓` `syntaxNumber` 桃、`$` `syntaxOperator` 青|分支 `accent`(图标与分支名同色)
|
|
26
|
+
|
|
24
27
|
- 第 1 行:其他扩展通过 `setStatus` 设置的文本(如 `@muzi3/comate-quota` 的额度条)+ git 分支。
|
|
25
28
|
分支前是 Nerd Font 的 git 图标 `U+E0A0`(nf-pl-branch,与 starship 默认提示符同一字形,
|
|
26
|
-
`accent` 色),**需要 Nerd Font
|
|
29
|
+
`accent` 色),**需要 Nerd Font 字体**,否则显示为豆腐块;分支名同色 `accent`(图标与名字
|
|
30
|
+
合成一整块,在行尾立成一个锚点)
|
|
27
31
|
- 第 2 行:上下文进度条(14 格)+ `百分比 / 窗口` + 累计用量(`↑in ↓out $cost`)。
|
|
28
32
|
进度条配色按占用率分级:`<70%` 绿(`success`)、`70~90%` 黄(`warning`)、`>90%` 红(`error`);
|
|
29
33
|
刚压缩完占用未知时显示为暗色 `?`
|
|
34
|
+
- 分色规则:语义色(绿/黄/红)只用于占用率档位,其余按“角色”取色,避免整行一片灰或一片绿。
|
|
35
|
+
标签 `muted`;占比跟进度条同色,`/ 窗口` 降为 `dim`;用量三块各一色(见上)。
|
|
36
|
+
`mdLink`/`syntaxNumber`/`syntaxOperator` 是按“颜色”选的 token:pi 的 `ThemeColor` 是闭合的
|
|
37
|
+
56 个语义 token,没有给 footer 留名字。Mocha 的 `teal` 因此取不到——26 色里只有它和
|
|
38
|
+
rosewater/flamingo/pink/base/crust 没有任何 token 指向,改主题里这些 token 的映射等于改这里的观感
|
|
30
39
|
- 第 1、2 行的标签列固定 8 列(`Comate ` / `Context `,含标签后的分隔空格),两条进度条因此左对齐。
|
|
31
|
-
`Context ` 由本包补齐;扩展状态行是别的包产出的文本,要自己把标签补齐到 8
|
|
40
|
+
`Context ` 由本包补齐;扩展状态行是别的包产出的文本,要自己把标签补齐到 8 列、标签色用 `muted`
|
|
32
41
|
(参考 `@muzi3/comate-quota`)——这是跨包视觉契约
|
|
33
42
|
- 行尾的分支与累计用量是次要信息,放不下就整段丢弃(不截断),进度条与百分比不参与降级
|
|
34
43
|
- footer 高度不超过 2 行:没装扩展就没有额度条,不在 git 仓库就没有分支
|
|
@@ -54,6 +63,11 @@ Context ██░░░░░░░░░░░░ 1.7% / 272k ↑74.7k ↓3.8
|
|
|
54
63
|
- 呼吸灯:流式为 `· • ● ● • ·` @120ms(颜色按主题 token 递进)。不闪的时候完全不显示——空闲、
|
|
55
64
|
或关掉 `indicator` 时都不留静止的点(静止的点反而更难看出在不在生成);代价是模型名会左右
|
|
56
65
|
移 2 列,在以 `─` 为主的边框上几乎看不出来
|
|
66
|
+
- 配色:模型名直接取编辑器**当前**的 `borderColor`(`this.borderColor`),边框是什么色它就是
|
|
67
|
+
什么色。pi 会按 thinking level 换这个函数(`getThinkingBorderColor`:off `overlay0`、low `blue`、
|
|
68
|
+
medium `sapphire`、high `mauve`……),bash 模式另有 `getBashModeBorderColor`,所以写死任何 token
|
|
69
|
+
都会在切换后与边框脱色(例如 medium 下边框是青色而模型名还是灰的);
|
|
70
|
+
`⏱ 耗时` 为 `dim`
|
|
57
71
|
- 耗时口径与旧版 footer 一致,只是位置变了:流式为已进行秒数(随呼吸灯帧重绘跳秒),
|
|
58
72
|
空闲为上一轮总耗时;宽度不足时先丢掉 `⏱ 耗时`,再逐字截断模型名
|
|
59
73
|
- 退出/关闭 `working` 会换回内置编辑器,还原 pi 默认:仅流式期间在边框显示模型名
|
|
@@ -108,6 +122,8 @@ pi.events.emit("pi-ui:model-override", undefined); // 恢复会话模型
|
|
|
108
122
|
| `mdQuoteBorder` | `surface2` | `surface1` | 同上,引用属于次要结构 |
|
|
109
123
|
| `mdHeading` | `mauve` | `lavender` | `mauve` 同时是 `accent`/`mdListBullet`/`syntaxKeyword`/`thinkingHigh`,标题再占用就分不出主次 |
|
|
110
124
|
| `mdListBullet` | `mauve` | `overlay2` | 列表符号属于次要装饰,降为灰色 |
|
|
125
|
+
| `thinkingOff` | `surface1` | `overlay0` | 1.80:1 时编辑框边框(thinking off 档)几乎看不见,模型名也一致地暗;换成 `overlay0` 后 3.36:1 |
|
|
126
|
+
| `thinkingMinimal` | `surface2` | `overlay1` | 随 `thinkingOff` 上移,否则 off(3.36)会比 minimal(2.46)还亮,暗端递进反转 |
|
|
111
127
|
| `thinkingXhigh` | `red` | `maroon` | 给 `thinkingMax` 让出 `red`,形成 `mauve → maroon → red` 的递进 |
|
|
112
128
|
| `thinkingMax` | 未定义 | `red` | 补上最高档,取代回落的默认色 |
|
|
113
129
|
|
|
@@ -39,13 +39,15 @@ type BorderTitle = {
|
|
|
39
39
|
class PiUiBorderTitle implements BorderTitle {
|
|
40
40
|
private readonly tui: TUI;
|
|
41
41
|
private readonly inputs: EditorTitleInputs;
|
|
42
|
+
private readonly getBorderColor: (text: string) => string;
|
|
42
43
|
private ticker: ReturnType<typeof setInterval> | undefined;
|
|
43
44
|
private framesKey: string | undefined;
|
|
44
45
|
private frames: string[] = [];
|
|
45
46
|
|
|
46
|
-
constructor(tui: TUI, inputs: EditorTitleInputs) {
|
|
47
|
+
constructor(tui: TUI, inputs: EditorTitleInputs, getBorderColor: (text: string) => string) {
|
|
47
48
|
this.tui = tui;
|
|
48
49
|
this.inputs = inputs;
|
|
50
|
+
this.getBorderColor = getBorderColor;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/** 状态或模型变化后调用:立即重绘,并按是否流式启停呼吸灯。 */
|
|
@@ -73,7 +75,9 @@ class PiUiBorderTitle implements BorderTitle {
|
|
|
73
75
|
// 不闪的时候不占位:留一个静止的点比没有更难看出在不在生成。
|
|
74
76
|
const parts: string[] = [];
|
|
75
77
|
if (pulse !== undefined) parts.push(pulse);
|
|
76
|
-
|
|
78
|
+
// 与编辑框边框同色:直接用编辑器当前的 borderColor,边框换成什么色(thinking
|
|
79
|
+
// level、bash 模式、换主题)标题就跟着变,不需要自己维护 token 映射。
|
|
80
|
+
if (model !== undefined) parts.push(this.getBorderColor(model));
|
|
77
81
|
const head = parts.join(" ");
|
|
78
82
|
|
|
79
83
|
const seconds = this.inputs.getTimerSeconds();
|
|
@@ -132,7 +136,8 @@ export class PiUiBorderEditor extends CustomEditor {
|
|
|
132
136
|
|
|
133
137
|
constructor(tui: EditorArgs[0], theme: EditorArgs[1], keybindings: EditorArgs[2], inputs: EditorTitleInputs) {
|
|
134
138
|
super(tui, theme, keybindings, { embedWorkingStatus: true });
|
|
135
|
-
|
|
139
|
+
// 延迟取:pi 会用 getThinkingBorderColor / getBashModeBorderColor 整体替换 borderColor。
|
|
140
|
+
this.title = new PiUiBorderTitle(tui, inputs, (text) => this.borderColor(text));
|
|
136
141
|
this.attachTitle();
|
|
137
142
|
}
|
|
138
143
|
|
package/extensions/footer.ts
CHANGED
|
@@ -10,7 +10,8 @@ const GIT_ICON = "\ue0a0";
|
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* 第 1、2 行的标签列宽(含标签后的分隔空格),两条进度条靠它左对齐:`Comate ` / `Context `。
|
|
13
|
-
* 第 1
|
|
13
|
+
* 第 1 行的标签由产出该状态的扩展自己补齐,这是跨包视觉契约;标签色同为 `muted`,
|
|
14
|
+
* 两行标签看起来必须是一套。
|
|
14
15
|
*/
|
|
15
16
|
const LABEL_COLUMN = 8;
|
|
16
17
|
|
|
@@ -109,8 +110,9 @@ export class PiUiFooter implements Component {
|
|
|
109
110
|
private renderBranch(): string {
|
|
110
111
|
const branch = this.data.getGitBranch();
|
|
111
112
|
if (!branch) return "";
|
|
112
|
-
//
|
|
113
|
-
|
|
113
|
+
// 图标与分支名同色:整块 accent 让分支在行尾立成一个锚点。名字单独压成灰色会
|
|
114
|
+
// 读成"图标是装饰、名字是正文",两种颜色反而把这一块拆散。
|
|
115
|
+
return this.theme.fg("accent", `${GIT_ICON} ${branch}`);
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
/** 第 2 行:上下文进度条 + 累计用量,标签补齐到 LABEL_COLUMN 与上一行左对齐。 */
|
|
@@ -122,14 +124,13 @@ export class PiUiFooter implements Component {
|
|
|
122
124
|
|
|
123
125
|
const color = contextColor(usage.percent);
|
|
124
126
|
const gauge = theme.fg(color, renderContextBar(usage.percent));
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
127
|
+
// 占比跟条同色(这是这一行的重点),窗口总量降到 dim——两者同色时整行只剩一块颜色,
|
|
128
|
+
// 看不出哪个数字更该看。
|
|
129
|
+
const counter =
|
|
130
|
+
theme.fg(color, usage.percent === null ? "?" : `${usage.percent.toFixed(1)}%`) +
|
|
131
|
+
theme.fg("dim", ` / ${formatTokens(usage.contextWindow)}`);
|
|
131
132
|
// 标签自带补位空格,不能再走 join(" "),否则进度条起点比第 1 行右移 2 列。
|
|
132
|
-
const labeled = `${theme.fg("
|
|
133
|
+
const labeled = `${theme.fg("muted", "Context".padEnd(LABEL_COLUMN, " "))}${gauge}`;
|
|
133
134
|
|
|
134
135
|
// 变窄时依次丢累计用量、丢标签、丢计数器;进度条与百分比是这一行的目的,不接受被截断。
|
|
135
136
|
const candidates = [
|
|
@@ -145,14 +146,21 @@ export class PiUiFooter implements Component {
|
|
|
145
146
|
return truncateToWidth(gauge, width, "");
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* 会话累计用量;本轮还没发生用量时为空串。
|
|
151
|
+
*
|
|
152
|
+
* 三块各一个色:`↑` 进用 `mdLink`(Mocha blue,冷)、`↓` 出用 `syntaxNumber`(peach,暖)、
|
|
153
|
+
* `$` 花费用 `syntaxOperator`(sky 青)。pi 的 `ThemeColor` 是闭合的 56 个语义 token,没有给
|
|
154
|
+
* footer 留名字,所以这里是按“颜色”选 token——改主题里这些 token 的映射会直接改这里观感。
|
|
155
|
+
* Mocha 的 teal 取不到:26 色里只有它和 rosewater/flamingo/pink/base/crust 没有 token 指向。
|
|
156
|
+
*/
|
|
149
157
|
private renderUsageStats(): string {
|
|
150
158
|
const totals = this.inputs.getUsageTotals();
|
|
151
159
|
const stats: string[] = [];
|
|
152
|
-
if (totals.input) stats.push(`↑${formatTokens(totals.input)}`);
|
|
153
|
-
if (totals.output) stats.push(`↓${formatTokens(totals.output)}`);
|
|
154
|
-
if (totals.cost) stats.push(`$${totals.cost.toFixed(3)}`);
|
|
155
|
-
return
|
|
160
|
+
if (totals.input) stats.push(this.theme.fg("mdLink", `↑${formatTokens(totals.input)}`));
|
|
161
|
+
if (totals.output) stats.push(this.theme.fg("syntaxNumber", `↓${formatTokens(totals.output)}`));
|
|
162
|
+
if (totals.cost) stats.push(this.theme.fg("syntaxOperator", `$${totals.cost.toFixed(3)}`));
|
|
163
|
+
return stats.join(" ");
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
/** 暗色包裹;空串原样返回,避免产生只有 ANSI 码、宽度为 0 的“隐形”内容。 */
|
package/package.json
CHANGED
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"syntaxType": "yellow",
|
|
80
80
|
"syntaxOperator": "sky",
|
|
81
81
|
"syntaxPunctuation": "overlay2",
|
|
82
|
-
"thinkingOff": "
|
|
83
|
-
"thinkingMinimal": "
|
|
82
|
+
"thinkingOff": "overlay0",
|
|
83
|
+
"thinkingMinimal": "overlay1",
|
|
84
84
|
"thinkingLow": "blue",
|
|
85
85
|
"thinkingMedium": "sapphire",
|
|
86
86
|
"thinkingHigh": "mauve",
|