pi-zentui 0.8.0 → 0.9.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/README.md
CHANGED
|
@@ -157,6 +157,7 @@ Default config values — copy this and change any value you want:
|
|
|
157
157
|
{
|
|
158
158
|
"projectRefreshIntervalMs": 30000,
|
|
159
159
|
"footerFormat": "",
|
|
160
|
+
"separator": "pipe",
|
|
160
161
|
"contextStyle": "text",
|
|
161
162
|
"contextThresholds": {
|
|
162
163
|
"warning": 70,
|
|
@@ -272,13 +273,14 @@ Default config values — copy this and change any value you want:
|
|
|
272
273
|
- Style values can be Starship/terminal strings (`bold purple`, `fg:202`, `#89b` / `#89b4fa`, `bg:blue fg:bright-green`) or Pi theme tokens (`accent`, `borderMuted`, `thinkingHigh`). Short `#rgb` hex values expand to `#rrggbb`.
|
|
273
274
|
- `projectRefreshIntervalMs`: project status polling interval; `0` disables polling. Values `1..4999` clamp up to `5000` (minimum 5s); invalid/non-finite values fall back to `30000`.
|
|
274
275
|
- `contextStyle`: `text` (default), `gauge`, or `text+gauge` for the context segment.
|
|
276
|
+
- `separator`: controls the default footer layout and extension-status connectors: `pipe` (default, ` | `), `dot` (` · `), `chevron` (` › `), or `none` (one space). Cycle it from the `/zentui` **Layout** tab. This selects the separator glyph; `colors.separator` controls its color. Custom `footerFormat` literals and `$sep` keep their existing behavior.
|
|
275
277
|
- `contextThresholds`: `{ warning, error }` percentages (default `70` / `90`) that select contextNormal / contextWarning / contextError colors.
|
|
276
278
|
- `pathDisplay`: controls how the cwd/`$cwd` path is shown. `mode` is `basename` (default, last segment only) or `full` (path with home contracted to `~`). In `full` mode, `depth` keeps only the last N trailing directories (`0` = entire path after `~`, max `5`); when parents are dropped the path is prefixed with `…/` (Starship-style). The `/zentui` **Layout** tab cycles path mode and path depth (`0`–`5`; depth is ignored for basename). Example: `~/Projects/foo/bar` with `depth: 2` → `…/foo/bar`.
|
|
277
279
|
- `icons`: every shown icon key is configurable; omit any key to use the Zentui default. `icons.mode` is `auto` | `nerd` | `ascii` (default `auto`, same glyphs as nerd). ASCII mode swaps in plain fallbacks for statusline icons and runtime symbols — useful without a Nerd Font. Custom per-icon strings always win over mode defaults. Custom `icons.os` always wins; when left at the mode default, Zentui maps the OS icon by platform. `rail` sets the vertical glyph drawn as the left rail of the active editor frame and previous user messages when `copyFriendly` is disabled (default `│`; any single Unicode vertical or block glyph). `editorPrompt` controls an optional copy-friendly editor prompt glyph; the default is `""` so copy-friendly mode stays rail-free.
|
|
278
280
|
- `colorSources`: `theme` maps styles through Pi theme tokens; `terminal` emits terminal colors. `/zentui` switches these sources; manual JSON controls specific style values.
|
|
279
281
|
- `features`: `editor` enables Zentui's custom editor, selector borders, and previous-message chrome. `statusLine` enables Zentui's custom footer/status line. `copyFriendly` hides editor and previous-message rail glyphs so native terminal selection copies less chrome. All three can be changed from `/zentui` or direct slash-command arguments.
|
|
280
282
|
- `footerSegments`: show or hide individual built-in footer segments (`cwd`, `gitBranch`, `gitStatus`, `gitCounts`, `gitCommit`, `gitMetrics`, `runtime`, `packageVersion`, `sessionDuration`, `username`, `time`, `os`, `context`, `tokens`, `cost`). Toggle them from the `Built-in segments` tab in `/zentui`.
|
|
281
|
-
- `footerFormat`: optional Starship-style template string that fully controls the footer layout. When set, it overrides `footerSegments`. See [Footer Format Template](#footer-format-template) below. The `/zentui` **Layout** tab configures context style, path display mode/depth, and icon mode; set or clear custom formats with `/zentui format`.
|
|
283
|
+
- `footerFormat`: optional Starship-style template string that fully controls the footer layout. When set, it overrides `footerSegments`. See [Footer Format Template](#footer-format-template) below. The `/zentui` **Layout** tab configures context style, separator, path display mode/depth, and icon mode; set or clear custom formats with `/zentui format`.
|
|
282
284
|
- `gitCommit`: Starship [`git_commit`](https://starship.rs/config/#git-commit)-style options for the `gitCommit` footer segment. `hashLength` (default `7`, clamped to `4`–`40`) controls the short-hash display length. `onlyDetached` (default `true`) shows the hash mainly on detached HEAD. `showTag` (default `true`) appends an exact-match tag (`git describe --tags --exact-match HEAD`). The tag probe piggybacks on the existing git refresh — it only runs when both the segment and `showTag` are on, and misses/failures degrade silently.
|
|
283
285
|
- `gitMetrics`: Starship [`git_metrics`](https://starship.rs/config/#git-metrics)-style options for the `gitMetrics` footer segment. Uses `git diff HEAD --numstat` (staged + unstaged combined — the Starship “total dirty” view) to show aggregate `+added −deleted` line counts. `onlyNonzero` (default `true`) omits each zero component independently and hides the segment entirely at `0/0`. `ignoreSubmodules` (default `false`) adds `--ignore-submodules=all`. The numstat diff piggybacks on the existing git refresh and uses a hard 2s timeout; a metrics-only failure degrades silently without discarding fresh branch/status data. On very large monorepos the diff may lag or be omitted on timeout.
|
|
284
286
|
- `extensionStatuses`: controls third-party statuses published by other Pi extensions through `ctx.ui.setStatus()`. `defaultPlacement` and each `placements` value can be `off`, `left`, `middle`, or `right`. The `Extension segments` tab in `/zentui` lists only statuses that are currently active.
|
|
@@ -17,6 +17,7 @@ export type ColorSource = "theme" | "terminal";
|
|
|
17
17
|
export type { IconMode } from "./icons";
|
|
18
18
|
|
|
19
19
|
export type ContextStyle = "text" | "gauge" | "text+gauge";
|
|
20
|
+
export type SeparatorStyle = "pipe" | "dot" | "chevron" | "none";
|
|
20
21
|
|
|
21
22
|
export type ContextThresholds = {
|
|
22
23
|
warning: number;
|
|
@@ -103,6 +104,7 @@ const MIN_PROJECT_REFRESH_INTERVAL_MS = 5_000;
|
|
|
103
104
|
export type PolishedTuiConfig = {
|
|
104
105
|
projectRefreshIntervalMs: number;
|
|
105
106
|
footerFormat: string;
|
|
107
|
+
separator: SeparatorStyle;
|
|
106
108
|
contextStyle: ContextStyle;
|
|
107
109
|
contextThresholds: ContextThresholds;
|
|
108
110
|
pathDisplay: PathDisplayConfig;
|
|
@@ -195,6 +197,7 @@ export const configPath = join(getAgentDir(), "zentui.json");
|
|
|
195
197
|
export const defaultConfig: PolishedTuiConfig = {
|
|
196
198
|
projectRefreshIntervalMs: DEFAULT_PROJECT_REFRESH_INTERVAL_MS,
|
|
197
199
|
footerFormat: "",
|
|
200
|
+
separator: "pipe",
|
|
198
201
|
contextStyle: "text",
|
|
199
202
|
contextThresholds: { warning: 70, error: 90 },
|
|
200
203
|
pathDisplay: { mode: "basename", depth: 0 },
|
|
@@ -297,6 +300,14 @@ function parseContextStyle(value: unknown): ContextStyle {
|
|
|
297
300
|
return defaultConfig.contextStyle;
|
|
298
301
|
}
|
|
299
302
|
|
|
303
|
+
export function isSeparatorStyle(value: unknown): value is SeparatorStyle {
|
|
304
|
+
return value === "pipe" || value === "dot" || value === "chevron" || value === "none";
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function parseSeparatorStyle(value: unknown): SeparatorStyle {
|
|
308
|
+
return isSeparatorStyle(value) ? value : defaultConfig.separator;
|
|
309
|
+
}
|
|
310
|
+
|
|
300
311
|
function parseContextThresholds(value: unknown): ContextThresholds {
|
|
301
312
|
const defaults = defaultConfig.contextThresholds;
|
|
302
313
|
if (!isRecord(value)) return { ...defaults };
|
|
@@ -639,6 +650,7 @@ export function mergeConfig(parsed: unknown): PolishedTuiConfig {
|
|
|
639
650
|
return {
|
|
640
651
|
projectRefreshIntervalMs: parseProjectRefreshIntervalMs(config.projectRefreshIntervalMs),
|
|
641
652
|
footerFormat: stringValue(config, "footerFormat") ?? "",
|
|
653
|
+
separator: parseSeparatorStyle(config.separator),
|
|
642
654
|
contextStyle: parseContextStyle(config.contextStyle),
|
|
643
655
|
contextThresholds: parseContextThresholds(config.contextThresholds),
|
|
644
656
|
pathDisplay: parsePathDisplay(config.pathDisplay),
|
|
@@ -757,6 +769,16 @@ export function saveContextStylePatch(style: ContextStyle, path = configPath): P
|
|
|
757
769
|
return mergeConfig(record);
|
|
758
770
|
}
|
|
759
771
|
|
|
772
|
+
export function saveSeparatorPatch(
|
|
773
|
+
separator: SeparatorStyle,
|
|
774
|
+
path = configPath,
|
|
775
|
+
): PolishedTuiConfig {
|
|
776
|
+
const record = readConfigRecord(path);
|
|
777
|
+
record.separator = parseSeparatorStyle(separator);
|
|
778
|
+
writeFileSync(path, `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
779
|
+
return mergeConfig(record);
|
|
780
|
+
}
|
|
781
|
+
|
|
760
782
|
export function saveContextThresholdsPatch(
|
|
761
783
|
thresholds: Partial<ContextThresholds>,
|
|
762
784
|
path = configPath,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
|
-
import type { PolishedTuiConfig } from "./config";
|
|
3
|
+
import type { PolishedTuiConfig, SeparatorStyle } from "./config";
|
|
4
4
|
import { FOOTER_FORMAT_ALIASES } from "./config";
|
|
5
5
|
import { collectExtensionStatusSegments, type ExtensionStatusSegment } from "./extension-status";
|
|
6
6
|
import { parseFooterFormat, renderFormatSplit, stripOrphanSeparators } from "./footer-format";
|
|
@@ -21,6 +21,13 @@ import { resolveRuntimeSymbol } from "./icons";
|
|
|
21
21
|
import type { FooterState } from "./state";
|
|
22
22
|
import { renderStyleForSource } from "./style";
|
|
23
23
|
|
|
24
|
+
const separatorText: Record<SeparatorStyle, string> = {
|
|
25
|
+
pipe: " | ",
|
|
26
|
+
dot: " · ",
|
|
27
|
+
chevron: " › ",
|
|
28
|
+
none: " ",
|
|
29
|
+
};
|
|
30
|
+
|
|
24
31
|
function joinStatusTexts(statusTexts: string[], separator: string): string {
|
|
25
32
|
return statusTexts.filter(Boolean).join(separator);
|
|
26
33
|
}
|
|
@@ -169,7 +176,12 @@ export function installFooter(
|
|
|
169
176
|
const config = getConfig();
|
|
170
177
|
const colorSource = config.colorSources.starship;
|
|
171
178
|
const iconMode = config.icons.mode;
|
|
172
|
-
const separator = renderStyleForSource(
|
|
179
|
+
const separator = renderStyleForSource(
|
|
180
|
+
theme,
|
|
181
|
+
colorSource,
|
|
182
|
+
config.colors.separator,
|
|
183
|
+
separatorText[config.separator],
|
|
184
|
+
);
|
|
173
185
|
const innerWidth = Math.max(1, width - 2);
|
|
174
186
|
const cwdLabel = renderStyleForSource(
|
|
175
187
|
theme,
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
loadConfig,
|
|
18
18
|
type PathDisplayConfig,
|
|
19
19
|
type PolishedTuiConfig,
|
|
20
|
+
type SeparatorStyle,
|
|
20
21
|
saveColorSourcesPatch,
|
|
21
22
|
saveContextStylePatch,
|
|
22
23
|
saveExtensionStatusColorMode,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
saveFooterSegmentsPatch,
|
|
27
28
|
saveIconsModePatch,
|
|
28
29
|
savePathDisplayPatch,
|
|
30
|
+
saveSeparatorPatch,
|
|
29
31
|
saveUiFeaturesPatch,
|
|
30
32
|
type UiFeaturesConfig,
|
|
31
33
|
} from "./config";
|
|
@@ -449,6 +451,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
449
451
|
setContextStyle(style: ContextStyle) {
|
|
450
452
|
currentConfig = saveContextStylePatch(style);
|
|
451
453
|
},
|
|
454
|
+
setSeparator(separator: SeparatorStyle) {
|
|
455
|
+
currentConfig = saveSeparatorPatch(separator);
|
|
456
|
+
},
|
|
452
457
|
setPathDisplay(patch: Partial<PathDisplayConfig>) {
|
|
453
458
|
currentConfig = savePathDisplayPatch(patch);
|
|
454
459
|
},
|
|
@@ -22,9 +22,11 @@ import {
|
|
|
22
22
|
type IconMode,
|
|
23
23
|
isExtensionStatusColorMode,
|
|
24
24
|
isExtensionStatusPlacement,
|
|
25
|
+
isSeparatorStyle,
|
|
25
26
|
type PathDisplayConfig,
|
|
26
27
|
type PathDisplayMode,
|
|
27
28
|
type PolishedTuiConfig,
|
|
29
|
+
type SeparatorStyle,
|
|
28
30
|
type UiFeaturesConfig,
|
|
29
31
|
} from "./config";
|
|
30
32
|
import { sanitizeExtensionStatusText } from "./extension-status";
|
|
@@ -40,6 +42,7 @@ const extensionStatusPlacementValues: ExtensionStatusPlacement[] = [
|
|
|
40
42
|
];
|
|
41
43
|
const extensionStatusColorModeValues: ExtensionStatusColorMode[] = ["zentui", "original"];
|
|
42
44
|
const contextStyleValues: ContextStyle[] = ["text", "gauge", "text+gauge"];
|
|
45
|
+
const separatorStyleValues: SeparatorStyle[] = ["pipe", "dot", "chevron", "none"];
|
|
43
46
|
const pathDisplayModeValues: PathDisplayMode[] = ["basename", "full"];
|
|
44
47
|
const pathDepthValues = ["0", "1", "2", "3", "4", "5"] as const;
|
|
45
48
|
const iconModeValues: IconMode[] = ["auto", "nerd", "ascii"];
|
|
@@ -58,7 +61,7 @@ type ColorSettingId = "starship" | "editorMessages";
|
|
|
58
61
|
type FeatureSettingId = keyof UiFeaturesConfig;
|
|
59
62
|
type FooterSegmentSettingId = keyof FooterSegmentsConfig;
|
|
60
63
|
type SettingsSection = (typeof settingsSections)[number];
|
|
61
|
-
type LayoutSettingId = "contextStyle" | "pathDisplay" | "pathDepth" | "iconMode";
|
|
64
|
+
type LayoutSettingId = "contextStyle" | "separator" | "pathDisplay" | "pathDepth" | "iconMode";
|
|
62
65
|
|
|
63
66
|
type SettingsCommandDeps = {
|
|
64
67
|
getConfig: () => PolishedTuiConfig;
|
|
@@ -71,6 +74,7 @@ type SettingsCommandDeps = {
|
|
|
71
74
|
setFooterFormat: (value: string) => void;
|
|
72
75
|
setIconMode: (mode: IconMode) => void;
|
|
73
76
|
setContextStyle: (style: ContextStyle) => void;
|
|
77
|
+
setSeparator: (separator: SeparatorStyle) => void;
|
|
74
78
|
setPathDisplay: (patch: Partial<PathDisplayConfig>) => void;
|
|
75
79
|
getActiveExtensionStatuses: () => ReadonlyMap<string, string>;
|
|
76
80
|
setExtensionStatusPlacement: (key: string, placement: ExtensionStatusPlacement) => void;
|
|
@@ -227,6 +231,7 @@ function isPathDepthValue(value: string): boolean {
|
|
|
227
231
|
function isLayoutSettingId(value: string): value is LayoutSettingId {
|
|
228
232
|
return (
|
|
229
233
|
value === "contextStyle" ||
|
|
234
|
+
value === "separator" ||
|
|
230
235
|
value === "pathDisplay" ||
|
|
231
236
|
value === "pathDepth" ||
|
|
232
237
|
value === "iconMode"
|
|
@@ -437,6 +442,13 @@ function buildItems(
|
|
|
437
442
|
currentValue: config.contextStyle,
|
|
438
443
|
values: contextStyleValues,
|
|
439
444
|
},
|
|
445
|
+
{
|
|
446
|
+
id: "separator",
|
|
447
|
+
label: "Separator",
|
|
448
|
+
description: "Choose the separator between default footer segments.",
|
|
449
|
+
currentValue: config.separator,
|
|
450
|
+
values: separatorStyleValues,
|
|
451
|
+
},
|
|
440
452
|
{
|
|
441
453
|
id: "pathDisplay",
|
|
442
454
|
label: "Path display",
|
|
@@ -679,6 +691,15 @@ export function registerZentuiSettingsCommand(pi: ExtensionAPI, deps: SettingsCo
|
|
|
679
691
|
return;
|
|
680
692
|
}
|
|
681
693
|
|
|
694
|
+
if (id === "separator" && isSeparatorStyle(newValue)) {
|
|
695
|
+
deps.setSeparator(newValue);
|
|
696
|
+
settingsList.updateValue(id, newValue);
|
|
697
|
+
deps.requestRender();
|
|
698
|
+
ctx.ui.notify(`Separator: ${newValue}`, "info");
|
|
699
|
+
tui.requestRender();
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
|
|
682
703
|
if (id === "pathDisplay" && isPathDisplayMode(newValue)) {
|
|
683
704
|
deps.setPathDisplay({ mode: newValue });
|
|
684
705
|
settingsList.updateValue(id, newValue);
|