rinco-pi-hud 1.4.0 → 1.6.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/NOTICE +6 -1
- package/README.md +7 -2
- package/docs/CONTRIBUTING.md +8 -0
- package/docs/README.zh-CN.md +7 -2
- package/docs/footer.md +152 -0
- package/docs/upstream-zentui.md +81 -0
- package/extensions/hud/ZENTUI-LICENSE +21 -0
- package/extensions/hud/commands/settings-controller.ts +6 -0
- package/extensions/hud/commands/settings.ts +94 -9
- package/extensions/hud/config/config.ts +3 -0
- package/extensions/hud/config/normalize.ts +43 -4
- package/extensions/hud/config/persist.ts +45 -0
- package/extensions/hud/config/schema.ts +70 -22
- package/extensions/hud/footer/compact.ts +160 -0
- package/extensions/hud/footer/custom-values.ts +83 -0
- package/extensions/hud/footer/demand.ts +32 -0
- package/extensions/hud/footer/format.ts +152 -10
- package/extensions/hud/footer/index.ts +126 -26
- package/extensions/hud/footer/render-labels.ts +2 -2
- package/extensions/hud/index.ts +101 -8
- package/extensions/hud/segments/custom-variables.ts +152 -0
- package/extensions/hud/segments/extension-status.ts +35 -15
- package/extensions/hud/segments/git.ts +37 -29
- package/extensions/hud/segments/live-metadata.ts +357 -0
- package/extensions/hud/segments/project-state.ts +4 -0
- package/extensions/hud/segments/repository-root.ts +18 -0
- package/extensions/hud/segments/runtime-definitions.ts +12 -0
- package/extensions/hud/segments/runtime.ts +6 -2
- package/extensions/hud/session/footer-clock.ts +14 -20
- package/extensions/hud/state/index.ts +3 -0
- package/extensions/hud/state/project-controller.ts +7 -1
- package/extensions/hud/telemetry/format.ts +28 -7
- package/extensions/hud/telemetry/optional.ts +183 -0
- package/package.json +1 -1
package/NOTICE
CHANGED
|
@@ -4,7 +4,12 @@ License: MIT.
|
|
|
4
4
|
|
|
5
5
|
This package contains a dynamic HUD footer for Pi, adapted from the Footer
|
|
6
6
|
portion of pi-zentui by Luka: https://github.com/lmilojevicc/pi-zentui
|
|
7
|
-
|
|
7
|
+
Footer integration snapshot: version 0.32.0, commit
|
|
8
|
+
9e8e798cfcce26cee9e1def4daf3668616482610.
|
|
9
|
+
Adapted modules include template joining/compact layout, custom values,
|
|
10
|
+
repository paths, optional telemetry, and passive live metadata.
|
|
11
|
+
License: MIT. The original copyright and license text is retained at
|
|
12
|
+
extensions/hud/ZENTUI-LICENSE.
|
|
8
13
|
|
|
9
14
|
Session, model, activity, MCP, configuration-count, and Codex subscription
|
|
10
15
|
status capabilities are adapted from pi-shannon-statusline by RealAlexandreAI:
|
package/README.md
CHANGED
|
@@ -54,6 +54,9 @@ pi install "$PWD"
|
|
|
54
54
|
| `/zentui statusline enable\|disable\|toggle` | Show or hide the whole footer |
|
|
55
55
|
| `/zentui row project\|session\|activity\|usage <enable\|disable\|toggle>` | Turn one row on or off |
|
|
56
56
|
| `/zentui format "<template>"` | Replace the four rows with a single-line template |
|
|
57
|
+
| `/zentui compact-format "<template>"` | Opt into a wrapped template below 80 columns; `clear` removes it |
|
|
58
|
+
| `/zentui footer hud\|hidden\|native` | Four-group/template HUD, statuses only, or Pi’s native footer |
|
|
59
|
+
| `/zentui layout\|segments\|extensions` | Open a settings section directly |
|
|
57
60
|
| `/codex-status` | Print Codex subscription usage and rate-limit windows |
|
|
58
61
|
| `/usage-refresh` | Refresh the model usage or balance shown in the footer |
|
|
59
62
|
|
|
@@ -98,7 +101,9 @@ Variables accept `$name` or `${name}`. The [footer reference](docs/footer.md) li
|
|
|
98
101
|
|
|
99
102
|
`/zentui` writes to `~/.pi/agent/rinco-pi-hud.json` and applies changes immediately. It covers the separator style, icon mode (`nerd`, `ascii`, or `auto`), footer colors (Pi theme tokens or terminal palette styles), context rendering (text, gauge, or both), the project refresh interval, and third-party extension statuses with their own placement and color mode.
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
Footer colors follow the active Pi theme by default, including theme switches and automatic light/dark changes. In `/zentui`, **Footer colors → theme** enables this behavior; **terminal** keeps the original Sakura RGB palette. Explicit custom colors (hex, indexed, or `fg:`/`bg:` styles) remain fixed in either mode. Remove those entries from `colors` to restore automatic theme colors.
|
|
105
|
+
|
|
106
|
+
Footer features are adapted from Zentui **0.32.0**, without installing its editor, messages, working line, tool display, or OMP adapter. New opt-in features include repository-relative paths, `$join_sep`, compact templates, extension-published values, safe status hyperlinks, PR/CI metadata, and average token rate. Defaults and existing configurations remain unchanged. See [docs/upstream-zentui.md](docs/upstream-zentui.md) for the exact snapshot and integration boundaries, and [docs/footer.md](docs/footer.md) for configuration and examples.
|
|
102
107
|
|
|
103
108
|
## Project contents
|
|
104
109
|
|
|
@@ -156,7 +161,7 @@ The footer measures the terminal and wraps at segment boundaries, so narrow wind
|
|
|
156
161
|
<details>
|
|
157
162
|
<summary>Colors look incorrect</summary>
|
|
158
163
|
|
|
159
|
-
The default
|
|
164
|
+
The default palette follows the active Pi theme. If the footer still uses fixed colors, select **Footer colors → theme** in `/zentui` and remove explicit color overrides from `~/.pi/agent/rinco-pi-hud.json`. Third-party statuses set to `original` retain their own colors. Truecolor and 256-color output follow Pi's detected terminal capabilities.
|
|
160
165
|
</details>
|
|
161
166
|
|
|
162
167
|
<details>
|
package/docs/CONTRIBUTING.md
CHANGED
|
@@ -61,6 +61,14 @@ Tests live in `tests/`:
|
|
|
61
61
|
- `model-usage-refresh.test.ts` — Model usage refresh integration.
|
|
62
62
|
- `token-switch-usage.test.ts` — Token Switch balance formatting.
|
|
63
63
|
- `deepseek-balance.test.ts` — DeepSeek balance parsing, formatting, and query safety.
|
|
64
|
+
- `footer-template-enhancements.test.ts` — Conditional joining, compact packing and alignment.
|
|
65
|
+
- `footer-options.test.ts` — New layout persistence, direct commands and settings routes.
|
|
66
|
+
- `footer-lifecycle.test.ts` — Root wiring, ownership/demand cleanup, clock teardown and inactive network gating.
|
|
67
|
+
- `repository-path.test.ts` — Repository paths, worktrees, safe fallback and stale roots.
|
|
68
|
+
- `live-metadata.test.ts` — Demand gating, GitHub identity/TTL and completed-call token rate.
|
|
69
|
+
- `zentui-footer-integration.test.ts` — Publisher bounds, atomic values, links, hidden mode and optional telemetry.
|
|
70
|
+
|
|
71
|
+
Upstream source versions and adaptation boundaries are recorded in [upstream-zentui.md](upstream-zentui.md).
|
|
64
72
|
|
|
65
73
|
When adding tests:
|
|
66
74
|
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -54,6 +54,9 @@ pi install "$PWD"
|
|
|
54
54
|
| `/zentui statusline enable\|disable\|toggle` | 显示或隐藏整个脚注 |
|
|
55
55
|
| `/zentui row project\|session\|activity\|usage <enable\|disable\|toggle>` | 开关某一行 |
|
|
56
56
|
| `/zentui format "<template>"` | 用单行模板替换四行布局 |
|
|
57
|
+
| `/zentui compact-format "<template>"` | 设置低于 80 列时使用的换行模板;`clear` 清除 |
|
|
58
|
+
| `/zentui footer hud\|hidden\|native` | HUD、仅第三方状态、恢复 Pi 原生脚注 |
|
|
59
|
+
| `/zentui layout\|segments\|extensions` | 直接打开对应设置页 |
|
|
57
60
|
| `/codex-status` | 打印 Codex 订阅用量与限额窗口 |
|
|
58
61
|
| `/usage-refresh` | 立即刷新脚注上的模型用量或余额 |
|
|
59
62
|
|
|
@@ -98,7 +101,9 @@ Usage 行跟随当前模型的 Provider:
|
|
|
98
101
|
|
|
99
102
|
`/zentui` 把配置写入 `~/.pi/agent/rinco-pi-hud.json`,改动立即生效。可配置分隔符样式、图标模式(`nerd`、`ascii`、`auto`)、脚注配色(Pi 主题色或终端调色板样式)、Context 渲染方式(文本、进度条或两者)、项目刷新间隔,以及第三方扩展状态的位置与颜色模式。
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
脚注默认自动跟随当前 Pi 主题,包括手动切换主题和自动明暗切换。在 `/zentui` 中选择 **Footer colors → theme** 启用跟随,选择 **terminal** 则保留原有 Sakura RGB 配色。显式自定义颜色(十六进制、索引色或 `fg:`/`bg:` 样式)在两种模式下都会保留;删除 `colors` 中的相应配置即可恢复自动配色。
|
|
105
|
+
|
|
106
|
+
已适配 Zentui **0.32.0** 的 Footer 新功能:仓库相对路径、`$join_sep` 条件分隔符、窄屏模板、扩展发布的自定义值、安全状态超链接,以及按需启用的 PR/CI 与平均 Token 速率。默认四行 HUD、原有配置及余额查询保持不变;不接管编辑器、消息、工作进度行或工具展示,也不引入 OMP 适配。同步快照和边界见[上游集成记录](upstream-zentui.md),配置示例和完整变量参考见 [docs/footer.md](footer.md)。
|
|
102
107
|
|
|
103
108
|
## 仓库结构
|
|
104
109
|
|
|
@@ -156,7 +161,7 @@ npx tsx assets/readme/source/build-assets.mts /tmp/footer.json
|
|
|
156
161
|
<details>
|
|
157
162
|
<summary>颜色显示异常</summary>
|
|
158
163
|
|
|
159
|
-
|
|
164
|
+
默认配色自动跟随当前 Pi 主题。如果脚注仍使用固定颜色,请在 `/zentui` 中选择 **Footer colors → theme**,并删除 `~/.pi/agent/rinco-pi-hud.json` 中的显式颜色覆盖。设为 `original` 的第三方扩展状态会保留自己的颜色。Truecolor 与 256 色输出仍按 Pi 检测到的终端能力选择。
|
|
160
165
|
</details>
|
|
161
166
|
|
|
162
167
|
<details>
|
package/docs/footer.md
CHANGED
|
@@ -26,6 +26,48 @@ Control each default-layout row independently:
|
|
|
26
26
|
|
|
27
27
|
The command shape is `/zentui row <project|session|activity|usage> <enable|disable|toggle>`. Values persist under `footerRows`; all four default to `true`. Row switches apply only to the default layout and do not alter a custom single-line `footerFormat`.
|
|
28
28
|
|
|
29
|
+
## Footer modes
|
|
30
|
+
|
|
31
|
+
- `/zentui footer hud`: enable the normal four groups or configured templates.
|
|
32
|
+
- `/zentui footer hidden`: show only allowed third-party statuses, no row when none exist.
|
|
33
|
+
- `/zentui footer native`: release ownership to Pi; saved HUD choices remain unchanged.
|
|
34
|
+
|
|
35
|
+
Hidden and HUD share the existing per-key `extensionStatuses` placement/color policy.
|
|
36
|
+
Native is Pi-owned and does not apply this policy. `/zentui layout`, `segments`, and
|
|
37
|
+
`extensions` open the corresponding settings tabs directly.
|
|
38
|
+
|
|
39
|
+
## Repository paths
|
|
40
|
+
|
|
41
|
+
`pathDisplay` defaults to `{ "mode": "basename", "depth": 0 }`.
|
|
42
|
+
Select `repository` in Layout → Path display, or set:
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{ "pathDisplay": { "mode": "repository", "depth": 2 } }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Repository root displays `.`; `/repo/packages/core/src` displays `…/core/src` at depth 2.
|
|
49
|
+
Zero depth is unlimited. Worktrees are supported. Until a root qualified for the current cwd
|
|
50
|
+
is available, missing/failed/unsafe roots fall back to the unlimited full path with home `~`
|
|
51
|
+
abbreviation; a stale root is never used to produce a relative path.
|
|
52
|
+
|
|
53
|
+
## Colors and theme switching
|
|
54
|
+
|
|
55
|
+
By default, `colorSources.starship` is `"theme"`. Built-in footer colors use semantic Pi theme tokens such as `accent`, `muted`, `success`, `warning`, and `error`; runtime labels also use the active theme. Switching themes, including automatic light/dark switching, recolors the footer without reloading the extension. This applies to both the default rows and custom templates.
|
|
56
|
+
|
|
57
|
+
To enable theme colors in an existing configuration, use **Footer colors → theme** in `/zentui`, or set:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"colorSources": {
|
|
62
|
+
"starship": "theme"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Select `"terminal"` to retain the original Sakura RGB defaults and Starship runtime colors. Changing the source does not write a palette into your configuration.
|
|
68
|
+
|
|
69
|
+
Entries you explicitly set under `colors` override the selected defaults. Semantic tokens (for example, `"cwd": "bold accent"`) follow the theme; hex colors, indexed colors, and explicit `fg:`/`bg:` styles remain fixed. Remove an override to restore the default for that segment. Third-party extension statuses in `original` color mode retain their own ANSI colors; use `zentui` color mode to recolor them with the HUD.
|
|
70
|
+
|
|
29
71
|
## Built-in status segments
|
|
30
72
|
|
|
31
73
|
| Key | Default | Group | Description |
|
|
@@ -81,6 +123,12 @@ Clear it:
|
|
|
81
123
|
- `( ... )` — conditional group; hidden when all contained variables are empty
|
|
82
124
|
- `$fill` — layout partition marker (0 = left only, 1 = left/right, 2 = left/middle/right)
|
|
83
125
|
- `$sep` — styled ` | ` separator, does not cause a conditional group to show
|
|
126
|
+
- `$join_sep` — insert the styled separator only between populated sibling fields
|
|
127
|
+
|
|
128
|
+
For example, `$session_name$join_sep($git_branch $git_status)$join_sep$runtime` joins only
|
|
129
|
+
populated fields, with no orphan separator when an intervening field is missing. Each group
|
|
130
|
+
and top-level fill/wrap boundary has its own join scope. Do not pad `$join_sep` with spaces;
|
|
131
|
+
the separator already owns spacing. Templates without it retain their previous behavior.
|
|
84
132
|
|
|
85
133
|
### Variable list
|
|
86
134
|
|
|
@@ -125,7 +173,13 @@ Clear it:
|
|
|
125
173
|
| `$tool_counts` | Native tool completion counts |
|
|
126
174
|
| `$running_tools` | Recently running tools |
|
|
127
175
|
| `$active_agents` | Active primary agent runs |
|
|
176
|
+
| `$subscription` | Optional `(sub)` when Pi reports OAuth subscription use (also kimi-coding) |
|
|
177
|
+
| `$auto_compaction` | Optional `(auto)` when public, trust-aware Pi settings report automatic compaction |
|
|
178
|
+
| `$pr_number` / `$pr_url` | Current open GitHub PR number / plain HTTPS URL, qualified against local HEAD |
|
|
179
|
+
| `$ci` | `CI passing`, `CI running`, `CI failed`, `CI no checks`, or `CI stale`; unavailable data is empty |
|
|
180
|
+
| `$token_rate` | Current interaction’s completed-call average, e.g. `48 tok/s avg` |
|
|
128
181
|
| `$sep` | Styled ` | ` |
|
|
182
|
+
| `$join_sep` | Conditional sibling separator |
|
|
129
183
|
| `$fill` | Layout marker, renders nothing |
|
|
130
184
|
|
|
131
185
|
### Aliases
|
|
@@ -140,6 +194,8 @@ $tag → $git_tag
|
|
|
140
194
|
$duration → $session_duration
|
|
141
195
|
$session → $session_name
|
|
142
196
|
$thinking_level → $thinking
|
|
197
|
+
$thinkingLevel → $thinking
|
|
198
|
+
$codex_quota → $codex_usage
|
|
143
199
|
$turn_count → $turn
|
|
144
200
|
$codex → $codex_usage
|
|
145
201
|
$tools → $tool_counts
|
|
@@ -147,6 +203,102 @@ $agents → $active_agents
|
|
|
147
203
|
$separator → $sep
|
|
148
204
|
```
|
|
149
205
|
|
|
206
|
+
## Opt-in compact templates
|
|
207
|
+
|
|
208
|
+
Set a separate narrow layout (used below 80 terminal columns):
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
/zentui compact-format "$cwd$wrap(on $git_branch)$fill$context$wrap_sep$tokens$wrap_sep$extension_statuses"
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`$wrap` starts an independently packable chunk with a space boundary.
|
|
215
|
+
`$wrap_sep` uses the configured default-layout separator only when chunks share a row.
|
|
216
|
+
The first top-level `$fill` divides left/right zones; right-hand chunks have priority in
|
|
217
|
+
template order and are right-aligned. Nested/additional fills do not create extra zones.
|
|
218
|
+
`$extension_statuses` expands allowed third-party statuses into separate chunks; the existing
|
|
219
|
+
`$extensions` still reports installed packages. Wide templates keep their existing status placements.
|
|
220
|
+
|
|
221
|
+
`compactFooterMaxLines` defaults to 2 (range 1–8), and omitted/truncated chunks use `…`.
|
|
222
|
+
Layout → Responsive compact controls `responsiveFooter`; an empty compact format is inert.
|
|
223
|
+
Use `/zentui compact-format clear` to return to the existing layout at every width.
|
|
224
|
+
Custom templates and wide templates are otherwise unchanged; arbitrary narrow layouts cannot keep every field.
|
|
225
|
+
|
|
226
|
+
## Custom extension values
|
|
227
|
+
|
|
228
|
+
Alias stable publisher keys in JSON, then explicitly reference them in templates:
|
|
229
|
+
|
|
230
|
+
```json
|
|
231
|
+
{
|
|
232
|
+
"footerVariables": { "quota": "@scope/usage:quota" },
|
|
233
|
+
"customValueColorMode": "original",
|
|
234
|
+
"customValueColors": { "@scope/usage:quota": "bold success" },
|
|
235
|
+
"footerFormat": "$cwd$fill($quota)($sep$cost)",
|
|
236
|
+
"compactFooterFormat": "$cwd$fill($quota)$wrap_sep$context"
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Aliases cannot shadow builtins, builtin aliases, structural/prototype names, except an explicit
|
|
241
|
+
`ci` alias may replace GitHub CI and suppress its query. At most 16 aliases are accepted.
|
|
242
|
+
Unpublished/empty values drop optional groups. Nothing is automatically appended to templates.
|
|
243
|
+
|
|
244
|
+
Original color mode preserves safe publisher SGR/HTTP(S) links; Zentui mode uses the current
|
|
245
|
+
HUD extension-status color. `customValueColors` overrides are keyed by publisher ID across
|
|
246
|
+
aliases: explicit styles replace publisher styling, `""` removes styling, and deleting a leaf
|
|
247
|
+
restores inheritance. These JSON-only per-value overrides are independent of `setStatus()` colors.
|
|
248
|
+
Layout → Custom value colors changes the shared mode.
|
|
249
|
+
|
|
250
|
+
Publish through Pi's shared event bus using Zentui protocol v1:
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
const key = "@scope/usage:quota";
|
|
254
|
+
const capability = { supported: false, active: false, key };
|
|
255
|
+
pi.events.emit("zentui:variable-capability", capability);
|
|
256
|
+
if (capability.active) {
|
|
257
|
+
pi.events.emit("zentui:variable", { key, text: "Quota 80%" });
|
|
258
|
+
ctx.ui.setStatus(key, undefined); // remove only your own fallback
|
|
259
|
+
} else {
|
|
260
|
+
ctx.ui.setStatus(key, "Quota 80%");
|
|
261
|
+
}
|
|
262
|
+
// Removal also works after demand becomes inactive:
|
|
263
|
+
pi.events.emit("zentui:variable", { key, text: undefined });
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Capability probes receive `supported: true`, `version: 1`, and current `active` demand.
|
|
267
|
+
Probe again on each publisher refresh. Active means an owned, enabled current TUI Footer
|
|
268
|
+
references the alias in its wide or enabled compact template, not guaranteed visibility at every width.
|
|
269
|
+
Hidden/native/disabled/unowned consumers are inactive. Registry limits: 16 publisher keys,
|
|
270
|
+
64 UTF-16 units per key, 256 per raw text. Inactive/malformed/oversized updates are ignored;
|
|
271
|
+
empty text removes a value. Session replacement, shutdown or lost ownership/demand clears values.
|
|
272
|
+
Publishers must republish on reactivation and own their refresh/freshness resources.
|
|
273
|
+
A custom value is kept whole or omitted with template recomposition, never partially cropped.
|
|
274
|
+
|
|
275
|
+
## Status hyperlinks and live metadata
|
|
276
|
+
|
|
277
|
+
Third-party statuses in `original` mode preserve only SGR and HTTP(S) OSC 8 hyperlinks.
|
|
278
|
+
Unsafe protocols, title, clipboard, cursor controls and embedded line breaks are stripped;
|
|
279
|
+
links and styles are closed before adjacent content. Zentui mode strips publisher styling/links.
|
|
280
|
+
|
|
281
|
+
```text
|
|
282
|
+
/zentui format "$cwd( · PR #$pr_number)( · $ci)$fill($token_rate)"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
PR/CI collection is opt-in through template references and requires installed/authenticated
|
|
286
|
+
`gh` plus an unambiguous `github.com` Git remote. No tools are installed or authenticated
|
|
287
|
+
automatically, and no fetch/write is performed. Repository/head changes invalidate results;
|
|
288
|
+
missing CLI/auth/PR, mismatched local/PR HEAD, detached HEAD or conflicting remotes yield empty values.
|
|
289
|
+
A 30-second cache is refreshed by normal session/project synchronization, not an extra polling timer.
|
|
290
|
+
A stale retained check snapshot shows `CI stale`. An explicit `ci` custom alias uses only publisher data.
|
|
291
|
+
|
|
292
|
+
`$token_rate` divides completed output by observed model-call duration in the current interaction.
|
|
293
|
+
It includes first-byte wait, excludes tools/gaps, and never estimates in-flight output speed.
|
|
294
|
+
It retains completed values until the next interaction; incomplete measurements show `— tok/s avg`.
|
|
295
|
+
|
|
296
|
+
Optional subscription and compaction markers use public Pi capabilities only and render empty
|
|
297
|
+
when unavailable. Automatic compaction reads respect public project trust and are refreshed during
|
|
298
|
+
normal Footer synchronization. They do not change Pi settings.
|
|
299
|
+
|
|
300
|
+
See [the integration record](upstream-zentui.md) for the pinned source and differences from full Zentui.
|
|
301
|
+
|
|
150
302
|
## Model quota
|
|
151
303
|
|
|
152
304
|
The status line automatically switches quota display based on model provider:
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Zentui Footer integration
|
|
2
|
+
|
|
3
|
+
## Snapshot
|
|
4
|
+
|
|
5
|
+
- Source: https://github.com/lmilojevicc/pi-zentui
|
|
6
|
+
- Package version: **0.32.0**
|
|
7
|
+
- Commit: `9e8e798cfcce26cee9e1def4daf3668616482610`
|
|
8
|
+
- License: MIT; original copyright and full terms are in
|
|
9
|
+
[extensions/hud/ZENTUI-LICENSE](../extensions/hud/ZENTUI-LICENSE).
|
|
10
|
+
|
|
11
|
+
This is a selective source adaptation, **not a dependency update or a complete Zentui fork**.
|
|
12
|
+
No extra runtime dependency is installed. Pi 1.3–1.x remains supported.
|
|
13
|
+
|
|
14
|
+
## Adapted capabilities
|
|
15
|
+
|
|
16
|
+
| Upstream capability | HUD integration |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Repository-relative cwd | `pathDisplay.mode: "repository"`, worktree-aware root, depth after relative conversion, unlimited full-path fallback when no current safe root exists |
|
|
19
|
+
| Conditional joining | `$join_sep` in local group/fill/wrap scopes, ignoring empty and control-only fields |
|
|
20
|
+
| Compact templates | `compactFooterFormat`, `$wrap`, `$wrap_sep`, first `$fill` left/right alignment, 1–8 line cap |
|
|
21
|
+
| Extension statuses in compact layouts | `$extension_statuses`; existing `$extensions` still means installed-package count |
|
|
22
|
+
| Custom publisher values | Protocol v1 events, `footerVariables` aliases, demand/ownership/session gating, atomic fitting |
|
|
23
|
+
| Custom value colors | `customValueColorMode` and JSON-only publisher-keyed `customValueColors` overrides |
|
|
24
|
+
| Original status hyperlinks | Preserve safe HTTP(S) OSC 8 and SGR only; close links/styles before neighboring content |
|
|
25
|
+
| Passive live metadata | Template-opt-in `$pr_number`, `$pr_url`, `$ci`, `$token_rate` |
|
|
26
|
+
| Atomic telemetry | `$cache_read` and `$cache_write` already existed; add optional `$subscription` / `$auto_compaction` |
|
|
27
|
+
| Thinking variable | `$thinkingLevel` aliases the existing `$thinking` |
|
|
28
|
+
| Status-only / native mode | `/zentui footer hidden` keeps permitted statuses; `native` releases Footer ownership |
|
|
29
|
+
| Direct settings access | `/zentui layout`, `segments`, `extensions` |
|
|
30
|
+
|
|
31
|
+
## Deliberate differences
|
|
32
|
+
|
|
33
|
+
- Four semantic HUD groups remain the default. Compact rendering is **opt-in**: an empty compact
|
|
34
|
+
format never replaces the default layout. A configured compact format is selected below
|
|
35
|
+
**80 terminal columns** when `responsiveFooter` is enabled.
|
|
36
|
+
- Keep the independent, flat `~/.pi/agent/rinco-pi-hud.json` schema. Upstream
|
|
37
|
+
`components.footer.styles.starship` objects are not automatically imported.
|
|
38
|
+
- Keep existing theme defaults, legacy configuration migration, and all HUD counters, skills,
|
|
39
|
+
MCP file counts, model/provider labels, live context and usage totals.
|
|
40
|
+
- Keep the existing Codex quota client and DeepSeek / Token Switch balance probes.
|
|
41
|
+
`$codex_quota` is a compatibility alias for `$codex_usage`, not a new collector.
|
|
42
|
+
Upstream's separate official-OpenAI response-header quota collector is not installed.
|
|
43
|
+
- Status placement/color policy is shared between HUD and Hidden modes; native mode belongs to
|
|
44
|
+
Pi and is not decorated to enforce HUD visibility settings.
|
|
45
|
+
- GitHub supports unambiguous `github.com` fetch remotes only. It uses existing `git` and `gh`
|
|
46
|
+
executables, never installs/authenticates tools, and does not fetch or mutate the repository.
|
|
47
|
+
Private GitHub Enterprise hosts, detached HEAD, conflicting remotes, missing auth/CLI/PR,
|
|
48
|
+
or a PR head that differs from local HEAD render empty.
|
|
49
|
+
- No editors, user-message styling, working-line protocol, tool display, experimental thinking,
|
|
50
|
+
selector patches, OMP data/adapter, or full upstream component/preset settings are installed.
|
|
51
|
+
Avoid installing full Zentui alongside this package unless exactly one owns Footer.
|
|
52
|
+
|
|
53
|
+
## Refresh and safety
|
|
54
|
+
|
|
55
|
+
PR/CI demand comes only from enabled HUD templates (including an enabled compact format),
|
|
56
|
+
not from defaults or merely configured aliases. An explicit custom `ci` alias suppresses
|
|
57
|
+
GitHub CI demand. Queries are read-only, bounded, negative-cached for 30 seconds, and
|
|
58
|
+
qualified against repository/worktree, branch, remotes and HEAD before and after lookup.
|
|
59
|
+
Current UI sync and project refresh ticks drive collection; there is no extra GitHub polling timer.
|
|
60
|
+
Expired retained CI reads `CI stale`. Stop, replacement, cwd changes and lost demand discard
|
|
61
|
+
in-flight results. A successful unchanged snapshot reuses cached GitHub data.
|
|
62
|
+
|
|
63
|
+
Token rate is final output divided by observed completed model-call duration in the current
|
|
64
|
+
agent interaction; first-byte wait is included, tools/inter-call gaps are excluded. It is not a
|
|
65
|
+
live streaming speed estimate. Completed values remain until the next interaction; missing or
|
|
66
|
+
incomplete measurement reads `— tok/s avg`.
|
|
67
|
+
|
|
68
|
+
Publishers retain responsibility for their values' freshness and fallback statuses.
|
|
69
|
+
No publication data is saved to disk. Values that cannot fit whole are removed and the template
|
|
70
|
+
is recomposed, never shown as a partial quota. The protocol bounds remain 16 keys, 64 UTF-16
|
|
71
|
+
units per key and 256 units per raw value.
|
|
72
|
+
|
|
73
|
+
## Future updates
|
|
74
|
+
|
|
75
|
+
1. Pin and record the new upstream SHA before comparing source.
|
|
76
|
+
2. Compare Footer/template, runtime/git, status sanitization, custom-value protocol and telemetry
|
|
77
|
+
modules, rather than replacing upstream's composition root.
|
|
78
|
+
3. Preserve this project's configuration file, four-group layout, provider probes and Pi compatibility.
|
|
79
|
+
4. Add tests for both new behavior and old layouts, including narrow widths, ANSI/OSC,
|
|
80
|
+
transient errors, stale responses, disabled demand and session/ownership cleanup.
|
|
81
|
+
5. Run `npm run verify` and `npm run pack:check`; separately smoke-test a real Pi TUI.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Luka
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
saveExtensionStatusColorMode,
|
|
7
7
|
saveExtensionStatusPlacement,
|
|
8
8
|
saveFooterFormatPatch,
|
|
9
|
+
saveFooterOptionsPatch,
|
|
9
10
|
saveFooterRowsPatch,
|
|
10
11
|
saveFooterSegmentsPatch,
|
|
11
12
|
saveGitBranchPatch,
|
|
@@ -48,6 +49,11 @@ export function registerSettingsController(pi: ExtensionAPI, deps: SettingsContr
|
|
|
48
49
|
updateConfig(saveFooterSegmentsPatch(patch));
|
|
49
50
|
deps.restartSessionTimer();
|
|
50
51
|
},
|
|
52
|
+
setFooterOptions(patch, ctx) {
|
|
53
|
+
updateConfig(saveFooterOptionsPatch(patch));
|
|
54
|
+
deps.applyConfiguredUi(ctx);
|
|
55
|
+
deps.restartSessionTimer();
|
|
56
|
+
},
|
|
51
57
|
setFooterFormat(value) {
|
|
52
58
|
updateConfig(saveFooterFormatPatch(value));
|
|
53
59
|
deps.restartSessionTimer();
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
type ExtensionStatusColorMode,
|
|
17
17
|
type ExtensionStatusPlacement,
|
|
18
18
|
type FooterRowsConfig,
|
|
19
|
+
type FooterOptionsPatch,
|
|
19
20
|
type FooterSegmentsConfig,
|
|
20
21
|
type GitBranchConfig,
|
|
21
22
|
type GitBranchMaxLength,
|
|
@@ -45,7 +46,7 @@ const extensionStatusPlacementValues: ExtensionStatusPlacement[] = [
|
|
|
45
46
|
const extensionStatusColorModeValues: ExtensionStatusColorMode[] = ["zentui", "original"];
|
|
46
47
|
const contextStyleValues: ContextStyle[] = ["text", "gauge", "text+gauge"];
|
|
47
48
|
const separatorStyleValues: SeparatorStyle[] = ["pipe", "dot", "chevron", "none"];
|
|
48
|
-
const pathDisplayModeValues: PathDisplayMode[] = ["basename", "full"];
|
|
49
|
+
const pathDisplayModeValues: PathDisplayMode[] = ["basename", "full", "repository"];
|
|
49
50
|
const pathDepthValues = ["0", "1", "2", "3", "4", "5"] as const;
|
|
50
51
|
const branchLengthPresetValues = ["full", "10", "20", "30", "40", "50"] as const;
|
|
51
52
|
const iconModeValues: IconMode[] = ["auto", "nerd", "ascii"];
|
|
@@ -74,6 +75,7 @@ type SettingsCommandDeps = {
|
|
|
74
75
|
setFooterRows: (patch: Partial<FooterRowsConfig>) => void;
|
|
75
76
|
setFooterSegments: (patch: Partial<FooterSegmentsConfig>) => void;
|
|
76
77
|
setFooterFormat: (value: string) => void;
|
|
78
|
+
setFooterOptions?: (patch: FooterOptionsPatch, ctx: ExtensionContext) => void;
|
|
77
79
|
setIconMode: (mode: IconMode) => void;
|
|
78
80
|
setContextStyle: (style: ContextStyle) => void;
|
|
79
81
|
setSeparator: (separator: SeparatorStyle) => void;
|
|
@@ -92,7 +94,7 @@ const colorSettingLabels: Record<ColorSettingId, string> = {
|
|
|
92
94
|
|
|
93
95
|
const colorSettingDescriptions: Record<ColorSettingId, string> = {
|
|
94
96
|
starship:
|
|
95
|
-
"
|
|
97
|
+
"Follow the active Pi theme automatically (theme), or keep the original Sakura/runtime palette (terminal). Explicit color overrides are preserved.",
|
|
96
98
|
};
|
|
97
99
|
|
|
98
100
|
const featureSettingLabels: Record<FeatureSettingId, string> = {
|
|
@@ -181,6 +183,14 @@ const footerSegmentSettingDescriptions: Record<FooterSegmentSettingId, string> =
|
|
|
181
183
|
};
|
|
182
184
|
|
|
183
185
|
const directCommandSuggestions = [
|
|
186
|
+
"layout",
|
|
187
|
+
"extensions",
|
|
188
|
+
"segments",
|
|
189
|
+
"footer hud",
|
|
190
|
+
"footer hidden",
|
|
191
|
+
"footer native",
|
|
192
|
+
"compact-format clear",
|
|
193
|
+
'compact-format "$cwd$fill$context"',
|
|
184
194
|
"statusline enable",
|
|
185
195
|
"statusline disable",
|
|
186
196
|
"statusline toggle",
|
|
@@ -262,7 +272,7 @@ function isContextStyle(value: string): value is ContextStyle {
|
|
|
262
272
|
}
|
|
263
273
|
|
|
264
274
|
function isPathDisplayMode(value: string): value is PathDisplayMode {
|
|
265
|
-
return value === "basename" || value === "full";
|
|
275
|
+
return value === "basename" || value === "full" || value === "repository";
|
|
266
276
|
}
|
|
267
277
|
|
|
268
278
|
function isPathDepthValue(value: string): boolean {
|
|
@@ -323,7 +333,7 @@ function footerSegmentPatch(
|
|
|
323
333
|
}
|
|
324
334
|
|
|
325
335
|
function usageText(): string {
|
|
326
|
-
return 'Usage: /zentui statusline [enable|disable|toggle], /zentui row [project|session|activity|usage] [enable|disable|toggle], or /zentui format "<template>"';
|
|
336
|
+
return 'Usage: /zentui statusline [enable|disable|toggle], /zentui row [project|session|activity|usage] [enable|disable|toggle], or /zentui format "<template>", /zentui compact-format "<template>"|clear, /zentui footer hud|hidden|native, /zentui layout|extensions|segments';
|
|
327
337
|
}
|
|
328
338
|
|
|
329
339
|
function isFooterRow(value: string): value is keyof FooterRowsConfig {
|
|
@@ -376,7 +386,7 @@ function parseDirectFeatureCommand(
|
|
|
376
386
|
|
|
377
387
|
function parseFormatCommand(args: string): { value: string | undefined } | undefined {
|
|
378
388
|
const trimmed = args.trim();
|
|
379
|
-
if (
|
|
389
|
+
if (!/^format(?:\s|$)/i.test(trimmed)) return undefined;
|
|
380
390
|
|
|
381
391
|
const rest = trimmed.slice("format".length).trim();
|
|
382
392
|
if (!rest || rest.toLowerCase() === "clear") return { value: undefined };
|
|
@@ -437,6 +447,30 @@ function buildItems(
|
|
|
437
447
|
}
|
|
438
448
|
if (section === "layout") {
|
|
439
449
|
return [
|
|
450
|
+
{
|
|
451
|
+
id: "footerStyle",
|
|
452
|
+
label: "Footer content",
|
|
453
|
+
currentValue: config.footerStyle,
|
|
454
|
+
values: ["hud", "hidden"],
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
id: "responsiveFooter",
|
|
458
|
+
label: "Responsive compact",
|
|
459
|
+
currentValue: featureValue(config.responsiveFooter),
|
|
460
|
+
values: featureStateValues,
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
id: "compactFooterMaxLines",
|
|
464
|
+
label: "Compact max lines",
|
|
465
|
+
currentValue: String(config.compactFooterMaxLines),
|
|
466
|
+
values: ["1", "2", "3", "4", "5", "6", "7", "8"],
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
id: "customValueColorMode",
|
|
470
|
+
label: "Custom value colors",
|
|
471
|
+
currentValue: config.customValueColorMode,
|
|
472
|
+
values: ["original", "zentui"],
|
|
473
|
+
},
|
|
440
474
|
{
|
|
441
475
|
id: "contextStyle",
|
|
442
476
|
label: "Context style",
|
|
@@ -454,7 +488,7 @@ function buildItems(
|
|
|
454
488
|
{
|
|
455
489
|
id: "pathDisplay",
|
|
456
490
|
label: "Path display",
|
|
457
|
-
description: "Show cwd as basename
|
|
491
|
+
description: "Show cwd as basename, full path (~), or relative to the repository root.",
|
|
458
492
|
currentValue: config.pathDisplay.mode,
|
|
459
493
|
values: pathDisplayModeValues,
|
|
460
494
|
},
|
|
@@ -462,7 +496,7 @@ function buildItems(
|
|
|
462
496
|
id: "pathDepth",
|
|
463
497
|
label: "Path depth",
|
|
464
498
|
description:
|
|
465
|
-
"In full mode, trailing directories to show (0 = all, max 5). Ignored for basename.",
|
|
499
|
+
"In full/repository mode, trailing directories to show (0 = all, max 5). Ignored for basename.",
|
|
466
500
|
currentValue: String(config.pathDisplay.depth),
|
|
467
501
|
values: [...pathDepthValues],
|
|
468
502
|
},
|
|
@@ -575,6 +609,38 @@ export function registerZentuiSettingsCommand(pi: ExtensionAPI, deps: SettingsCo
|
|
|
575
609
|
handler: async (_args, ctx) => {
|
|
576
610
|
const args = typeof _args === "string" ? _args : "";
|
|
577
611
|
|
|
612
|
+
const initialSection = new Map<string, SettingsSection>([
|
|
613
|
+
["layout", "layout"],
|
|
614
|
+
["extensions", "extensionSegments"],
|
|
615
|
+
["segments", "builtinSegments"],
|
|
616
|
+
]).get(args.trim().toLowerCase());
|
|
617
|
+
|
|
618
|
+
const footerCommand = /^footer\s+(hud|hidden|native)$/i.exec(args.trim());
|
|
619
|
+
const compactCommand = /^compact-format(?:\s|$)/i.test(args.trim())
|
|
620
|
+
? parseFormatCommand(args.trim().replace(/^compact-format/i, "format"))
|
|
621
|
+
: undefined;
|
|
622
|
+
if (footerCommand || compactCommand) {
|
|
623
|
+
try {
|
|
624
|
+
if (footerCommand) {
|
|
625
|
+
const style = footerCommand[1]?.toLowerCase();
|
|
626
|
+
if (style === "hud" || style === "hidden") {
|
|
627
|
+
if (!deps.setFooterOptions) throw new Error("Footer options unavailable");
|
|
628
|
+
deps.setFooterOptions({ footerStyle: style }, ctx);
|
|
629
|
+
}
|
|
630
|
+
deps.setUiFeatures({ statusLine: style !== "native" }, ctx);
|
|
631
|
+
} else if (compactCommand) {
|
|
632
|
+
if (!deps.setFooterOptions) throw new Error("Footer options unavailable");
|
|
633
|
+
deps.setFooterOptions({ compactFooterFormat: compactCommand.value ?? "" }, ctx);
|
|
634
|
+
}
|
|
635
|
+
deps.requestRender();
|
|
636
|
+
if (ctx.hasUI) ctx.ui.notify("Footer settings updated", "info");
|
|
637
|
+
} catch (error) {
|
|
638
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
639
|
+
if (ctx.hasUI) ctx.ui.notify(`Could not update footer settings: ${message}`, "error");
|
|
640
|
+
}
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
578
644
|
const formatCommand = parseFormatCommand(args);
|
|
579
645
|
if (formatCommand) {
|
|
580
646
|
try {
|
|
@@ -630,7 +696,7 @@ export function registerZentuiSettingsCommand(pi: ExtensionAPI, deps: SettingsCo
|
|
|
630
696
|
return;
|
|
631
697
|
}
|
|
632
698
|
|
|
633
|
-
if (args.trim()) {
|
|
699
|
+
if (args.trim() && !initialSection) {
|
|
634
700
|
if (ctx.hasUI) ctx.ui.notify(usageText(), "warning");
|
|
635
701
|
return;
|
|
636
702
|
}
|
|
@@ -640,7 +706,7 @@ export function registerZentuiSettingsCommand(pi: ExtensionAPI, deps: SettingsCo
|
|
|
640
706
|
|
|
641
707
|
await ctx.ui.custom<void>((tui, theme, _keybindings, done) => {
|
|
642
708
|
const settingsListTheme = deps.settingsListTheme ?? getSettingsListTheme();
|
|
643
|
-
let activeSection: SettingsSection = "coloring";
|
|
709
|
+
let activeSection: SettingsSection = initialSection ?? "coloring";
|
|
644
710
|
const applyFeatureChange = (id: FeatureSettingId, newValue: FeatureState) => {
|
|
645
711
|
deps.setUiFeatures(featurePatch(id, newValue), ctx);
|
|
646
712
|
deps.requestRender();
|
|
@@ -670,6 +736,25 @@ export function registerZentuiSettingsCommand(pi: ExtensionAPI, deps: SettingsCo
|
|
|
670
736
|
return;
|
|
671
737
|
}
|
|
672
738
|
|
|
739
|
+
const footerPatch: FooterOptionsPatch | undefined =
|
|
740
|
+
id === "footerStyle" && (newValue === "hud" || newValue === "hidden")
|
|
741
|
+
? { footerStyle: newValue }
|
|
742
|
+
: id === "responsiveFooter" && isFeatureState(newValue)
|
|
743
|
+
? { responsiveFooter: newValue === "enabled" }
|
|
744
|
+
: id === "compactFooterMaxLines" && /^[1-8]$/.test(newValue)
|
|
745
|
+
? { compactFooterMaxLines: Number(newValue) }
|
|
746
|
+
: id === "customValueColorMode" && isExtensionStatusColorMode(newValue)
|
|
747
|
+
? { customValueColorMode: newValue }
|
|
748
|
+
: undefined;
|
|
749
|
+
if (footerPatch) {
|
|
750
|
+
if (!deps.setFooterOptions) throw new Error("Footer options unavailable");
|
|
751
|
+
deps.setFooterOptions(footerPatch, ctx);
|
|
752
|
+
settingsList.updateValue(id, newValue);
|
|
753
|
+
deps.requestRender();
|
|
754
|
+
tui.requestRender();
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
673
758
|
if (isLayoutSettingId(id)) {
|
|
674
759
|
if (id === "contextStyle" && isContextStyle(newValue)) {
|
|
675
760
|
deps.setContextStyle(newValue);
|
|
@@ -50,7 +50,10 @@ export {
|
|
|
50
50
|
|
|
51
51
|
export { ensureConfigExists, loadConfig } from "./file-io.js";
|
|
52
52
|
|
|
53
|
+
export type { FooterOptionsPatch } from "./persist.js";
|
|
54
|
+
|
|
53
55
|
export {
|
|
56
|
+
saveFooterOptionsPatch,
|
|
54
57
|
saveColorSourcesPatch,
|
|
55
58
|
saveContextStylePatch,
|
|
56
59
|
saveContextThresholdsPatch,
|