agy-powerline 1.2.0 → 1.3.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 +34 -0
- package/README.fr.md +190 -0
- package/README.ja.md +68 -26
- package/README.md +84 -29
- package/README.zh-CN.md +80 -38
- package/package.json +3 -2
- package/src/cli/caps.js +2 -0
- package/src/cli/configure.js +8 -144
- package/src/cli/doctor.js +12 -0
- package/src/cli/export.js +82 -0
- package/src/cli/gradient.js +121 -0
- package/src/cli/import.js +273 -0
- package/src/cli/init.js +186 -7
- package/src/cli/layout.js +2 -0
- package/src/cli/preset.js +2 -0
- package/src/cli/repo-url.js +7 -0
- package/src/cli/theme.js +2 -0
- package/src/cli/uninstall.js +6 -2
- package/src/index.js +51 -0
- package/src/lib/catalog.js +54 -2
- package/src/lib/colors.js +127 -0
- package/src/lib/config-check.js +74 -0
- package/src/lib/config.js +20 -1
- package/src/lib/exec.js +140 -21
- package/src/lib/fit.js +7 -0
- package/src/lib/formatters.js +67 -0
- package/src/lib/git.js +374 -2
- package/src/lib/layouts/lines.js +139 -20
- package/src/lib/layouts/pills.js +167 -18
- package/src/lib/paths.js +73 -13
- package/src/lib/prompt.js +30 -0
- package/src/lib/run.js +2 -0
- package/src/lib/session.js +17 -3
- package/src/lib/shared.js +11 -1
- package/src/lib/themes.js +79 -5
- package/src/lib/usage.js +524 -0
- package/src/lib/widget-options.js +194 -0
- package/src/lib/widget-style.js +39 -0
- package/src/runtime/statusline.js +15 -1
package/README.zh-CN.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# agy-powerline
|
|
2
2
|
|
|
3
|
-
[English](README.md) | **简体中文** | [日本語](README.ja.md)
|
|
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
6
|
[](https://www.npmjs.com/package/agy-powerline)
|
|
7
|
+
[](https://www.npmjs.com/package/agy-powerline)
|
|
7
8
|
[](https://nodejs.org/)
|
|
9
|
+
[](docs/install.md)
|
|
8
10
|
[](https://opensource.org/licenses/MIT)
|
|
9
11
|
[](#隐私与安全)
|
|
12
|
+
[](https://scorecard.dev/viewer/?uri=github.com/wassim-azi/agy-powerline)
|
|
13
|
+
[](https://github.com/Piebald-AI/awesome-gemini-cli)
|
|
10
14
|
|
|
11
15
|
> 本文译自英文版 [README](README.md),可能落后于英文版;如有出入,以英文版为准。详细文档([`docs/`](docs/))仅提供英文版。
|
|
12
16
|
|
|
@@ -30,6 +34,14 @@ agy # 启动 Antigravity
|
|
|
30
34
|
|
|
31
35
|
`init` 在替换任何内容之前都会先询问,并会先备份 Antigravity 的 `settings.json`。运行 `init` 后需重启一次 `agy`。详情:[`init` 做了什么](docs/install.md)。
|
|
32
36
|
|
|
37
|
+
也可以让 agy 帮你安装:把下面这段话粘贴到 `agy` 会话中。
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
Install the agy-powerline statusline: run `npx agy-powerline init --yes`, then tell me to restart agy.
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`--yes` 会跳过 agy 无法回答的确认提示,并在备份 `settings.json` 后替换已有的 `statusLine`。
|
|
44
|
+
|
|
33
45
|
如果终端没有 Nerd Font,可以开启 ASCII 模式(在 `configure` 中按 `i`)。
|
|
34
46
|
|
|
35
47
|
## 自定义
|
|
@@ -52,12 +64,12 @@ agy # 启动 Antigravity
|
|
|
52
64
|
|
|
53
65
|
### 主题
|
|
54
66
|
|
|
55
|
-
| 终端背景 | 主题
|
|
56
|
-
| ---------- |
|
|
57
|
-
| 深色 | `dark-aurora`(默认)、`dark-ocean`、`dark-ember`、`dark-forest`、`dark-neon`、`dark-mono`、`dark-nocturne`、`dark-onyx`、`dark-metro` |
|
|
58
|
-
| 浅色 | `light-paper`、`light-sky`、`light-sunset`、`light-cream`
|
|
59
|
-
| 色盲友好 | `colorblind-dark`、`colorblind-light`
|
|
60
|
-
| 自定义颜色 | `custom`([方法](docs/configuration.md#custom-theme))
|
|
67
|
+
| 终端背景 | 主题 |
|
|
68
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
69
|
+
| 深色 | `dark-aurora`(默认)、`dark-ocean`、`dark-ember`、`dark-forest`、`dark-neon`、`dark-mono`、`dark-nocturne`、`dark-onyx`、`dark-metro`、`dark-macchiato` |
|
|
70
|
+
| 浅色 | `light-paper`、`light-sky`、`light-sunset`、`light-cream` |
|
|
71
|
+
| 色盲友好 | `colorblind-dark`、`colorblind-light` |
|
|
72
|
+
| 自定义颜色 | `custom`([方法](docs/configuration.md#custom-theme)) |
|
|
61
73
|
|
|
62
74
|
用 `npx agy-powerline theme --gallery` 预览所有主题。
|
|
63
75
|
|
|
@@ -78,42 +90,47 @@ agy # 启动 Antigravity
|
|
|
78
90
|
|
|
79
91
|
## 组件
|
|
80
92
|
|
|
81
|
-
| 组件
|
|
82
|
-
|
|
|
83
|
-
| `model`
|
|
84
|
-
| `fiveHourBar`、`weeklyBar`
|
|
85
|
-
| `fiveHourPct`、`weeklyPct`
|
|
86
|
-
| `fiveHourReset`、`weeklyReset`
|
|
87
|
-
| `quota`
|
|
88
|
-
| `thirdPartyFiveHour`、`thirdPartyWeekly`
|
|
89
|
-
| `argonWeekly`、`argonReset`
|
|
90
|
-
| `context`、`contextPct`、`contextTokens`
|
|
91
|
-
| `git`
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
93
|
+
| 组件 | 显示内容 |
|
|
94
|
+
| ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `model` | 当前使用的模型 |
|
|
96
|
+
| `fiveHourBar`、`weeklyBar` | 以进度条显示 5h / 7d 剩余配额,带[进度标记](docs/configuration.md#quota-pace-marker) |
|
|
97
|
+
| `fiveHourPct`、`weeklyPct` | 以百分比显示 5h / 7d 剩余配额 |
|
|
98
|
+
| `fiveHourReset`、`weeklyReset` | 距配额重置的时间,或重置时刻 |
|
|
99
|
+
| `quota` | 最接近用完的配额及其重置时间 |
|
|
100
|
+
| `thirdPartyFiveHour`、`thirdPartyWeekly` | 第三方模型的配额 |
|
|
101
|
+
| `argonWeekly`、`argonReset` | Gemini 4 Argon 独立的每周配额(Google AI Pro)及其重置时间 |
|
|
102
|
+
| `context`、`contextPct`、`contextTokens` | 已用的上下文窗口:进度条、百分比或 token 数 |
|
|
103
|
+
| `git` | 分支、未提交的更改以及领先/落后的提交数 |
|
|
104
|
+
| `gitChanges`, `gitFiles`, `gitSha`, `gitStash`, `gitWorktree` | Lines added and removed, staged/modified/untracked/conflicted files, commit ID, stashes, linked worktree |
|
|
105
|
+
| `project`、`cwd` | 项目文件夹、当前文件夹 |
|
|
106
|
+
| `agentState`、`plan`、`sandbox`、`artifacts`、`version` | 代理正在做什么、你的套餐、沙箱、产物、CLI 版本 |
|
|
107
|
+
| `tasks` | 正在运行的子代理和后台任务 |
|
|
108
|
+
| `mode` | 审批模式(如 accept-edits)和 Vim 模式 |
|
|
109
|
+
| `title` | 对话标题 |
|
|
110
|
+
| `totalTokens`、`cacheHit`、`cacheRead`、`cacheCreate` | 会话 token 数和提示缓存的使用情况 |
|
|
111
|
+
| `tokensToday`、`tokensWeek` | 今天和过去 7 天的 token 用量(所有对话,Node 22.13+) |
|
|
112
|
+
| `ram` | 本机已用内存 |
|
|
113
|
+
| `customText`、`exec` | 你自己的文本,或 [shell 命令](docs/configuration.md#shell-command-widget-exec)的输出 |
|
|
100
114
|
|
|
101
115
|
没有内容可显示的组件会自动隐藏。组件可以更改颜色、去掉标签,或设置自己的告警阈值:见 [Configuration](docs/configuration.md)。
|
|
102
116
|
|
|
103
117
|
## 命令
|
|
104
118
|
|
|
105
|
-
| 命令 | 作用
|
|
106
|
-
| ----------------------------------------- |
|
|
107
|
-
| `npx agy-powerline init [--yes]` | 安装或更新状态栏
|
|
108
|
-
| `npx agy-powerline configure` | 打开编辑器(别名 `config`)
|
|
109
|
-
| `npx agy-powerline preset <name>` | 应用预设(`--list` 查看全部)
|
|
110
|
-
| `npx agy-powerline theme <name>` | 切换主题(`--list`、`--gallery`)
|
|
111
|
-
| `npx agy-powerline
|
|
112
|
-
| `npx agy-powerline
|
|
113
|
-
| `npx agy-powerline
|
|
114
|
-
| `npx agy-powerline
|
|
115
|
-
| `npx agy-powerline
|
|
116
|
-
| `npx agy-powerline
|
|
119
|
+
| 命令 | 作用 |
|
|
120
|
+
| ----------------------------------------- | ----------------------------------------------------- |
|
|
121
|
+
| `npx agy-powerline init [--yes]` | 安装或更新状态栏 |
|
|
122
|
+
| `npx agy-powerline configure` | 打开编辑器(别名 `config`) |
|
|
123
|
+
| `npx agy-powerline preset <name>` | 应用预设(`--list` 查看全部) |
|
|
124
|
+
| `npx agy-powerline theme <name>` | 切换主题(`--list`、`--gallery`) |
|
|
125
|
+
| `npx agy-powerline gradient <name>` | Color each row with a gradient (`--list`, `off`) |
|
|
126
|
+
| `npx agy-powerline layout <pills\|lines>` | 切换布局 |
|
|
127
|
+
| `npx agy-powerline caps <style>` | 切换胶囊形状:`round`、`angled`、`slanted`、`flat` |
|
|
128
|
+
| `npx agy-powerline export [file]` | Save your config to a file, to back it up or share it |
|
|
129
|
+
| `npx agy-powerline import <file>` | Load a config, after showing what changes (`--merge`) |
|
|
130
|
+
| `npx agy-powerline doctor` | 检查安装并说明问题所在 |
|
|
131
|
+
| `npx agy-powerline report` | 生成保护隐私的错误报告 |
|
|
132
|
+
| `npx agy-powerline uninstall [--delete]` | 从 agy 中移除(`--delete` 会同时删除其文件) |
|
|
133
|
+
| `npx agy-powerline render --demo` | 用示例数据打印状态栏 |
|
|
117
134
|
|
|
118
135
|
任何命令加上 `--help` 即可查看其选项。
|
|
119
136
|
|
|
@@ -143,6 +160,31 @@ npx agy-powerline doctor # 检查一切是否为最新
|
|
|
143
160
|
|
|
144
161
|
欢迎贡献。请先阅读 [CONTRIBUTING.md](CONTRIBUTING.md)(环境搭建、架构、添加组件或主题)和 [Code of Conduct](CODE_OF_CONDUCT.md)。Pull request 请提交到 `develop` 分支。在 [GitHub issues](https://github.com/wassim-azi/agy-powerline/issues) 中报告错误,或在 [Discussions](https://github.com/wassim-azi/agy-powerline/discussions) 中分享想法和反馈。
|
|
145
162
|
|
|
163
|
+
感谢所有贡献者:
|
|
164
|
+
|
|
165
|
+
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
166
|
+
<!-- prettier-ignore-start -->
|
|
167
|
+
<!-- markdownlint-disable -->
|
|
168
|
+
<table>
|
|
169
|
+
<tbody>
|
|
170
|
+
<tr>
|
|
171
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yatharth2soni"><img src="https://avatars.githubusercontent.com/u/238868599?v=4&s=160" width="80" height="80" alt="Yatharth Soni"/><br /><sub><b>Yatharth Soni</b></sub></a></td>
|
|
172
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dannyward630"><img src="https://avatars.githubusercontent.com/u/71604164?v=4&s=160" width="80" height="80" alt="Danny Ward"/><br /><sub><b>Danny Ward</b></sub></a></td>
|
|
173
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yoreva-01"><img src="https://avatars.githubusercontent.com/u/195282707?v=4&s=160" width="80" height="80" alt="Yusuf Orçan"/><br /><sub><b>Yusuf Orçan</b></sub></a></td>
|
|
174
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yuefdev"><img src="https://avatars.githubusercontent.com/u/120668197?v=4&s=160" width="80" height="80" alt="Yusuf Efe"/><br /><sub><b>Yusuf Efe</b></sub></a></td>
|
|
175
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/hiyouwhatsapp"><img src="https://avatars.githubusercontent.com/u/89997383?v=4&s=160" width="80" height="80" alt="hiyouwhatsapp"/><br /><sub><b>hiyouwhatsapp</b></sub></a></td>
|
|
176
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dharamdan01"><img src="https://avatars.githubusercontent.com/u/188893946?v=4&s=160" width="80" height="80" alt="dharamdan01"/><br /><sub><b>dharamdan01</b></sub></a></td>
|
|
177
|
+
</tr>
|
|
178
|
+
</tbody>
|
|
179
|
+
</table>
|
|
180
|
+
|
|
181
|
+
<!-- markdownlint-restore -->
|
|
182
|
+
<!-- prettier-ignore-end -->
|
|
183
|
+
|
|
184
|
+
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
185
|
+
|
|
186
|
+
如果 agy-powerline 对你有用,点个 ⭐ 能帮助更多 agy 用户发现它。
|
|
187
|
+
|
|
146
188
|
## 许可证
|
|
147
189
|
|
|
148
190
|
[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.3.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
|
+
"configurator": "node scripts/configurator/build.js",
|
|
38
39
|
"lint": "eslint .",
|
|
39
40
|
"lint:fix": "eslint . --fix",
|
|
40
41
|
"format": "prettier --write .",
|
|
@@ -71,6 +72,6 @@
|
|
|
71
72
|
"@eslint/js": "10.0.1",
|
|
72
73
|
"eslint": "10.12.0",
|
|
73
74
|
"globals": "17.13.0",
|
|
74
|
-
"prettier": "3.9.
|
|
75
|
+
"prettier": "3.9.10"
|
|
75
76
|
}
|
|
76
77
|
}
|
package/src/cli/caps.js
CHANGED
package/src/cli/configure.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
1
3
|
const readline = require("readline");
|
|
2
4
|
const { spawn } = require("child_process");
|
|
3
5
|
const {
|
|
@@ -19,11 +21,10 @@ const {
|
|
|
19
21
|
PRESETS,
|
|
20
22
|
PRESET_ORDER,
|
|
21
23
|
applyPreset,
|
|
22
|
-
resolveQuotaThresholds,
|
|
23
|
-
resolveContextThresholds,
|
|
24
24
|
} = require("../lib/shared");
|
|
25
25
|
const { resolveInstallDir } = require("../lib/paths");
|
|
26
26
|
const { WIDGET_TAGS } = require("../lib/widget-style");
|
|
27
|
+
const { editFields: getEditFields } = require("../lib/widget-options");
|
|
27
28
|
const { loadEditableConfig } = require("./config-guard");
|
|
28
29
|
const {
|
|
29
30
|
parseColor,
|
|
@@ -48,7 +49,7 @@ const {
|
|
|
48
49
|
glyphName,
|
|
49
50
|
} = require("../lib/powerline");
|
|
50
51
|
|
|
51
|
-
const REPO_URL = "
|
|
52
|
+
const { REPO_URL } = require("./repo-url");
|
|
52
53
|
|
|
53
54
|
const CSI = "\x1b[";
|
|
54
55
|
const RESET = `${CSI}0m`;
|
|
@@ -749,151 +750,14 @@ function drawMain() {
|
|
|
749
750
|
});
|
|
750
751
|
}
|
|
751
752
|
|
|
752
|
-
// Widgets that take each option, as the layouts read them.
|
|
753
|
-
const QUOTA_WIDGETS = new Set([
|
|
754
|
-
"quota",
|
|
755
|
-
"fiveHourBar",
|
|
756
|
-
"fiveHourPct",
|
|
757
|
-
"weeklyBar",
|
|
758
|
-
"weeklyPct",
|
|
759
|
-
"thirdPartyFiveHour",
|
|
760
|
-
"thirdPartyWeekly",
|
|
761
|
-
"argonWeekly",
|
|
762
|
-
]);
|
|
763
|
-
const PACED_WIDGETS = new Set([
|
|
764
|
-
"fiveHourBar",
|
|
765
|
-
"weeklyBar",
|
|
766
|
-
"thirdPartyFiveHour",
|
|
767
|
-
"thirdPartyWeekly",
|
|
768
|
-
"argonWeekly",
|
|
769
|
-
]);
|
|
770
|
-
const CONTEXT_WIDGETS = new Set(["context", "contextPct", "contextTokens"]);
|
|
771
|
-
const RESET_WIDGETS = new Set(["fiveHourReset", "weeklyReset", "argonReset"]);
|
|
772
|
-
const COLOR_SLOTS = ["model", "context", "git", "weekly", "thirdParty", "fiveHour", "meta"];
|
|
773
753
|
// Longest custom badge text; the badge is meant to be a word or two.
|
|
774
754
|
const TEXT_MAX = 40;
|
|
775
755
|
|
|
776
|
-
const onOff = (value) => (value ? "on" : "off");
|
|
777
|
-
|
|
778
|
-
// The options of one widget: cycle fields step through values, number fields
|
|
779
|
-
// step by 5 with the arrows, text fields take typing, and fixed ones only show
|
|
780
|
-
// their value. undefined means "not set": the widget follows the global
|
|
781
|
-
// setting or its default.
|
|
782
756
|
function editFields(widget) {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
: context
|
|
788
|
-
? resolveContextThresholds({}, config)
|
|
789
|
-
: null;
|
|
790
|
-
const fields = [
|
|
791
|
-
{
|
|
792
|
-
key: "color",
|
|
793
|
-
label: "Color",
|
|
794
|
-
values: [undefined, ...COLOR_SLOTS],
|
|
795
|
-
show: (value) => (value === undefined ? "theme default" : colorName(value)),
|
|
796
|
-
help: "Draws this widget in another of the theme's colors. Caution and danger colors still take over when a quota or the context runs low.",
|
|
797
|
-
},
|
|
798
|
-
{
|
|
799
|
-
key: "raw",
|
|
800
|
-
label: "Label",
|
|
801
|
-
values: [undefined, false, true],
|
|
802
|
-
show: (value) =>
|
|
803
|
-
value === undefined
|
|
804
|
-
? `default (${config.minimalist ? "hidden" : "shown"})`
|
|
805
|
-
: value
|
|
806
|
-
? "hidden, value only"
|
|
807
|
-
: "shown",
|
|
808
|
-
help: "Shows or hides the icon and label before the value. Default follows minimalist mode (m).",
|
|
809
|
-
},
|
|
810
|
-
];
|
|
811
|
-
|
|
812
|
-
if (widget.id === "model") {
|
|
813
|
-
fields.push({
|
|
814
|
-
key: "short",
|
|
815
|
-
label: "Short name",
|
|
816
|
-
values: [undefined, true],
|
|
817
|
-
show: (value) => onOff(value),
|
|
818
|
-
help: "3.8 Flash·High instead of Gemini 3.8 Flash (High).",
|
|
819
|
-
});
|
|
820
|
-
}
|
|
821
|
-
if (widget.id === "totalTokens") {
|
|
822
|
-
fields.push({
|
|
823
|
-
key: "split",
|
|
824
|
-
label: "Split",
|
|
825
|
-
values: [undefined, true],
|
|
826
|
-
show: (value) => onOff(value),
|
|
827
|
-
help: "Input and output tokens apart (123.1K↑ 355.0K↓) instead of one total.",
|
|
828
|
-
});
|
|
829
|
-
}
|
|
830
|
-
if (RESET_WIDGETS.has(widget.id)) {
|
|
831
|
-
fields.push({
|
|
832
|
-
key: "format",
|
|
833
|
-
label: "Format",
|
|
834
|
-
values: [undefined, "time", "both"],
|
|
835
|
-
show: (value) => value ?? "countdown",
|
|
836
|
-
help: "countdown (2hr 47m), time (6:33pm) or both (6:33pm (2hr 47m)).",
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
if (widget.id === "customText") {
|
|
840
|
-
fields.push({
|
|
841
|
-
key: "text",
|
|
842
|
-
label: "Text",
|
|
843
|
-
text: true,
|
|
844
|
-
show: (value) => value || "(empty: hidden)",
|
|
845
|
-
help: "The badge's text. Enter to type, Enter again to keep it, Esc to cancel.",
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
if (widget.id === "exec") {
|
|
849
|
-
fields.push({
|
|
850
|
-
key: "cmd",
|
|
851
|
-
label: "Command",
|
|
852
|
-
fixed: true,
|
|
853
|
-
show: (value) => value || "(none)",
|
|
854
|
-
help: `The command runs only in agy, from your install folder. Edit it in ${getConfigPath()}.`,
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
if (thresholds) {
|
|
858
|
-
const what = quota ? "left" : "used";
|
|
859
|
-
fields.push(
|
|
860
|
-
{
|
|
861
|
-
key: "warn",
|
|
862
|
-
label: "Caution at",
|
|
863
|
-
number: thresholds.warn,
|
|
864
|
-
show: (value) => (value === undefined ? `default (${thresholds.warn}%)` : `${value}%`),
|
|
865
|
-
help: `Turns to the caution color at this share ${what}.`,
|
|
866
|
-
},
|
|
867
|
-
{
|
|
868
|
-
key: "crit",
|
|
869
|
-
label: "Danger at",
|
|
870
|
-
number: thresholds.crit,
|
|
871
|
-
show: (value) => (value === undefined ? `default (${thresholds.crit}%)` : `${value}%`),
|
|
872
|
-
help: `Turns to the danger color at this share ${what}.`,
|
|
873
|
-
},
|
|
874
|
-
{
|
|
875
|
-
key: "blink",
|
|
876
|
-
label: "Blink",
|
|
877
|
-
values: [undefined, true, "warn", false],
|
|
878
|
-
show: (value) =>
|
|
879
|
-
value === undefined
|
|
880
|
-
? `default (${thresholds.blink ? "on" : "off"})`
|
|
881
|
-
: { true: "at danger", warn: "at caution and danger", false: "off" }[String(value)],
|
|
882
|
-
help: "Makes the widget blink when it reaches the danger (or caution) level. ASCII mode never blinks.",
|
|
883
|
-
}
|
|
884
|
-
);
|
|
885
|
-
}
|
|
886
|
-
if (PACED_WIDGETS.has(widget.id)) {
|
|
887
|
-
fields.push({
|
|
888
|
-
key: "pace",
|
|
889
|
-
label: "Pace marker",
|
|
890
|
-
values: [undefined, false, true],
|
|
891
|
-
show: (value) =>
|
|
892
|
-
value === undefined ? `default (${onOff(config.pacing !== false)})` : onOff(value),
|
|
893
|
-
help: "Marks where usage would be if it were spread evenly over the window. Default follows r.",
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
return fields;
|
|
757
|
+
return getEditFields(widget, config, {
|
|
758
|
+
colorName: (value) => (value === undefined ? "theme default" : colorName(value)),
|
|
759
|
+
cmdHelp: `The command runs only in agy, from your install folder. Edit it in ${getConfigPath()}.`,
|
|
760
|
+
});
|
|
897
761
|
}
|
|
898
762
|
|
|
899
763
|
// A theme slot's color name, in that color.
|
package/src/cli/doctor.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
1
3
|
const fs = require("fs");
|
|
2
4
|
const os = require("os");
|
|
3
5
|
const path = require("path");
|
|
@@ -23,6 +25,7 @@ const {
|
|
|
23
25
|
const { runStatuslineCommand } = require("../lib/run");
|
|
24
26
|
const { detectColorDepth } = require("../lib/colors");
|
|
25
27
|
const { readLog, getLogPath } = require("../lib/log");
|
|
28
|
+
const { USAGE_PERIODS, usageAvailable } = require("../lib/usage");
|
|
26
29
|
const { version } = require("../../package.json");
|
|
27
30
|
|
|
28
31
|
const DIM = "\x1b[2m";
|
|
@@ -144,6 +147,15 @@ async function doctorCommand(options = {}) {
|
|
|
144
147
|
console.log(" The bar shows the defaults until you fix the file or delete it.");
|
|
145
148
|
}
|
|
146
149
|
|
|
150
|
+
// Not a failure: the widgets just hide.
|
|
151
|
+
if (configState.config.widgets.some((widget) => USAGE_PERIODS[widget.id]) && !usageAvailable()) {
|
|
152
|
+
console.log();
|
|
153
|
+
console.log(
|
|
154
|
+
`${YELLOW} usage widgets need Node 22.13 or newer${RESET} (this is ${process.version}).`
|
|
155
|
+
);
|
|
156
|
+
console.log(" tokensToday and tokensWeek stay hidden until Node is updated.");
|
|
157
|
+
}
|
|
158
|
+
|
|
147
159
|
const breaking = cmdBreakingChars(installDir);
|
|
148
160
|
if (breaking.length) {
|
|
149
161
|
failed += 1;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const { readConfigFile, mergeConfig, setConfigDir } = require("../lib/shared");
|
|
6
|
+
const { resolveInstallDir, writeFileAtomic, PRIVATE_FILE_MODE } = require("../lib/paths");
|
|
7
|
+
const { printInvalidConfig } = require("./config-guard");
|
|
8
|
+
const { version } = require("../../package.json");
|
|
9
|
+
|
|
10
|
+
const DIM = "\x1b[2m";
|
|
11
|
+
const GREEN = "\x1b[32m";
|
|
12
|
+
const RED = "\x1b[31m";
|
|
13
|
+
const YELLOW = "\x1b[33m";
|
|
14
|
+
const RESET = "\x1b[0m";
|
|
15
|
+
|
|
16
|
+
// The config as the bar reads it, with the release that wrote it first, so an
|
|
17
|
+
// import can tell a file from a newer release.
|
|
18
|
+
function exportDocument(config) {
|
|
19
|
+
return { agyPowerline: { version }, ...mergeConfig(config) };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function execCommandCount(config) {
|
|
23
|
+
return (config.widgets || []).filter(
|
|
24
|
+
(widget) => widget.id === "exec" && String(widget.cmd || "").trim()
|
|
25
|
+
).length;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function exportCommand(file, options = {}) {
|
|
29
|
+
const installDir = resolveInstallDir();
|
|
30
|
+
if (!installDir) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setConfigDir(installDir);
|
|
35
|
+
const { config, error } = readConfigFile();
|
|
36
|
+
if (error) {
|
|
37
|
+
// Exporting the defaults would look like a backup of the user's bar.
|
|
38
|
+
printInvalidConfig(error);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const body = JSON.stringify(exportDocument(config), null, 2) + "\n";
|
|
44
|
+
|
|
45
|
+
if (file) {
|
|
46
|
+
const target = path.resolve(String(file));
|
|
47
|
+
let existing = null;
|
|
48
|
+
try {
|
|
49
|
+
existing = fs.statSync(target);
|
|
50
|
+
} catch {
|
|
51
|
+
// Nothing there yet.
|
|
52
|
+
}
|
|
53
|
+
if (existing?.isDirectory()) {
|
|
54
|
+
console.error(`${RED}error${RESET} ${target} is a folder. Name a file to write.`);
|
|
55
|
+
process.exitCode = 1;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (existing && !options.force) {
|
|
59
|
+
console.error(`${RED}error${RESET} ${target} already exists. Pass --force to replace it.`);
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Owner-only, like the config itself: it can hold your commands.
|
|
64
|
+
writeFileAtomic(target, body, { mode: PRIVATE_FILE_MODE });
|
|
65
|
+
console.error(`${GREEN}✔${RESET} Config exported to ${DIM}${target}${RESET}`);
|
|
66
|
+
} else {
|
|
67
|
+
process.stdout.write(body);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Notes go to stderr, so `export > file` keeps the JSON clean.
|
|
71
|
+
const commands = execCommandCount(config);
|
|
72
|
+
if (commands) {
|
|
73
|
+
console.error(
|
|
74
|
+
`${YELLOW}note${RESET} The export holds ${commands} exec widget command${commands === 1 ? "" : "s"}; check ${commands === 1 ? "it" : "them"} before sharing the file.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
exportCommand,
|
|
81
|
+
exportDocument,
|
|
82
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
saveConfig,
|
|
5
|
+
setConfigDir,
|
|
6
|
+
getConfigPath,
|
|
7
|
+
renderStatusline,
|
|
8
|
+
sampleData,
|
|
9
|
+
resolveColorDepth,
|
|
10
|
+
} = require("../lib/shared");
|
|
11
|
+
const { GRADIENTS, GRADIENT_ORDER, normalizeGradient, gradientStops } = require("../lib/themes");
|
|
12
|
+
const { bg, gradientColors, setActiveDepth, getActiveDepth } = require("../lib/colors");
|
|
13
|
+
const { resolveInstallDir } = require("../lib/paths");
|
|
14
|
+
const { loadEditableConfig } = require("./config-guard");
|
|
15
|
+
|
|
16
|
+
const DIM = "\x1b[2m";
|
|
17
|
+
const BOLD = "\x1b[1m";
|
|
18
|
+
const GREEN = "\x1b[32m";
|
|
19
|
+
const RED = "\x1b[31m";
|
|
20
|
+
const CYAN = "\x1b[36m";
|
|
21
|
+
const RESET = "\x1b[0m";
|
|
22
|
+
|
|
23
|
+
const STRIP_WIDTH = 24;
|
|
24
|
+
|
|
25
|
+
// A row of colored cells showing the gradient; empty without color.
|
|
26
|
+
function gradientStrip(value, config) {
|
|
27
|
+
const stops = gradientStops(value);
|
|
28
|
+
if (!stops) {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
const previous = getActiveDepth();
|
|
32
|
+
setActiveDepth(resolveColorDepth({ configColor: config.color, env: process.env }));
|
|
33
|
+
try {
|
|
34
|
+
if (getActiveDepth() === "none") {
|
|
35
|
+
return "";
|
|
36
|
+
}
|
|
37
|
+
const cells = gradientColors(stops, STRIP_WIDTH).map((color) => `${bg(color)} `);
|
|
38
|
+
return `${cells.join("")}${RESET}`;
|
|
39
|
+
} finally {
|
|
40
|
+
setActiveDepth(previous);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function listGradients(config) {
|
|
45
|
+
const current = config.gradient || "off";
|
|
46
|
+
|
|
47
|
+
console.log();
|
|
48
|
+
console.log(`${BOLD} agy-powerline gradients${RESET}`);
|
|
49
|
+
console.log(` current ${CYAN}${current}${RESET} ${gradientStrip(config.gradient, config)}`);
|
|
50
|
+
console.log(` config ${DIM}${getConfigPath()}${RESET}`);
|
|
51
|
+
console.log();
|
|
52
|
+
|
|
53
|
+
const maxIdLen = Math.max(...GRADIENT_ORDER.map((id) => id.length), "off".length);
|
|
54
|
+
|
|
55
|
+
for (const id of [...GRADIENT_ORDER, "off"]) {
|
|
56
|
+
const isCurrent = id === current;
|
|
57
|
+
const marker = isCurrent ? `${GREEN}*${RESET}` : " ";
|
|
58
|
+
const idFormatted = isCurrent
|
|
59
|
+
? `${BOLD}${CYAN}${id.padEnd(maxIdLen)}${RESET}`
|
|
60
|
+
: id.padEnd(maxIdLen);
|
|
61
|
+
const preview = id === "off" ? `${DIM}theme colors only${RESET}` : gradientStrip(id, config);
|
|
62
|
+
console.log(` ${marker} ${idFormatted} ${preview}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log();
|
|
66
|
+
console.log(` Run ${CYAN}npx agy-powerline gradient <name>${RESET} to color each row.`);
|
|
67
|
+
console.log(` Your own colors: ${CYAN}npx agy-powerline gradient "#ff5f6d-#ffc371"${RESET}`);
|
|
68
|
+
console.log();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function gradientCommand(name, options = {}) {
|
|
72
|
+
const installDir = resolveInstallDir();
|
|
73
|
+
if (!installDir) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setConfigDir(installDir);
|
|
78
|
+
const config = loadEditableConfig();
|
|
79
|
+
if (!config) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!name || options.list) {
|
|
84
|
+
listGradients(config);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const raw = String(name).trim();
|
|
89
|
+
const off = raw.toLowerCase() === "off";
|
|
90
|
+
const value = off ? null : normalizeGradient(raw);
|
|
91
|
+
|
|
92
|
+
if (!off && !value) {
|
|
93
|
+
console.error(`${RED}error${RESET} Unknown gradient "${raw}".`);
|
|
94
|
+
console.error(` Available gradients: ${GRADIENT_ORDER.join(", ")}, off`);
|
|
95
|
+
console.error(" Or 2 to 6 hex colors joined by dashes, e.g. #ff5f6d-#ffc371");
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (off) {
|
|
101
|
+
delete config.gradient;
|
|
102
|
+
} else {
|
|
103
|
+
config.gradient = value;
|
|
104
|
+
}
|
|
105
|
+
saveConfig(config);
|
|
106
|
+
|
|
107
|
+
const title = off ? "off" : GRADIENTS[value]?.name || value;
|
|
108
|
+
|
|
109
|
+
console.log();
|
|
110
|
+
console.log(`${GREEN}✔${RESET} Gradient set to ${BOLD}${title}${RESET}`);
|
|
111
|
+
console.log(` config ${DIM}${getConfigPath()}${RESET}`);
|
|
112
|
+
console.log();
|
|
113
|
+
console.log(" Preview:");
|
|
114
|
+
console.log(renderStatusline(sampleData(), config));
|
|
115
|
+
console.log();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
module.exports = {
|
|
119
|
+
gradientCommand,
|
|
120
|
+
gradientStrip,
|
|
121
|
+
};
|