agy-powerline 1.3.0 → 1.4.0
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/CHANGELOG.md +39 -1
- package/README.fr.md +81 -28
- package/README.ja.md +81 -28
- package/README.md +81 -28
- package/README.zh-CN.md +81 -28
- package/package.json +3 -1
- package/src/cli/configure.js +62 -28
- package/src/index.js +2 -1
- package/src/lib/colors.js +17 -14
- package/src/lib/config.js +69 -20
- package/src/lib/git.js +14 -5
- package/src/lib/layouts/lines.js +39 -11
- package/src/lib/layouts/pills.js +72 -22
- package/src/lib/shared.js +2 -1
- package/src/lib/widget-style.js +12 -6
package/README.zh-CN.md
CHANGED
|
@@ -3,13 +3,19 @@
|
|
|
3
3
|
[English](README.md) | **简体中文** | [日本語](README.ja.md) | [Français](README.fr.md)
|
|
4
4
|
|
|
5
5
|
[](https://github.com/wassim-azi/agy-powerline/actions/workflows/ci.yml)
|
|
6
|
+
[](https://github.com/wassim-azi/agy-powerline/actions/workflows/codeql.yml)
|
|
6
7
|
[](https://www.npmjs.com/package/agy-powerline)
|
|
7
|
-
[](https://www.npmjs.com/package/agy-powerline)
|
|
9
|
+
[](https://github.com/wassim-azi/agy-powerline/stargazers)
|
|
10
|
+
[](https://agy-powerline.com/configure/)
|
|
8
11
|
[](https://nodejs.org/)
|
|
9
|
-
[](docs/install.md)
|
|
13
|
+
[](docs/install.md)
|
|
14
|
+
[](docs/install.md)
|
|
10
15
|
[](https://opensource.org/licenses/MIT)
|
|
11
16
|
[](#隐私与安全)
|
|
12
17
|
[](https://scorecard.dev/viewer/?uri=github.com/wassim-azi/agy-powerline)
|
|
18
|
+
[](https://www.bestpractices.dev/projects/15655)
|
|
13
19
|
[](https://github.com/Piebald-AI/awesome-gemini-cli)
|
|
14
20
|
|
|
15
21
|
> 本文译自英文版 [README](README.md),可能落后于英文版;如有出入,以英文版为准。详细文档([`docs/`](docs/))仅提供英文版。
|
|
@@ -19,7 +25,7 @@
|
|
|
19
25
|
一眼看到当前模型、Gemini 5h/7d、Claude/GPT 和 Argon 配额、上下文窗口、git 分支等信息。在交互式编辑器中挑选组件、主题和布局。一条命令即可安装,零依赖。
|
|
20
26
|
|
|
21
27
|
<p align="center">
|
|
22
|
-
<img src="assets/
|
|
28
|
+
<img src="assets/demo.gif" alt="agy-powerline 的 configure:输入 argon 添加 Argon 每周配额,然后浏览预设、主题和布局并实时预览状态栏,最后打开 Powerline 标签页" width="850">
|
|
23
29
|
</p>
|
|
24
30
|
|
|
25
31
|
## 安装
|
|
@@ -34,6 +40,15 @@ agy # 启动 Antigravity
|
|
|
34
40
|
|
|
35
41
|
`init` 在替换任何内容之前都会先询问,并会先备份 Antigravity 的 `settings.json`。运行 `init` 后需重启一次 `agy`。详情:[`init` 做了什么](docs/install.md)。
|
|
36
42
|
|
|
43
|
+
<details>
|
|
44
|
+
<summary>截图:真实 agy 会话中的状态栏(Windows Terminal)</summary>
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="assets/screenshots/windows.png" alt="Windows Terminal 中 agy 输入框下方的 agy-powerline:上方是一段简短对话,下方是三行的 Pills 状态栏" width="850">
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
</details>
|
|
51
|
+
|
|
37
52
|
也可以让 agy 帮你安装:把下面这段话粘贴到 `agy` 会话中。
|
|
38
53
|
|
|
39
54
|
```text
|
|
@@ -54,13 +69,15 @@ Install the agy-powerline statusline: run `npx agy-powerline init --yes`, then t
|
|
|
54
69
|
|
|
55
70
|
从一个预设开始,再按需调整:
|
|
56
71
|
|
|
57
|
-
| 预设
|
|
58
|
-
|
|
|
59
|
-
| `default`
|
|
60
|
-
| `compact`
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
72
|
+
| 预设 | 显示内容 |
|
|
73
|
+
| --------- | ---------------------------------------------------------------------- |
|
|
74
|
+
| `default` | 模型、上下文、git;第二行显示 5h 和 7d 配额及重置时间 |
|
|
75
|
+
| `compact` | 单行:简短模型名、最紧张的配额、上下文 %、git |
|
|
76
|
+
| `agent` | 代理状态、子代理、标题、模式、沙盒;模型、上下文 %、产物、最紧张的配额 |
|
|
77
|
+
| `git` | 项目、分支、worktree、stash;文件数、改动行数、提交 |
|
|
78
|
+
| `tokens` | 上下文 token、会话输入/输出 token、缓存;今天和 7 天的 token |
|
|
79
|
+
| `quotas` | 套餐和模型;5h 和 7d 配额并排显示,第三方和 Argon 配额在第三行 |
|
|
80
|
+
| `full` | 全部:会话、项目和 git;上下文、token 和缓存;两组配额 |
|
|
64
81
|
|
|
65
82
|
### 主题
|
|
66
83
|
|
|
@@ -78,16 +95,48 @@ Install the agy-powerline statusline: run `npx agy-powerline init --yes`, then t
|
|
|
78
95
|
**Pills**(默认):powerline 风格的胶囊。可选圆角、尖角、斜角或平直形状,也可以在行与行之间留一个空行。
|
|
79
96
|
|
|
80
97
|
<p align="center">
|
|
81
|
-
<img src="assets/screenshots/pills-
|
|
98
|
+
<img src="assets/screenshots/pills-dark.png" alt="agy-powerline 的 Pills 布局,dark-aurora 主题:模型、上下文、项目和 git;Gemini 5h 和 7d 配额,带进度标记和重置时间;Claude/GPT 和 Argon 每周配额" width="850">
|
|
82
99
|
</p>
|
|
83
100
|
|
|
101
|
+
<details>
|
|
102
|
+
<summary>浅色终端</summary>
|
|
103
|
+
|
|
104
|
+
<p align="center">
|
|
105
|
+
<img src="assets/screenshots/pills-light.png" alt="agy-powerline 的 Pills 布局,浅色终端上的 light-paper 主题" width="850">
|
|
106
|
+
</p>
|
|
107
|
+
|
|
108
|
+
</details>
|
|
109
|
+
|
|
84
110
|
**Lines**:以纯彩色文本显示相同的信息。
|
|
85
111
|
|
|
86
112
|
<p align="center">
|
|
87
|
-
<img src="assets/screenshots/lines-dark.png" alt="agy-powerline 的 Lines 布局,深色终端上的 dark-
|
|
88
|
-
|
|
113
|
+
<img src="assets/screenshots/lines-dark.png" alt="agy-powerline 的 Lines 布局,深色终端上的 dark-aurora 主题" width="850">
|
|
114
|
+
</p>
|
|
115
|
+
|
|
116
|
+
<details>
|
|
117
|
+
<summary>浅色终端</summary>
|
|
118
|
+
|
|
119
|
+
<p align="center">
|
|
120
|
+
<img src="assets/screenshots/lines-light.png" alt="agy-powerline 的 Lines 布局,浅色终端上的 light-paper 主题" width="850">
|
|
89
121
|
</p>
|
|
90
122
|
|
|
123
|
+
</details>
|
|
124
|
+
|
|
125
|
+
**Classic**:用 `|` 分隔的纯彩色文本,进度条为方块,例如 `███░│░░░░░`。
|
|
126
|
+
|
|
127
|
+
<p align="center">
|
|
128
|
+
<img src="assets/screenshots/classic-dark.png" alt="agy-powerline 的 Classic 布局,深色终端上的 dark-aurora 主题,方块进度条" width="850">
|
|
129
|
+
</p>
|
|
130
|
+
|
|
131
|
+
<details>
|
|
132
|
+
<summary>浅色终端</summary>
|
|
133
|
+
|
|
134
|
+
<p align="center">
|
|
135
|
+
<img src="assets/screenshots/classic-light.png" alt="agy-powerline 的 Classic 布局,浅色终端上的 light-paper 主题" width="850">
|
|
136
|
+
</p>
|
|
137
|
+
|
|
138
|
+
</details>
|
|
139
|
+
|
|
91
140
|
## 组件
|
|
92
141
|
|
|
93
142
|
| 组件 | 显示内容 |
|
|
@@ -116,21 +165,21 @@ Install the agy-powerline statusline: run `npx agy-powerline init --yes`, then t
|
|
|
116
165
|
|
|
117
166
|
## 命令
|
|
118
167
|
|
|
119
|
-
| 命令
|
|
120
|
-
|
|
|
121
|
-
| `npx agy-powerline init [--yes]`
|
|
122
|
-
| `npx agy-powerline configure`
|
|
123
|
-
| `npx agy-powerline preset <name>`
|
|
124
|
-
| `npx agy-powerline theme <name>`
|
|
125
|
-
| `npx agy-powerline gradient <name>`
|
|
126
|
-
| `npx agy-powerline layout <pills\|lines>` | 切换布局 |
|
|
127
|
-
| `npx agy-powerline caps <style>`
|
|
128
|
-
| `npx agy-powerline export [file]`
|
|
129
|
-
| `npx agy-powerline import <file>`
|
|
130
|
-
| `npx agy-powerline doctor`
|
|
131
|
-
| `npx agy-powerline report`
|
|
132
|
-
| `npx agy-powerline uninstall [--delete]`
|
|
133
|
-
| `npx agy-powerline render --demo`
|
|
168
|
+
| 命令 | 作用 |
|
|
169
|
+
| -------------------------------------------------- | ----------------------------------------------------- |
|
|
170
|
+
| `npx agy-powerline init [--yes]` | 安装或更新状态栏 |
|
|
171
|
+
| `npx agy-powerline configure` | 打开编辑器(别名 `config`) |
|
|
172
|
+
| `npx agy-powerline preset <name>` | 应用预设(`--list` 查看全部) |
|
|
173
|
+
| `npx agy-powerline theme <name>` | 切换主题(`--list`、`--gallery`) |
|
|
174
|
+
| `npx agy-powerline gradient <name>` | Color each row with a gradient (`--list`, `off`) |
|
|
175
|
+
| `npx agy-powerline layout <pills\|lines\|classic>` | 切换布局 |
|
|
176
|
+
| `npx agy-powerline caps <style>` | 切换胶囊形状:`round`、`angled`、`slanted`、`flat` |
|
|
177
|
+
| `npx agy-powerline export [file]` | Save your config to a file, to back it up or share it |
|
|
178
|
+
| `npx agy-powerline import <file>` | Load a config, after showing what changes (`--merge`) |
|
|
179
|
+
| `npx agy-powerline doctor` | 检查安装并说明问题所在 |
|
|
180
|
+
| `npx agy-powerline report` | 生成保护隐私的错误报告 |
|
|
181
|
+
| `npx agy-powerline uninstall [--delete]` | 从 agy 中移除(`--delete` 会同时删除其文件) |
|
|
182
|
+
| `npx agy-powerline render --demo` | 用示例数据打印状态栏 |
|
|
134
183
|
|
|
135
184
|
任何命令加上 `--help` 即可查看其选项。
|
|
136
185
|
|
|
@@ -185,6 +234,10 @@ npx agy-powerline doctor # 检查一切是否为最新
|
|
|
185
234
|
|
|
186
235
|
如果 agy-powerline 对你有用,点个 ⭐ 能帮助更多 agy 用户发现它。
|
|
187
236
|
|
|
237
|
+
## Star History
|
|
238
|
+
|
|
239
|
+
[](https://star-history.com/#wassim-azi/agy-powerline&Date)
|
|
240
|
+
|
|
188
241
|
## 许可证
|
|
189
242
|
|
|
190
243
|
[MIT](LICENSE) © Wassim AZI
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agy-powerline",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Fast, configurable statusline for Google Antigravity CLI (agy): live Gemini, Claude/GPT and Argon quota pacing, git, and an interactive TUI configurator",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agy-powerline": "src/index.js"
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"test:update": "node tests/update-snapshots.js",
|
|
36
36
|
"test:coverage": "node --test --experimental-test-coverage tests/shared.js",
|
|
37
37
|
"gallery": "node scripts/gallery/build.js",
|
|
38
|
+
"screenshots": "node scripts/gallery/screenshots.js",
|
|
38
39
|
"configurator": "node scripts/configurator/build.js",
|
|
39
40
|
"lint": "eslint .",
|
|
40
41
|
"lint:fix": "eslint . --fix",
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
"devDependencies": {
|
|
72
73
|
"@eslint/js": "10.0.1",
|
|
73
74
|
"eslint": "10.12.0",
|
|
75
|
+
"fast-check": "4.10.2",
|
|
74
76
|
"globals": "17.13.0",
|
|
75
77
|
"prettier": "3.9.10"
|
|
76
78
|
}
|
package/src/cli/configure.js
CHANGED
|
@@ -295,7 +295,15 @@ const TAB_OF_SCREEN = {
|
|
|
295
295
|
glyphs: "powerline",
|
|
296
296
|
};
|
|
297
297
|
|
|
298
|
-
const PRESET_ICONS = {
|
|
298
|
+
const PRESET_ICONS = {
|
|
299
|
+
default: "🎯",
|
|
300
|
+
compact: "⚡",
|
|
301
|
+
agent: "🧠",
|
|
302
|
+
git: "🔀",
|
|
303
|
+
tokens: "📊",
|
|
304
|
+
quotas: "🔋",
|
|
305
|
+
full: "🚀",
|
|
306
|
+
};
|
|
299
307
|
|
|
300
308
|
// Set by renderFrame so tests can draw a screen at a fixed size.
|
|
301
309
|
let columnsOverride = null;
|
|
@@ -409,6 +417,7 @@ function previewParts(width) {
|
|
|
409
417
|
// The toggle keys work on the widget list, so only that screen names them.
|
|
410
418
|
const key = (letter) => (screen === "main" ? letter : "");
|
|
411
419
|
const chips = [
|
|
420
|
+
ui.chip("gaps", preview.rowGap, key("v")),
|
|
412
421
|
ui.chip("emojis", preview.emojis, key("e")),
|
|
413
422
|
ui.chip("minimal", preview.minimalist, key("m")),
|
|
414
423
|
ui.chip("pace", preview.pacing, key("r")),
|
|
@@ -416,10 +425,7 @@ function previewParts(width) {
|
|
|
416
425
|
ui.chip("ascii", preview.ascii, key("i")),
|
|
417
426
|
];
|
|
418
427
|
if (preview.layout === "pills") {
|
|
419
|
-
chips.unshift(
|
|
420
|
-
ui.chip("joins", preview.powerline.enabled, key("j")),
|
|
421
|
-
ui.chip("gaps", preview.rowGap, key("v"))
|
|
422
|
-
);
|
|
428
|
+
chips.unshift(ui.chip("joins", preview.powerline.enabled, key("j")));
|
|
423
429
|
}
|
|
424
430
|
|
|
425
431
|
return { title, bar: String(rendered).split("\n"), chips: chips.join(" ") };
|
|
@@ -1029,21 +1035,38 @@ const toHex = (rgb) => `#${rgb.map((part) => part.toString(16).padStart(2, "0"))
|
|
|
1029
1035
|
|
|
1030
1036
|
// One color as a pill in that color with the theme's pill text, the way the
|
|
1031
1037
|
// bar draws it, then its hex value.
|
|
1032
|
-
function
|
|
1033
|
-
return
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
[
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
[
|
|
1044
|
-
|
|
1038
|
+
function colorCell(label, rgb, ink) {
|
|
1039
|
+
return `${ui.pill(` ${label.padEnd(9)}`, rgb, ink)} ${dim(toHex(rgb))}`;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Widget colors on the left, states on the right; a blank line between rows
|
|
1043
|
+
// keeps the pills from touching.
|
|
1044
|
+
const PALETTE_ROWS = [
|
|
1045
|
+
[
|
|
1046
|
+
["model", "Model"],
|
|
1047
|
+
["fiveHour", "OK"],
|
|
1048
|
+
],
|
|
1049
|
+
[
|
|
1050
|
+
["context", "Context"],
|
|
1051
|
+
["caution", "Caution"],
|
|
1052
|
+
],
|
|
1053
|
+
[
|
|
1054
|
+
["git", "Git"],
|
|
1055
|
+
["warn", "Danger"],
|
|
1056
|
+
],
|
|
1057
|
+
[
|
|
1058
|
+
["weekly", "7d quota"],
|
|
1059
|
+
["meta", "Details"],
|
|
1060
|
+
],
|
|
1045
1061
|
];
|
|
1046
1062
|
|
|
1063
|
+
function paletteLines(colors) {
|
|
1064
|
+
return PALETTE_ROWS.flatMap((row, index) => [
|
|
1065
|
+
...(index ? [""] : []),
|
|
1066
|
+
{ raw: row.map(([slot, label]) => colorCell(label, colors[slot], colors.text)).join(" ") },
|
|
1067
|
+
]);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1047
1070
|
function themeDetails(id) {
|
|
1048
1071
|
if (id === "custom") {
|
|
1049
1072
|
return {
|
|
@@ -1067,9 +1090,7 @@ function themeDetails(id) {
|
|
|
1067
1090
|
...(background ? [{ raw: `${dim("For")} ${ui.tag(background)}` }] : []),
|
|
1068
1091
|
"",
|
|
1069
1092
|
heading("Palette"),
|
|
1070
|
-
...
|
|
1071
|
-
colorLine(label, theme.colors[slot], theme.colors.text)
|
|
1072
|
-
),
|
|
1093
|
+
...paletteLines(theme.colors),
|
|
1073
1094
|
"",
|
|
1074
1095
|
heading("Sample"),
|
|
1075
1096
|
...themeSample(theme.colors),
|
|
@@ -1204,7 +1225,7 @@ const POWERLINE_HELP = {
|
|
|
1204
1225
|
enabled:
|
|
1205
1226
|
"On: adjacent widgets share one pill, with a separator between them. Off: every widget gets its own pill.",
|
|
1206
1227
|
rowGap:
|
|
1207
|
-
"Puts a blank line between
|
|
1228
|
+
"Puts a blank line between rows, in every layout, so stacked pills don't touch and text rows get more air. The bar takes more lines (three rows become five).",
|
|
1208
1229
|
style: "Sets start caps, end caps and separators in one go. Enter cycles the styles.",
|
|
1209
1230
|
separators: "Glyphs between widgets of a pill. With several, they cycle per join, left to right.",
|
|
1210
1231
|
startCaps: "Glyphs that open each pill. With several, they cycle per pill, left to right.",
|
|
@@ -1233,7 +1254,7 @@ function powerlineDetails(row) {
|
|
|
1233
1254
|
function drawPowerline() {
|
|
1234
1255
|
const items = POWERLINE_ROWS.map((row) => ({ text: row.label, value: powerlineRowValue(row) }));
|
|
1235
1256
|
const notes =
|
|
1236
|
-
config.layout
|
|
1257
|
+
config.layout !== "pills"
|
|
1237
1258
|
? ["Powerline shapes apply to Pills. Switch layout with l."]
|
|
1238
1259
|
: [
|
|
1239
1260
|
"A style sets caps and separators together; edit a list to fine-tune it.",
|
|
@@ -1318,10 +1339,24 @@ function drawGlyphs() {
|
|
|
1318
1339
|
});
|
|
1319
1340
|
}
|
|
1320
1341
|
|
|
1342
|
+
// Three short paragraphs per layout, each under 90 columns so a wide panel
|
|
1343
|
+
// shows them unwrapped.
|
|
1321
1344
|
const LAYOUT_HELP = {
|
|
1322
|
-
pills:
|
|
1323
|
-
|
|
1324
|
-
"
|
|
1345
|
+
pills: [
|
|
1346
|
+
"Widgets sit in colored pills, joined by powerline caps and separators.",
|
|
1347
|
+
"Meters draw as small dots on the pill's color, with a larger dot as the pace marker.",
|
|
1348
|
+
"The Powerline tab sets the caps, separators, joins and the space between rows.",
|
|
1349
|
+
],
|
|
1350
|
+
lines: [
|
|
1351
|
+
"Colored text on the terminal background, with thin │ dividers between widgets.",
|
|
1352
|
+
"Meters draw as dots in the ok, caution or danger color, over a dim track.",
|
|
1353
|
+
"Powerline shapes don't apply; the theme, widgets and space between rows do.",
|
|
1354
|
+
],
|
|
1355
|
+
classic: [
|
|
1356
|
+
"Colored text on the terminal background, with | dividers between widgets.",
|
|
1357
|
+
"Meters draw as blocks in one color: █ filled, ░ empty and │ as the pace marker.",
|
|
1358
|
+
"Powerline shapes don't apply; the theme, widgets and space between rows do.",
|
|
1359
|
+
],
|
|
1325
1360
|
};
|
|
1326
1361
|
|
|
1327
1362
|
function drawLayout() {
|
|
@@ -1335,10 +1370,9 @@ function drawLayout() {
|
|
|
1335
1370
|
return screenFrame({
|
|
1336
1371
|
title: "Layout",
|
|
1337
1372
|
items,
|
|
1338
|
-
notes: ["Both layouts use your widgets and theme; Powerline shapes apply to Pills."],
|
|
1339
1373
|
details: hovered && {
|
|
1340
1374
|
title: hovered.name,
|
|
1341
|
-
lines: [
|
|
1375
|
+
lines: LAYOUT_HELP[hovered.id].flatMap((line, i) => (i ? ["", line] : [line])),
|
|
1342
1376
|
},
|
|
1343
1377
|
keys: [
|
|
1344
1378
|
[ui.glyph.upDown, "preview"],
|
package/src/index.js
CHANGED
|
@@ -102,7 +102,8 @@ const COMMANDS = [
|
|
|
102
102
|
{
|
|
103
103
|
name: "preset",
|
|
104
104
|
arg: "[name]",
|
|
105
|
-
description:
|
|
105
|
+
description:
|
|
106
|
+
"View or apply a widget preset (default, compact, agent, git, tokens, quotas, full)",
|
|
106
107
|
options: {
|
|
107
108
|
list: { type: "boolean", short: "l", help: "list all available presets" },
|
|
108
109
|
},
|
package/src/lib/colors.js
CHANGED
|
@@ -91,21 +91,24 @@ function rgb(r, g, b) {
|
|
|
91
91
|
return [r, g, b];
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (r > 248) {
|
|
101
|
-
return 231;
|
|
102
|
-
}
|
|
94
|
+
// The nearest xterm color: the 6x6x6 cube's levels (0, 95, 135, ... 255) are
|
|
95
|
+
// not evenly spaced, and a near-gray reads better from the gray ramp, so both
|
|
96
|
+
// candidates are measured and the closer one wins (as tmux does).
|
|
97
|
+
const CUBE_LEVELS = [0, 95, 135, 175, 215, 255];
|
|
103
98
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
99
|
+
function rgbToAnsi256([r, g, b]) {
|
|
100
|
+
const toCube = (v) => (v < 48 ? 0 : v < 115 ? 1 : Math.floor((v - 35) / 40));
|
|
101
|
+
const [cr, cg, cb] = [toCube(r), toCube(g), toCube(b)];
|
|
102
|
+
const cube = [CUBE_LEVELS[cr], CUBE_LEVELS[cg], CUBE_LEVELS[cb]];
|
|
103
|
+
|
|
104
|
+
const average = (r + g + b) / 3;
|
|
105
|
+
const grayIndex = average > 238 ? 23 : Math.max(0, Math.floor((average - 3) / 10));
|
|
106
|
+
const level = 8 + 10 * grayIndex;
|
|
107
|
+
const distance = ([x, y, z]) => (x - r) ** 2 + (y - g) ** 2 + (z - b) ** 2;
|
|
108
|
+
|
|
109
|
+
return distance([level, level, level]) < distance(cube)
|
|
110
|
+
? 232 + grayIndex
|
|
111
|
+
: 16 + 36 * cr + 6 * cg + cb;
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
function colorSeq(kind, [r, g, b]) {
|
package/src/lib/config.js
CHANGED
|
@@ -91,9 +91,14 @@ const LAYOUTS = {
|
|
|
91
91
|
name: "Lines",
|
|
92
92
|
description: "Plain colored text with dividers and dot meters",
|
|
93
93
|
},
|
|
94
|
+
classic: {
|
|
95
|
+
id: "classic",
|
|
96
|
+
name: "Classic",
|
|
97
|
+
description: "Plain colored text with | dividers and █░│ block meters",
|
|
98
|
+
},
|
|
94
99
|
};
|
|
95
100
|
|
|
96
|
-
const LAYOUT_ORDER = ["pills", "lines"];
|
|
101
|
+
const LAYOUT_ORDER = ["pills", "lines", "classic"];
|
|
97
102
|
|
|
98
103
|
function resolveLayoutId(layoutId) {
|
|
99
104
|
return LAYOUTS[layoutId] ? layoutId : "pills";
|
|
@@ -255,11 +260,12 @@ function saveConfig(config) {
|
|
|
255
260
|
}
|
|
256
261
|
|
|
257
262
|
// Presets set the widget list only; the layout (Pills or Lines) stays the one
|
|
258
|
-
// the user picked. Each keeps all its widgets at 80
|
|
263
|
+
// the user picked. Each answers one question and keeps all its widgets at 80
|
|
264
|
+
// columns (full at 100); widgets with nothing to show hide on their own.
|
|
259
265
|
const PRESETS = {
|
|
260
266
|
default: {
|
|
261
267
|
id: "default",
|
|
262
|
-
name: "
|
|
268
|
+
name: "Default",
|
|
263
269
|
description: "Model, context and git; 5h and 7d quotas with resets on a second row",
|
|
264
270
|
widgets: [
|
|
265
271
|
{ id: "model" },
|
|
@@ -277,41 +283,84 @@ const PRESETS = {
|
|
|
277
283
|
},
|
|
278
284
|
compact: {
|
|
279
285
|
id: "compact",
|
|
280
|
-
name: "
|
|
281
|
-
description: "
|
|
286
|
+
name: "Compact",
|
|
287
|
+
description: "One row: short model, tightest quota, context % and git",
|
|
282
288
|
widgets: [{ id: "model", short: true }, { id: "quota" }, { id: "contextPct" }, { id: "git" }],
|
|
283
289
|
},
|
|
284
|
-
|
|
285
|
-
id: "
|
|
286
|
-
name: "
|
|
287
|
-
description: "
|
|
290
|
+
agent: {
|
|
291
|
+
id: "agent",
|
|
292
|
+
name: "Agent activity",
|
|
293
|
+
description: "Agent state, subagents, title and mode; context, artifacts, quota",
|
|
294
|
+
widgets: [
|
|
295
|
+
{ id: "agentState" },
|
|
296
|
+
{ id: "tasks" },
|
|
297
|
+
{ id: "title" },
|
|
298
|
+
{ id: "separator" },
|
|
299
|
+
{ id: "mode" },
|
|
300
|
+
{ id: "sandbox" },
|
|
301
|
+
{ id: "lineBreak" },
|
|
302
|
+
{ id: "model", short: true },
|
|
303
|
+
{ id: "contextPct" },
|
|
304
|
+
{ id: "artifacts" },
|
|
305
|
+
{ id: "separator" },
|
|
306
|
+
{ id: "quota" },
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
git: {
|
|
310
|
+
id: "git",
|
|
311
|
+
name: "Git focus",
|
|
312
|
+
description: "Project, branch, worktree, stash; changed files and lines, commit",
|
|
288
313
|
widgets: [
|
|
289
314
|
{ id: "project" },
|
|
290
315
|
{ id: "git" },
|
|
291
|
-
{ id: "
|
|
316
|
+
{ id: "gitWorktree" },
|
|
317
|
+
{ id: "gitStash" },
|
|
292
318
|
{ id: "separator" },
|
|
293
319
|
{ id: "model", short: true },
|
|
294
320
|
{ id: "lineBreak" },
|
|
295
|
-
{ id: "
|
|
321
|
+
{ id: "gitFiles" },
|
|
322
|
+
{ id: "gitChanges" },
|
|
323
|
+
{ id: "gitSha" },
|
|
324
|
+
{ id: "separator" },
|
|
325
|
+
{ id: "contextPct" },
|
|
326
|
+
{ id: "quota" },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
tokens: {
|
|
330
|
+
id: "tokens",
|
|
331
|
+
name: "Token usage",
|
|
332
|
+
description: "Session tokens in and out, cache hit; tokens today and over 7 days",
|
|
333
|
+
widgets: [
|
|
334
|
+
{ id: "model", short: true },
|
|
335
|
+
{ id: "contextTokens" },
|
|
336
|
+
{ id: "separator" },
|
|
337
|
+
{ id: "totalTokens", split: true },
|
|
338
|
+
{ id: "cacheHit" },
|
|
339
|
+
{ id: "lineBreak" },
|
|
340
|
+
{ id: "tokensToday" },
|
|
341
|
+
{ id: "tokensWeek" },
|
|
296
342
|
{ id: "separator" },
|
|
297
343
|
{ id: "quota" },
|
|
298
|
-
{ id: "mode" },
|
|
299
344
|
],
|
|
300
345
|
},
|
|
301
|
-
|
|
302
|
-
id: "
|
|
303
|
-
name: "
|
|
304
|
-
description: "
|
|
346
|
+
quotas: {
|
|
347
|
+
id: "quotas",
|
|
348
|
+
name: "Quota watch",
|
|
349
|
+
description: "Plan and model; 5h and 7d quotas side by side; third-party and Argon",
|
|
305
350
|
widgets: [
|
|
351
|
+
{ id: "plan" },
|
|
352
|
+
{ id: "separator" },
|
|
353
|
+
{ id: "model", short: true },
|
|
354
|
+
{ id: "lineBreak" },
|
|
306
355
|
{ id: "fiveHourBar" },
|
|
307
356
|
{ id: "fiveHourReset" },
|
|
308
|
-
{ id: "
|
|
357
|
+
{ id: "separator" },
|
|
309
358
|
{ id: "weeklyBar" },
|
|
310
359
|
{ id: "weeklyReset" },
|
|
311
360
|
{ id: "lineBreak" },
|
|
312
361
|
{ id: "thirdPartyFiveHour" },
|
|
313
362
|
{ id: "thirdPartyWeekly" },
|
|
314
|
-
{ id: "
|
|
363
|
+
{ id: "separator" },
|
|
315
364
|
{ id: "argonWeekly" },
|
|
316
365
|
{ id: "argonReset" },
|
|
317
366
|
],
|
|
@@ -319,7 +368,7 @@ const PRESETS = {
|
|
|
319
368
|
full: {
|
|
320
369
|
id: "full",
|
|
321
370
|
name: "Everything",
|
|
322
|
-
description: "Session, project and git; context, tokens and cache; both quotas
|
|
371
|
+
description: "Session, project and git; context, tokens and cache; both quotas",
|
|
323
372
|
widgets: [
|
|
324
373
|
{ id: "agentState" },
|
|
325
374
|
{ id: "tasks" },
|
|
@@ -344,7 +393,7 @@ const PRESETS = {
|
|
|
344
393
|
},
|
|
345
394
|
};
|
|
346
395
|
|
|
347
|
-
const PRESET_ORDER = ["default", "compact", "
|
|
396
|
+
const PRESET_ORDER = ["default", "compact", "agent", "git", "tokens", "quotas", "full"];
|
|
348
397
|
|
|
349
398
|
function applyPreset(baseConfig, presetName) {
|
|
350
399
|
const preset = PRESETS[presetName] || PRESETS.default;
|
package/src/lib/git.js
CHANGED
|
@@ -13,6 +13,15 @@ const SYNC_CACHE_NAME = ".git-sync-cache.json";
|
|
|
13
13
|
const SYNC_CACHE_MAX = 20;
|
|
14
14
|
const COMMIT = /^[0-9a-f]{40}(?:[0-9a-f]{24})?$/;
|
|
15
15
|
|
|
16
|
+
// A git call that runs past its time is skipped until a later refresh. Tests
|
|
17
|
+
// give git more time: on a busy CI machine a plain call can take longer.
|
|
18
|
+
const timeouts = { sync: 150, detail: 300 };
|
|
19
|
+
function setGitTimeouts(next) {
|
|
20
|
+
const previous = { ...timeouts };
|
|
21
|
+
Object.assign(timeouts, next);
|
|
22
|
+
return previous;
|
|
23
|
+
}
|
|
24
|
+
|
|
16
25
|
// Only an absolute folder that exists can belong to the user's session. A
|
|
17
26
|
// relative path (a Windows path read on Linux, for one) resolves against
|
|
18
27
|
// wherever this process runs, and the upward walk would then report that
|
|
@@ -392,7 +401,7 @@ function getGitSync(gitDir, branch) {
|
|
|
392
401
|
["rev-list", "--left-right", "--count", `${branch}...${branch}@{upstream}`, "--"],
|
|
393
402
|
{
|
|
394
403
|
cwd: path.dirname(gitDir),
|
|
395
|
-
timeout:
|
|
404
|
+
timeout: timeouts.sync,
|
|
396
405
|
stdio: ["ignore", "pipe", "ignore"],
|
|
397
406
|
encoding: "utf8",
|
|
398
407
|
// Antigravity may run the statusline without a console; without this,
|
|
@@ -414,7 +423,7 @@ function getGitSync(gitDir, branch) {
|
|
|
414
423
|
// A timeout or missing git is worth knowing about when ahead/behind
|
|
415
424
|
// counts never show.
|
|
416
425
|
if (err.code === "ETIMEDOUT" || err.signal === "SIGTERM") {
|
|
417
|
-
logWarning("GIT_TIMEOUT", { timeoutMs:
|
|
426
|
+
logWarning("GIT_TIMEOUT", { timeoutMs: timeouts.sync });
|
|
418
427
|
} else if (err.code === "ENOENT") {
|
|
419
428
|
logWarning("GIT_NOT_FOUND");
|
|
420
429
|
}
|
|
@@ -437,7 +446,6 @@ const DETAIL_NEEDS = {
|
|
|
437
446
|
const STATUS_CACHE_NAME = ".git-status-cache.json";
|
|
438
447
|
const STATUS_CACHE_MAX = 20;
|
|
439
448
|
const STATUS_TTL_MS = 5000;
|
|
440
|
-
const DETAIL_TIMEOUT_MS = 300;
|
|
441
449
|
|
|
442
450
|
// No optional locks: the bar must never hold the index lock and make the
|
|
443
451
|
// user's own git commands fail. The -c flags and the submodule skip keep git
|
|
@@ -559,7 +567,7 @@ function runGitDetail(cwd, args) {
|
|
|
559
567
|
const { execFileSync } = require("child_process");
|
|
560
568
|
return execFileSync("git", args, {
|
|
561
569
|
cwd,
|
|
562
|
-
timeout:
|
|
570
|
+
timeout: timeouts.detail,
|
|
563
571
|
stdio: ["ignore", "pipe", "ignore"],
|
|
564
572
|
encoding: "utf8",
|
|
565
573
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -567,7 +575,7 @@ function runGitDetail(cwd, args) {
|
|
|
567
575
|
});
|
|
568
576
|
} catch (err) {
|
|
569
577
|
if (err.code === "ETIMEDOUT" || err.signal === "SIGTERM") {
|
|
570
|
-
logWarning("GIT_TIMEOUT", { timeoutMs:
|
|
578
|
+
logWarning("GIT_TIMEOUT", { timeoutMs: timeouts.detail });
|
|
571
579
|
} else if (err.code === "ENOENT") {
|
|
572
580
|
logWarning("GIT_NOT_FOUND");
|
|
573
581
|
}
|
|
@@ -767,4 +775,5 @@ module.exports = {
|
|
|
767
775
|
getGitDetails,
|
|
768
776
|
resolveGitDetails,
|
|
769
777
|
clearGitDetailsCache,
|
|
778
|
+
setGitTimeouts,
|
|
770
779
|
};
|