pi-zentui 0.8.0 → 0.10.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,
|
|
@@ -166,6 +167,9 @@ Default config values — copy this and change any value you want:
|
|
|
166
167
|
"mode": "basename",
|
|
167
168
|
"depth": 0
|
|
168
169
|
},
|
|
170
|
+
"gitBranch": {
|
|
171
|
+
"maxLength": "full"
|
|
172
|
+
},
|
|
169
173
|
"icons": {
|
|
170
174
|
"mode": "auto",
|
|
171
175
|
"cwd": "",
|
|
@@ -272,13 +276,15 @@ Default config values — copy this and change any value you want:
|
|
|
272
276
|
- 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
277
|
- `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
278
|
- `contextStyle`: `text` (default), `gauge`, or `text+gauge` for the context segment.
|
|
279
|
+
- `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
280
|
- `contextThresholds`: `{ warning, error }` percentages (default `70` / `90`) that select contextNormal / contextWarning / contextError colors.
|
|
276
281
|
- `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`.
|
|
282
|
+
- `gitBranch.maxLength`: visible width of the built-in branch name and `$git_branch` / `$branch`. The default `full` preserves the complete name; any positive integer uses that width including the trailing `…`. `/zentui` **Layout** cycles `full`, `10`, `20`, `30`, `40`, and `50`; custom positive integers can be set in JSON.
|
|
277
283
|
- `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
284
|
- `colorSources`: `theme` maps styles through Pi theme tokens; `terminal` emits terminal colors. `/zentui` switches these sources; manual JSON controls specific style values.
|
|
279
285
|
- `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
286
|
- `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`.
|
|
287
|
+
- `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, branch length, and icon mode; set or clear custom formats with `/zentui format`.
|
|
282
288
|
- `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
289
|
- `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
290
|
- `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;
|
|
@@ -31,6 +32,12 @@ export type PathDisplayConfig = {
|
|
|
31
32
|
depth: number;
|
|
32
33
|
};
|
|
33
34
|
|
|
35
|
+
export type GitBranchMaxLength = "full" | number;
|
|
36
|
+
|
|
37
|
+
export type GitBranchConfig = {
|
|
38
|
+
maxLength: GitBranchMaxLength;
|
|
39
|
+
};
|
|
40
|
+
|
|
34
41
|
export type ColorSourcesConfig = {
|
|
35
42
|
starship: ColorSource;
|
|
36
43
|
editor: ColorSource;
|
|
@@ -103,9 +110,11 @@ const MIN_PROJECT_REFRESH_INTERVAL_MS = 5_000;
|
|
|
103
110
|
export type PolishedTuiConfig = {
|
|
104
111
|
projectRefreshIntervalMs: number;
|
|
105
112
|
footerFormat: string;
|
|
113
|
+
separator: SeparatorStyle;
|
|
106
114
|
contextStyle: ContextStyle;
|
|
107
115
|
contextThresholds: ContextThresholds;
|
|
108
116
|
pathDisplay: PathDisplayConfig;
|
|
117
|
+
gitBranch: GitBranchConfig;
|
|
109
118
|
icons: ResolvedIcons;
|
|
110
119
|
colors: {
|
|
111
120
|
cwd: ColorSpec;
|
|
@@ -195,9 +204,11 @@ export const configPath = join(getAgentDir(), "zentui.json");
|
|
|
195
204
|
export const defaultConfig: PolishedTuiConfig = {
|
|
196
205
|
projectRefreshIntervalMs: DEFAULT_PROJECT_REFRESH_INTERVAL_MS,
|
|
197
206
|
footerFormat: "",
|
|
207
|
+
separator: "pipe",
|
|
198
208
|
contextStyle: "text",
|
|
199
209
|
contextThresholds: { warning: 70, error: 90 },
|
|
200
210
|
pathDisplay: { mode: "basename", depth: 0 },
|
|
211
|
+
gitBranch: { maxLength: "full" },
|
|
201
212
|
icons: {
|
|
202
213
|
mode: "auto",
|
|
203
214
|
...NERD_DEFAULT_ICONS,
|
|
@@ -297,6 +308,14 @@ function parseContextStyle(value: unknown): ContextStyle {
|
|
|
297
308
|
return defaultConfig.contextStyle;
|
|
298
309
|
}
|
|
299
310
|
|
|
311
|
+
export function isSeparatorStyle(value: unknown): value is SeparatorStyle {
|
|
312
|
+
return value === "pipe" || value === "dot" || value === "chevron" || value === "none";
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function parseSeparatorStyle(value: unknown): SeparatorStyle {
|
|
316
|
+
return isSeparatorStyle(value) ? value : defaultConfig.separator;
|
|
317
|
+
}
|
|
318
|
+
|
|
300
319
|
function parseContextThresholds(value: unknown): ContextThresholds {
|
|
301
320
|
const defaults = defaultConfig.contextThresholds;
|
|
302
321
|
if (!isRecord(value)) return { ...defaults };
|
|
@@ -331,6 +350,20 @@ function parsePathDisplay(value: unknown): PathDisplayConfig {
|
|
|
331
350
|
return { mode, depth };
|
|
332
351
|
}
|
|
333
352
|
|
|
353
|
+
function normalizeGitBranchMaxLength(value: unknown): GitBranchMaxLength {
|
|
354
|
+
if (value === "full") return value;
|
|
355
|
+
if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
|
|
356
|
+
return defaultConfig.gitBranch.maxLength;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function parseGitBranchConfig(value: unknown): GitBranchConfig {
|
|
360
|
+
const defaults = defaultConfig.gitBranch;
|
|
361
|
+
if (!isRecord(value)) return { ...defaults };
|
|
362
|
+
return {
|
|
363
|
+
maxLength: normalizeGitBranchMaxLength(value.maxLength),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
334
367
|
function stringValue(record: Record<string, unknown>, key: string): string | undefined {
|
|
335
368
|
const value = record[key];
|
|
336
369
|
return typeof value === "string" ? value : undefined;
|
|
@@ -633,15 +666,18 @@ export function mergeConfig(parsed: unknown): PolishedTuiConfig {
|
|
|
633
666
|
const gitMetrics = isRecord(config.gitMetrics)
|
|
634
667
|
? normalizeGitMetricsConfig(config.gitMetrics as Record<string, unknown>)
|
|
635
668
|
: defaultConfig.gitMetrics;
|
|
669
|
+
const gitBranch = parseGitBranchConfig(config.gitBranch);
|
|
636
670
|
const fixedEditor = isRecord(config.fixedEditor)
|
|
637
671
|
? normalizeFixedEditorConfig(config.fixedEditor as Record<string, unknown>)
|
|
638
672
|
: defaultConfig.fixedEditor;
|
|
639
673
|
return {
|
|
640
674
|
projectRefreshIntervalMs: parseProjectRefreshIntervalMs(config.projectRefreshIntervalMs),
|
|
641
675
|
footerFormat: stringValue(config, "footerFormat") ?? "",
|
|
676
|
+
separator: parseSeparatorStyle(config.separator),
|
|
642
677
|
contextStyle: parseContextStyle(config.contextStyle),
|
|
643
678
|
contextThresholds: parseContextThresholds(config.contextThresholds),
|
|
644
679
|
pathDisplay: parsePathDisplay(config.pathDisplay),
|
|
680
|
+
gitBranch,
|
|
645
681
|
icons: resolveConfiguredIcons(iconMode, iconOverrides),
|
|
646
682
|
colors: {
|
|
647
683
|
...defaultConfig.colors,
|
|
@@ -757,6 +793,16 @@ export function saveContextStylePatch(style: ContextStyle, path = configPath): P
|
|
|
757
793
|
return mergeConfig(record);
|
|
758
794
|
}
|
|
759
795
|
|
|
796
|
+
export function saveSeparatorPatch(
|
|
797
|
+
separator: SeparatorStyle,
|
|
798
|
+
path = configPath,
|
|
799
|
+
): PolishedTuiConfig {
|
|
800
|
+
const record = readConfigRecord(path);
|
|
801
|
+
record.separator = parseSeparatorStyle(separator);
|
|
802
|
+
writeFileSync(path, `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
803
|
+
return mergeConfig(record);
|
|
804
|
+
}
|
|
805
|
+
|
|
760
806
|
export function saveContextThresholdsPatch(
|
|
761
807
|
thresholds: Partial<ContextThresholds>,
|
|
762
808
|
path = configPath,
|
|
@@ -788,6 +834,21 @@ export function savePathDisplayPatch(
|
|
|
788
834
|
return mergeConfig(record);
|
|
789
835
|
}
|
|
790
836
|
|
|
837
|
+
export function saveGitBranchPatch(
|
|
838
|
+
patch: Partial<GitBranchConfig>,
|
|
839
|
+
path = configPath,
|
|
840
|
+
): PolishedTuiConfig {
|
|
841
|
+
const record = readConfigRecord(path);
|
|
842
|
+
const existing = isRecord(record.gitBranch)
|
|
843
|
+
? { ...(record.gitBranch as Record<string, unknown>) }
|
|
844
|
+
: {};
|
|
845
|
+
if (patch.maxLength !== undefined)
|
|
846
|
+
existing.maxLength = normalizeGitBranchMaxLength(patch.maxLength);
|
|
847
|
+
record.gitBranch = existing;
|
|
848
|
+
writeFileSync(path, `${JSON.stringify(record, null, 2)}\n`, "utf8");
|
|
849
|
+
return mergeConfig(record);
|
|
850
|
+
}
|
|
851
|
+
|
|
791
852
|
export function saveExtensionStatusPlacement(
|
|
792
853
|
key: string,
|
|
793
854
|
placement: ExtensionStatusPlacement,
|
|
@@ -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";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
buildSessionDurationLabel,
|
|
10
10
|
contextColorTier,
|
|
11
11
|
formatCwdLabel,
|
|
12
|
+
formatGitBranchText,
|
|
12
13
|
formatGitCommitSegment,
|
|
13
14
|
formatGitMetricsSegment,
|
|
14
15
|
formatOsLabel,
|
|
@@ -21,6 +22,13 @@ import { resolveRuntimeSymbol } from "./icons";
|
|
|
21
22
|
import type { FooterState } from "./state";
|
|
22
23
|
import { renderStyleForSource } from "./style";
|
|
23
24
|
|
|
25
|
+
const separatorText: Record<SeparatorStyle, string> = {
|
|
26
|
+
pipe: " | ",
|
|
27
|
+
dot: " · ",
|
|
28
|
+
chevron: " › ",
|
|
29
|
+
none: " ",
|
|
30
|
+
};
|
|
31
|
+
|
|
24
32
|
function joinStatusTexts(statusTexts: string[], separator: string): string {
|
|
25
33
|
return statusTexts.filter(Boolean).join(separator);
|
|
26
34
|
}
|
|
@@ -169,7 +177,12 @@ export function installFooter(
|
|
|
169
177
|
const config = getConfig();
|
|
170
178
|
const colorSource = config.colorSources.starship;
|
|
171
179
|
const iconMode = config.icons.mode;
|
|
172
|
-
const separator = renderStyleForSource(
|
|
180
|
+
const separator = renderStyleForSource(
|
|
181
|
+
theme,
|
|
182
|
+
colorSource,
|
|
183
|
+
config.colors.separator,
|
|
184
|
+
separatorText[config.separator],
|
|
185
|
+
);
|
|
173
186
|
const innerWidth = Math.max(1, width - 2);
|
|
174
187
|
const cwdLabel = renderStyleForSource(
|
|
175
188
|
theme,
|
|
@@ -181,6 +194,9 @@ export function installFooter(
|
|
|
181
194
|
}),
|
|
182
195
|
);
|
|
183
196
|
const branch = state.branch;
|
|
197
|
+
const branchText = branch
|
|
198
|
+
? formatGitBranchText(branch, config.gitBranch.maxLength)
|
|
199
|
+
: undefined;
|
|
184
200
|
const contextUsage = ctx.getContextUsage();
|
|
185
201
|
const contextWindow = ctx.model?.contextWindow ?? contextUsage?.contextWindow;
|
|
186
202
|
const contextLabel = buildContextDisplayLabel({
|
|
@@ -240,7 +256,11 @@ export function installFooter(
|
|
|
240
256
|
case "cwd":
|
|
241
257
|
return cwdLabel;
|
|
242
258
|
case "git_branch":
|
|
243
|
-
return
|
|
259
|
+
return branchText
|
|
260
|
+
? gitIcon
|
|
261
|
+
? `${gitIcon} ${gitColor(branchText)}`
|
|
262
|
+
: gitColor(branchText)
|
|
263
|
+
: "";
|
|
244
264
|
case "git_status":
|
|
245
265
|
return statusBlock;
|
|
246
266
|
case "git_state":
|
|
@@ -366,8 +386,8 @@ export function installFooter(
|
|
|
366
386
|
};
|
|
367
387
|
const branchParts: string[] = [];
|
|
368
388
|
if (config.footerSegments.gitBranch) {
|
|
369
|
-
if (
|
|
370
|
-
branchParts.push("on", gitIcon, gitColor(
|
|
389
|
+
if (branchText) {
|
|
390
|
+
branchParts.push("on", gitIcon, gitColor(branchText));
|
|
371
391
|
} else if (state.commit?.detached) {
|
|
372
392
|
// `HEAD` uses git-branch style; `(hash)` uses git-commit style
|
|
373
393
|
// (bold green) per Starship `git_commit` format.
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { homedir, hostname, userInfo } from "node:os";
|
|
2
2
|
import type { AssistantMessage } from "@earendil-works/pi-ai";
|
|
3
3
|
import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
4
5
|
import type {
|
|
5
6
|
ColorSource,
|
|
6
7
|
ColorSpec,
|
|
7
8
|
ContextStyle,
|
|
8
9
|
ContextThresholds,
|
|
10
|
+
GitBranchMaxLength,
|
|
9
11
|
PathDisplayMode,
|
|
10
12
|
} from "./config";
|
|
11
13
|
import type { GitCommitInfo, GitMetricsInfo } from "./git";
|
|
@@ -391,6 +393,18 @@ export function formatCwdLabel(cwd: string, cwdIcon: string, options?: FormatCwd
|
|
|
391
393
|
return cwdIcon ? `${cwdIcon} ${pathText}` : pathText;
|
|
392
394
|
}
|
|
393
395
|
|
|
396
|
+
function stripAnsi(text: string): string {
|
|
397
|
+
return text.replace(/\u001B\[[0-9;]*m/g, "");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function formatGitBranchText(
|
|
401
|
+
branch: string,
|
|
402
|
+
maxLength: GitBranchMaxLength = "full",
|
|
403
|
+
): string {
|
|
404
|
+
if (maxLength === "full" || visibleWidth(branch) <= maxLength) return branch;
|
|
405
|
+
return stripAnsi(truncateToWidth(branch, maxLength, "…"));
|
|
406
|
+
}
|
|
407
|
+
|
|
394
408
|
export function formatUsernameHostLabel(icon: string): string {
|
|
395
409
|
try {
|
|
396
410
|
const user = userInfo().username;
|
|
@@ -13,10 +13,12 @@ import {
|
|
|
13
13
|
ensureConfigExists,
|
|
14
14
|
type FixedEditorConfig,
|
|
15
15
|
type FooterSegmentsConfig,
|
|
16
|
+
type GitBranchConfig,
|
|
16
17
|
type IconMode,
|
|
17
18
|
loadConfig,
|
|
18
19
|
type PathDisplayConfig,
|
|
19
20
|
type PolishedTuiConfig,
|
|
21
|
+
type SeparatorStyle,
|
|
20
22
|
saveColorSourcesPatch,
|
|
21
23
|
saveContextStylePatch,
|
|
22
24
|
saveExtensionStatusColorMode,
|
|
@@ -24,8 +26,10 @@ import {
|
|
|
24
26
|
saveFixedEditorPatch,
|
|
25
27
|
saveFooterFormatPatch,
|
|
26
28
|
saveFooterSegmentsPatch,
|
|
29
|
+
saveGitBranchPatch,
|
|
27
30
|
saveIconsModePatch,
|
|
28
31
|
savePathDisplayPatch,
|
|
32
|
+
saveSeparatorPatch,
|
|
29
33
|
saveUiFeaturesPatch,
|
|
30
34
|
type UiFeaturesConfig,
|
|
31
35
|
} from "./config";
|
|
@@ -449,9 +453,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
449
453
|
setContextStyle(style: ContextStyle) {
|
|
450
454
|
currentConfig = saveContextStylePatch(style);
|
|
451
455
|
},
|
|
456
|
+
setSeparator(separator: SeparatorStyle) {
|
|
457
|
+
currentConfig = saveSeparatorPatch(separator);
|
|
458
|
+
},
|
|
452
459
|
setPathDisplay(patch: Partial<PathDisplayConfig>) {
|
|
453
460
|
currentConfig = savePathDisplayPatch(patch);
|
|
454
461
|
},
|
|
462
|
+
setGitBranch(patch: Partial<GitBranchConfig>) {
|
|
463
|
+
currentConfig = saveGitBranchPatch(patch);
|
|
464
|
+
},
|
|
455
465
|
getActiveExtensionStatuses() {
|
|
456
466
|
return getActiveExtensionStatuses();
|
|
457
467
|
},
|
|
@@ -17,14 +17,18 @@ import {
|
|
|
17
17
|
type ExtensionStatusPlacement,
|
|
18
18
|
type FixedEditorConfig,
|
|
19
19
|
type FooterSegmentsConfig,
|
|
20
|
+
type GitBranchConfig,
|
|
21
|
+
type GitBranchMaxLength,
|
|
20
22
|
getExtensionStatusColorMode,
|
|
21
23
|
getExtensionStatusPlacement,
|
|
22
24
|
type IconMode,
|
|
23
25
|
isExtensionStatusColorMode,
|
|
24
26
|
isExtensionStatusPlacement,
|
|
27
|
+
isSeparatorStyle,
|
|
25
28
|
type PathDisplayConfig,
|
|
26
29
|
type PathDisplayMode,
|
|
27
30
|
type PolishedTuiConfig,
|
|
31
|
+
type SeparatorStyle,
|
|
28
32
|
type UiFeaturesConfig,
|
|
29
33
|
} from "./config";
|
|
30
34
|
import { sanitizeExtensionStatusText } from "./extension-status";
|
|
@@ -40,8 +44,10 @@ const extensionStatusPlacementValues: ExtensionStatusPlacement[] = [
|
|
|
40
44
|
];
|
|
41
45
|
const extensionStatusColorModeValues: ExtensionStatusColorMode[] = ["zentui", "original"];
|
|
42
46
|
const contextStyleValues: ContextStyle[] = ["text", "gauge", "text+gauge"];
|
|
47
|
+
const separatorStyleValues: SeparatorStyle[] = ["pipe", "dot", "chevron", "none"];
|
|
43
48
|
const pathDisplayModeValues: PathDisplayMode[] = ["basename", "full"];
|
|
44
49
|
const pathDepthValues = ["0", "1", "2", "3", "4", "5"] as const;
|
|
50
|
+
const branchLengthPresetValues = ["full", "10", "20", "30", "40", "50"] as const;
|
|
45
51
|
const iconModeValues: IconMode[] = ["auto", "nerd", "ascii"];
|
|
46
52
|
type FeatureState = "enabled" | "disabled";
|
|
47
53
|
|
|
@@ -58,7 +64,13 @@ type ColorSettingId = "starship" | "editorMessages";
|
|
|
58
64
|
type FeatureSettingId = keyof UiFeaturesConfig;
|
|
59
65
|
type FooterSegmentSettingId = keyof FooterSegmentsConfig;
|
|
60
66
|
type SettingsSection = (typeof settingsSections)[number];
|
|
61
|
-
type LayoutSettingId =
|
|
67
|
+
type LayoutSettingId =
|
|
68
|
+
| "contextStyle"
|
|
69
|
+
| "separator"
|
|
70
|
+
| "pathDisplay"
|
|
71
|
+
| "pathDepth"
|
|
72
|
+
| "branchLength"
|
|
73
|
+
| "iconMode";
|
|
62
74
|
|
|
63
75
|
type SettingsCommandDeps = {
|
|
64
76
|
getConfig: () => PolishedTuiConfig;
|
|
@@ -71,7 +83,9 @@ type SettingsCommandDeps = {
|
|
|
71
83
|
setFooterFormat: (value: string) => void;
|
|
72
84
|
setIconMode: (mode: IconMode) => void;
|
|
73
85
|
setContextStyle: (style: ContextStyle) => void;
|
|
86
|
+
setSeparator: (separator: SeparatorStyle) => void;
|
|
74
87
|
setPathDisplay: (patch: Partial<PathDisplayConfig>) => void;
|
|
88
|
+
setGitBranch: (patch: Partial<GitBranchConfig>) => void;
|
|
75
89
|
getActiveExtensionStatuses: () => ReadonlyMap<string, string>;
|
|
76
90
|
setExtensionStatusPlacement: (key: string, placement: ExtensionStatusPlacement) => void;
|
|
77
91
|
setExtensionStatusColorMode: (key: string, colorMode: ExtensionStatusColorMode) => void;
|
|
@@ -224,11 +238,26 @@ function isPathDepthValue(value: string): boolean {
|
|
|
224
238
|
return (pathDepthValues as readonly string[]).includes(value);
|
|
225
239
|
}
|
|
226
240
|
|
|
241
|
+
function parseGitBranchLengthValue(value: string): GitBranchMaxLength | undefined {
|
|
242
|
+
if (value === "full") return value;
|
|
243
|
+
const parsed = Number(value);
|
|
244
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function branchLengthValues(maxLength: GitBranchMaxLength): string[] {
|
|
248
|
+
const current = String(maxLength);
|
|
249
|
+
return (branchLengthPresetValues as readonly string[]).includes(current)
|
|
250
|
+
? [...branchLengthPresetValues]
|
|
251
|
+
: [current, ...branchLengthPresetValues];
|
|
252
|
+
}
|
|
253
|
+
|
|
227
254
|
function isLayoutSettingId(value: string): value is LayoutSettingId {
|
|
228
255
|
return (
|
|
229
256
|
value === "contextStyle" ||
|
|
257
|
+
value === "separator" ||
|
|
230
258
|
value === "pathDisplay" ||
|
|
231
259
|
value === "pathDepth" ||
|
|
260
|
+
value === "branchLength" ||
|
|
232
261
|
value === "iconMode"
|
|
233
262
|
);
|
|
234
263
|
}
|
|
@@ -437,6 +466,13 @@ function buildItems(
|
|
|
437
466
|
currentValue: config.contextStyle,
|
|
438
467
|
values: contextStyleValues,
|
|
439
468
|
},
|
|
469
|
+
{
|
|
470
|
+
id: "separator",
|
|
471
|
+
label: "Separator",
|
|
472
|
+
description: "Choose the separator between default footer segments.",
|
|
473
|
+
currentValue: config.separator,
|
|
474
|
+
values: separatorStyleValues,
|
|
475
|
+
},
|
|
440
476
|
{
|
|
441
477
|
id: "pathDisplay",
|
|
442
478
|
label: "Path display",
|
|
@@ -452,6 +488,13 @@ function buildItems(
|
|
|
452
488
|
currentValue: String(config.pathDisplay.depth),
|
|
453
489
|
values: [...pathDepthValues],
|
|
454
490
|
},
|
|
491
|
+
{
|
|
492
|
+
id: "branchLength",
|
|
493
|
+
label: "Branch length",
|
|
494
|
+
description: "Show the full branch name or truncate it to a preset visible width.",
|
|
495
|
+
currentValue: String(config.gitBranch.maxLength),
|
|
496
|
+
values: branchLengthValues(config.gitBranch.maxLength),
|
|
497
|
+
},
|
|
455
498
|
{
|
|
456
499
|
id: "iconMode",
|
|
457
500
|
label: "Icon mode",
|
|
@@ -679,6 +722,15 @@ export function registerZentuiSettingsCommand(pi: ExtensionAPI, deps: SettingsCo
|
|
|
679
722
|
return;
|
|
680
723
|
}
|
|
681
724
|
|
|
725
|
+
if (id === "separator" && isSeparatorStyle(newValue)) {
|
|
726
|
+
deps.setSeparator(newValue);
|
|
727
|
+
settingsList.updateValue(id, newValue);
|
|
728
|
+
deps.requestRender();
|
|
729
|
+
ctx.ui.notify(`Separator: ${newValue}`, "info");
|
|
730
|
+
tui.requestRender();
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
|
|
682
734
|
if (id === "pathDisplay" && isPathDisplayMode(newValue)) {
|
|
683
735
|
deps.setPathDisplay({ mode: newValue });
|
|
684
736
|
settingsList.updateValue(id, newValue);
|
|
@@ -697,6 +749,17 @@ export function registerZentuiSettingsCommand(pi: ExtensionAPI, deps: SettingsCo
|
|
|
697
749
|
return;
|
|
698
750
|
}
|
|
699
751
|
|
|
752
|
+
if (id === "branchLength") {
|
|
753
|
+
const maxLength = parseGitBranchLengthValue(newValue);
|
|
754
|
+
if (maxLength === undefined) return;
|
|
755
|
+
deps.setGitBranch({ maxLength });
|
|
756
|
+
settingsList.updateValue(id, newValue);
|
|
757
|
+
deps.requestRender();
|
|
758
|
+
ctx.ui.notify(`Branch length: ${newValue}`, "info");
|
|
759
|
+
tui.requestRender();
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
|
|
700
763
|
if (id === "iconMode" && isIconMode(newValue)) {
|
|
701
764
|
deps.setIconMode(newValue);
|
|
702
765
|
settingsList.updateValue(id, newValue);
|