pi-tui-status-beautifier 1.3.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/extensions/tui-status-beautifier.ts +115 -46
- package/package.json +1 -1
|
@@ -22,6 +22,7 @@ export interface CustomStyleConfig {
|
|
|
22
22
|
separator?: string;
|
|
23
23
|
brackets?: [string, string];
|
|
24
24
|
inverse?: boolean;
|
|
25
|
+
padName?: boolean;
|
|
25
26
|
glyphs?: {
|
|
26
27
|
success?: string;
|
|
27
28
|
warning?: string;
|
|
@@ -31,21 +32,52 @@ export interface CustomStyleConfig {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
const BUILTIN_PRESETS: Record<string, CustomStyleConfig> = {
|
|
34
|
-
|
|
35
|
+
stream: {
|
|
35
36
|
separator: " ❯ ",
|
|
36
37
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
powerline: {
|
|
39
|
+
brackets: ["‹", "›"],
|
|
40
|
+
separator: " ",
|
|
41
|
+
glyphs: {
|
|
42
|
+
success: "◆",
|
|
43
|
+
warning: "◌",
|
|
44
|
+
error: "▲",
|
|
45
|
+
accent: "◈",
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
matrix: {
|
|
49
|
+
brackets: ["⦗", "⦘"],
|
|
40
50
|
separator: " ",
|
|
51
|
+
glyphs: {
|
|
52
|
+
success: "✦",
|
|
53
|
+
warning: "◍",
|
|
54
|
+
error: "✖",
|
|
55
|
+
accent: "✧",
|
|
56
|
+
},
|
|
41
57
|
},
|
|
42
58
|
glow: {
|
|
43
59
|
brackets: ["[", "]"],
|
|
44
60
|
inverse: true,
|
|
45
61
|
},
|
|
62
|
+
// Backward-compatible aliases
|
|
63
|
+
minimal: {
|
|
64
|
+
separator: " ❯ ",
|
|
65
|
+
padName: true,
|
|
66
|
+
},
|
|
67
|
+
glass: {
|
|
68
|
+
brackets: ["‹", "›"],
|
|
69
|
+
separator: " ",
|
|
70
|
+
glyphs: {
|
|
71
|
+
success: "◆",
|
|
72
|
+
warning: "◌",
|
|
73
|
+
error: "▲",
|
|
74
|
+
accent: "◈",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
46
77
|
};
|
|
47
78
|
|
|
48
|
-
const VALID_STYLES = ["
|
|
79
|
+
const VALID_STYLES = ["stream", "powerline", "matrix", "glow", "minimal", "glass", "custom", "off"] as const;
|
|
80
|
+
const OFF_STYLES = new Set(["off", "default", "none", "raw", "vanilla", "disable", "disabled"]);
|
|
49
81
|
|
|
50
82
|
// Store original statuses to allow dynamic redrawing on configuration changes.
|
|
51
83
|
const originalStatuses = new Map<string, string | undefined>();
|
|
@@ -92,8 +124,12 @@ export function loadConfig(cwd: string = process.cwd()): {
|
|
|
92
124
|
const data = JSON.parse(fs.readFileSync(globalPath, "utf-8"));
|
|
93
125
|
if (data.beautifier) {
|
|
94
126
|
if (typeof data.beautifier.style === "string") {
|
|
95
|
-
const s = data.beautifier.style;
|
|
96
|
-
if (
|
|
127
|
+
const s = data.beautifier.style.toLowerCase().trim();
|
|
128
|
+
if (OFF_STYLES.has(s)) {
|
|
129
|
+
resolvedStyle = "off";
|
|
130
|
+
} else if ((VALID_STYLES as readonly string[]).includes(s)) {
|
|
131
|
+
resolvedStyle = s;
|
|
132
|
+
}
|
|
97
133
|
}
|
|
98
134
|
if (data.beautifier.custom && typeof data.beautifier.custom === "object") {
|
|
99
135
|
resolvedCustom = data.beautifier.custom;
|
|
@@ -109,8 +145,12 @@ export function loadConfig(cwd: string = process.cwd()): {
|
|
|
109
145
|
if (data.beautifier) {
|
|
110
146
|
hasLocalConfig = true;
|
|
111
147
|
if (typeof data.beautifier.style === "string") {
|
|
112
|
-
const s = data.beautifier.style;
|
|
113
|
-
if (
|
|
148
|
+
const s = data.beautifier.style.toLowerCase().trim();
|
|
149
|
+
if (OFF_STYLES.has(s)) {
|
|
150
|
+
resolvedStyle = "off";
|
|
151
|
+
} else if ((VALID_STYLES as readonly string[]).includes(s)) {
|
|
152
|
+
resolvedStyle = s;
|
|
153
|
+
}
|
|
114
154
|
}
|
|
115
155
|
if (data.beautifier.custom && typeof data.beautifier.custom === "object") {
|
|
116
156
|
resolvedCustom = { ...(resolvedCustom || {}), ...data.beautifier.custom };
|
|
@@ -138,7 +178,7 @@ export function beautifyStatus(
|
|
|
138
178
|
customCfg?: CustomStyleConfig
|
|
139
179
|
): string | undefined {
|
|
140
180
|
if (originalVal === undefined) return undefined;
|
|
141
|
-
if (style === "off") return originalVal;
|
|
181
|
+
if (style === "off" || OFF_STYLES.has((style || "").toLowerCase().trim())) return originalVal;
|
|
142
182
|
|
|
143
183
|
try {
|
|
144
184
|
const hasAnsi = originalVal.includes("\u001B") || originalVal.includes("\u009B");
|
|
@@ -150,7 +190,7 @@ export function beautifyStatus(
|
|
|
150
190
|
return originalVal;
|
|
151
191
|
}
|
|
152
192
|
|
|
153
|
-
// 1. Get display name
|
|
193
|
+
// 1. Get display name (compact natural width by default to save precious terminal columns)
|
|
154
194
|
let name = key.toLowerCase();
|
|
155
195
|
if (COMMON_NAMES[name]) {
|
|
156
196
|
name = COMMON_NAMES[name];
|
|
@@ -164,7 +204,14 @@ export function beautifyStatus(
|
|
|
164
204
|
name = COMMON_NAMES[name];
|
|
165
205
|
}
|
|
166
206
|
}
|
|
167
|
-
const
|
|
207
|
+
const preset =
|
|
208
|
+
style === "custom"
|
|
209
|
+
? (customCfg || customStyleConfig || {})
|
|
210
|
+
: (BUILTIN_PRESETS[style] || BUILTIN_PRESETS.stream);
|
|
211
|
+
|
|
212
|
+
const visualName = preset.padName === true
|
|
213
|
+
? padToVisualWidth(sliceToVisualWidth(name, 10), 10)
|
|
214
|
+
: name;
|
|
168
215
|
|
|
169
216
|
// 2. Extract metrics/progress details safely (e.g. (2) or fraction 2/5 or count)
|
|
170
217
|
let details: string | undefined;
|
|
@@ -179,6 +226,14 @@ export function beautifyStatus(
|
|
|
179
226
|
const numberMatch = cleanVal.match(NUMBER_PATTERN);
|
|
180
227
|
if (numberMatch) {
|
|
181
228
|
details = numberMatch[1];
|
|
229
|
+
} else {
|
|
230
|
+
// If no numeric metric exists, extract the status word descriptor (e.g. "ready", "idle", "cleanup")
|
|
231
|
+
const wordMatch = cleanVal.match(
|
|
232
|
+
/(?::\s*|\b)(ready|idle|ok|done|failed|waiting|cleanup|active|online|offline|running|paused|error|success|connected|disconnected)\b/i
|
|
233
|
+
);
|
|
234
|
+
if (wordMatch) {
|
|
235
|
+
details = wordMatch[1].toLowerCase();
|
|
236
|
+
}
|
|
182
237
|
}
|
|
183
238
|
}
|
|
184
239
|
}
|
|
@@ -226,11 +281,6 @@ export function beautifyStatus(
|
|
|
226
281
|
}
|
|
227
282
|
|
|
228
283
|
// 4. Determine state glyph & indicator
|
|
229
|
-
const preset =
|
|
230
|
-
style === "custom"
|
|
231
|
-
? (customCfg || customStyleConfig || {})
|
|
232
|
-
: (BUILTIN_PRESETS[style] || BUILTIN_PRESETS.minimal);
|
|
233
|
-
|
|
234
284
|
const glyphMap = preset.glyphs || {};
|
|
235
285
|
let glyph =
|
|
236
286
|
glyphMap[state] ||
|
|
@@ -484,7 +534,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
484
534
|
originalStatuses.clear();
|
|
485
535
|
lastRenderedStatus.clear();
|
|
486
536
|
|
|
487
|
-
//
|
|
537
|
+
// Hook setStatus for clean status badge formatting (never hijack Pi's native footer)
|
|
488
538
|
const originalSetStatus = ctx.ui.setStatus;
|
|
489
539
|
if (originalSetStatus && !(originalSetStatus as any).__beautifierHooked) {
|
|
490
540
|
const wrapped = function (key: string, value: string | undefined) {
|
|
@@ -503,25 +553,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
503
553
|
(wrapped as any).__beautifierHooked = true;
|
|
504
554
|
ctx.ui.setStatus = wrapped;
|
|
505
555
|
}
|
|
506
|
-
|
|
507
|
-
// 2. Hook setFooter for full-footer beautification
|
|
508
|
-
if (typeof (ctx.ui as any).setFooter === "function") {
|
|
509
|
-
(ctx.ui as any).setFooter((tui: any, theme: any, footerData: any) => {
|
|
510
|
-
const unsub =
|
|
511
|
-
typeof footerData?.onBranchChange === "function"
|
|
512
|
-
? footerData.onBranchChange(() => tui.requestRender())
|
|
513
|
-
: undefined;
|
|
514
|
-
|
|
515
|
-
return {
|
|
516
|
-
dispose: unsub,
|
|
517
|
-
invalidate() {},
|
|
518
|
-
render(width: number): string[] {
|
|
519
|
-
if (currentStyle === "off") return [];
|
|
520
|
-
return renderBeautifiedFooter(width, tui, theme, footerData, ctx, currentStyle, customStyleConfig);
|
|
521
|
-
},
|
|
522
|
-
};
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
556
|
});
|
|
526
557
|
|
|
527
558
|
// Dynamic status bar style controller registry
|
|
@@ -533,11 +564,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
533
564
|
}
|
|
534
565
|
|
|
535
566
|
const styleOptions = [
|
|
536
|
-
"
|
|
537
|
-
"
|
|
538
|
-
"
|
|
539
|
-
"glow
|
|
540
|
-
"
|
|
567
|
+
"stream - Clean chevron stream (e.g. name ❯ ● ready)",
|
|
568
|
+
"powerline - Angled brackets & diamonds (e.g. name ‹◆ ready›)",
|
|
569
|
+
"matrix - Dot-matrix brackets & stars (e.g. name ⦗✦ ready⦘)",
|
|
570
|
+
"glow - High-contrast inverse pill (e.g. name [ ● ready ])",
|
|
571
|
+
"custom - [AI Prompt Recipe] Let your agent tailor your bespoke style",
|
|
572
|
+
"off - Revert to native Pi status bar & clean settings",
|
|
541
573
|
];
|
|
542
574
|
|
|
543
575
|
const choice = await ctx.ui.select(`Choose status style (Current: ${currentStyle}):`, styleOptions);
|
|
@@ -546,24 +578,61 @@ export default function (pi: ExtensionAPI) {
|
|
|
546
578
|
|
|
547
579
|
const styleKey = choice.split(" ")[0].trim();
|
|
548
580
|
|
|
581
|
+
if (styleKey === "off") {
|
|
582
|
+
currentStyle = "off";
|
|
583
|
+
customStyleConfig = undefined;
|
|
584
|
+
lastRenderedStatus.clear();
|
|
585
|
+
|
|
586
|
+
// Restore all raw original statuses immediately
|
|
587
|
+
for (const [key, val] of originalStatuses.entries()) {
|
|
588
|
+
ctx.ui.setStatus(key, val);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Zero-trace physical cleanup: completely prune the `beautifier` key from settings.json!
|
|
592
|
+
const { targetSettingsPath } = loadConfig(ctx.cwd || process.cwd());
|
|
593
|
+
try {
|
|
594
|
+
if (fs.existsSync(targetSettingsPath)) {
|
|
595
|
+
const data = fs.readFileSync(targetSettingsPath, "utf-8");
|
|
596
|
+
const config = JSON.parse(data);
|
|
597
|
+
if (config.beautifier) {
|
|
598
|
+
delete config.beautifier;
|
|
599
|
+
const tempSettings = `${targetSettingsPath}.tmp.${process.pid}.${Date.now()}`;
|
|
600
|
+
fs.writeFileSync(tempSettings, JSON.stringify(config, null, 2), "utf-8");
|
|
601
|
+
fs.renameSync(tempSettings, targetSettingsPath);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
} catch (_e) {
|
|
605
|
+
// Silently ignore disk write errors
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
ctx.ui.notify("Reverted to native Pi status bar. Cleaned up settings.json.", "info");
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
549
612
|
if (styleKey === "custom") {
|
|
550
613
|
const promptRecipe = [
|
|
551
614
|
"You are acting as my Pi TUI Status Bar designer for `pi-tui-status-beautifier`.",
|
|
552
|
-
"1. In my primary language, call `ask_user_question` with ASCII visual preview mockups (e.g.
|
|
553
|
-
"
|
|
615
|
+
"1. In my primary language, call `ask_user_question` with ASCII visual preview mockups showing ONLY realistic extension statuses (e.g. `chrome`, `toolflow`, `plan`):",
|
|
616
|
+
" - Powerline: chrome ‹◆ ready› | toolflow ‹◈ 2/5›",
|
|
617
|
+
" - Matrix: chrome ⦗✦ ready⦘ | toolflow ⦗◍ 2/5⦘",
|
|
618
|
+
" - Stream: chrome ❯ ● ready | toolflow ❯ ◐ 2/5",
|
|
619
|
+
" - Glow: chrome [ ● ready ] | toolflow [ ◐ 2/5 ]",
|
|
620
|
+
" (Do NOT include git branch or model name in mockups).",
|
|
621
|
+
"2. If I describe or pick a style, write the configuration into `./.pi/settings.json` (or `~/.pi/agent/settings.json`) under `beautifier`:",
|
|
554
622
|
" {",
|
|
555
623
|
' "beautifier": {',
|
|
556
624
|
' "style": "custom",',
|
|
557
625
|
' "custom": {',
|
|
558
626
|
' "separator": " » ",',
|
|
559
627
|
' "brackets": ["«", "»"],',
|
|
560
|
-
' "prefix": "
|
|
628
|
+
' "prefix": "",',
|
|
561
629
|
' "suffix": "",',
|
|
562
630
|
' "glyphs": { "success": "✦", "warning": "◇", "error": "✖", "accent": "✧" }',
|
|
563
631
|
" }",
|
|
564
632
|
" }",
|
|
565
633
|
" }",
|
|
566
|
-
"
|
|
634
|
+
"3. If I say revert, restore, default, off, or cancel, DELETE the `beautifier` key from `settings.json` and exit quietly without asking more questions.",
|
|
635
|
+
"4. Do NOT search filesystem or edit extension source files."
|
|
567
636
|
].join("\n");
|
|
568
637
|
|
|
569
638
|
if (typeof (ctx.ui as any).setEditorText === "function") {
|
package/package.json
CHANGED