pi-tui-status-beautifier 1.2.1 → 1.3.1
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 +294 -61
- package/package.json +1 -1
|
@@ -11,9 +11,41 @@ const COMMON_NAMES: Record<string, string> = {
|
|
|
11
11
|
"subagent-slash-text": "subagent",
|
|
12
12
|
"pi-wechat-assistant": "wechat",
|
|
13
13
|
"wechat-assistant": "wechat",
|
|
14
|
+
"pi-control-chrome": "chrome",
|
|
15
|
+
chrome: "chrome",
|
|
16
|
+
toolflow: "toolflow",
|
|
14
17
|
};
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
export interface CustomStyleConfig {
|
|
20
|
+
prefix?: string;
|
|
21
|
+
suffix?: string;
|
|
22
|
+
separator?: string;
|
|
23
|
+
brackets?: [string, string];
|
|
24
|
+
inverse?: boolean;
|
|
25
|
+
glyphs?: {
|
|
26
|
+
success?: string;
|
|
27
|
+
warning?: string;
|
|
28
|
+
error?: string;
|
|
29
|
+
accent?: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const BUILTIN_PRESETS: Record<string, CustomStyleConfig> = {
|
|
34
|
+
minimal: {
|
|
35
|
+
separator: " ❯ ",
|
|
36
|
+
},
|
|
37
|
+
glass: {
|
|
38
|
+
prefix: "▕ ",
|
|
39
|
+
suffix: " ▏",
|
|
40
|
+
separator: " ",
|
|
41
|
+
},
|
|
42
|
+
glow: {
|
|
43
|
+
brackets: ["[", "]"],
|
|
44
|
+
inverse: true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const VALID_STYLES = ["minimal", "glass", "glow", "custom", "off"] as const;
|
|
17
49
|
|
|
18
50
|
// Store original statuses to allow dynamic redrawing on configuration changes.
|
|
19
51
|
const originalStatuses = new Map<string, string | undefined>();
|
|
@@ -27,38 +59,83 @@ const ANSI_STRIP_REGEX =
|
|
|
27
59
|
|
|
28
60
|
// Hoisted regex patterns to prevent instantiation and GC overhead in the render hot-path
|
|
29
61
|
const STATUS_PATTERN =
|
|
30
|
-
/[
|
|
62
|
+
/[🟢🔴🟡⚪⏸✅❌✓✗?✔✖☑☐◆📋⌬★✦◇»«‹›⦗⦘]|[0-9]+\/[0-9]+|active|online|offline|running|paused|error|success|connected|disconnected|ready|idle|ok|done|failed|waiting|stopped|listening|cleanup|starting/i;
|
|
31
63
|
const NAME_SUFFIX_PATTERN = /-(extension|plugin|assistant|adapter|slash-text|slash|text|widget)$/gi;
|
|
32
64
|
const FRACTION_PATTERN = /(\d+\/\d+|\d+%\s*)/;
|
|
33
65
|
const PARENTHESES_PATTERN = /\((\d+)\)/;
|
|
34
66
|
const BRACKET_PATTERN = /\[(\d+)\]/;
|
|
35
67
|
const NUMBER_PATTERN = /\b(\d+)\b/;
|
|
36
68
|
|
|
37
|
-
// Load settings path
|
|
38
|
-
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
39
|
-
const settingsPath = path.join(home, ".pi/agent/settings.json");
|
|
40
|
-
|
|
41
69
|
let currentStyle = "minimal";
|
|
70
|
+
let customStyleConfig: CustomStyleConfig | undefined;
|
|
42
71
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
/**
|
|
73
|
+
* Resolves configuration hierarchically:
|
|
74
|
+
* Project-level `./.pi/settings.json` takes priority over global `~/.pi/agent/settings.json`
|
|
75
|
+
*/
|
|
76
|
+
export function loadConfig(cwd: string = process.cwd()): {
|
|
77
|
+
style: string;
|
|
78
|
+
custom?: CustomStyleConfig;
|
|
79
|
+
targetSettingsPath: string;
|
|
80
|
+
} {
|
|
81
|
+
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
82
|
+
const globalPath = path.join(home, ".pi/agent/settings.json");
|
|
83
|
+
const localPath = path.join(cwd, ".pi/settings.json");
|
|
84
|
+
|
|
85
|
+
let resolvedStyle = "minimal";
|
|
86
|
+
let resolvedCustom: CustomStyleConfig | undefined;
|
|
87
|
+
let hasLocalConfig = false;
|
|
88
|
+
|
|
89
|
+
// 1. Read global settings
|
|
90
|
+
try {
|
|
91
|
+
if (fs.existsSync(globalPath)) {
|
|
92
|
+
const data = JSON.parse(fs.readFileSync(globalPath, "utf-8"));
|
|
93
|
+
if (data.beautifier) {
|
|
94
|
+
if (typeof data.beautifier.style === "string") {
|
|
95
|
+
const s = data.beautifier.style;
|
|
96
|
+
if ((VALID_STYLES as readonly string[]).includes(s)) resolvedStyle = s;
|
|
97
|
+
}
|
|
98
|
+
if (data.beautifier.custom && typeof data.beautifier.custom === "object") {
|
|
99
|
+
resolvedCustom = data.beautifier.custom;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
50
102
|
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
//
|
|
103
|
+
} catch (_) {}
|
|
104
|
+
|
|
105
|
+
// 2. Project-level override (inherits and overrides global)
|
|
106
|
+
try {
|
|
107
|
+
if (fs.existsSync(localPath)) {
|
|
108
|
+
const data = JSON.parse(fs.readFileSync(localPath, "utf-8"));
|
|
109
|
+
if (data.beautifier) {
|
|
110
|
+
hasLocalConfig = true;
|
|
111
|
+
if (typeof data.beautifier.style === "string") {
|
|
112
|
+
const s = data.beautifier.style;
|
|
113
|
+
if ((VALID_STYLES as readonly string[]).includes(s)) resolvedStyle = s;
|
|
114
|
+
}
|
|
115
|
+
if (data.beautifier.custom && typeof data.beautifier.custom === "object") {
|
|
116
|
+
resolvedCustom = { ...(resolvedCustom || {}), ...data.beautifier.custom };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
} catch (_) {}
|
|
121
|
+
|
|
122
|
+
currentStyle = resolvedStyle;
|
|
123
|
+
customStyleConfig = resolvedCustom;
|
|
124
|
+
|
|
125
|
+
// Target path for atomic write: local project if exists or in project directory, otherwise global
|
|
126
|
+
const targetSettingsPath = hasLocalConfig || fs.existsSync(localPath) ? localPath : globalPath;
|
|
127
|
+
return { style: resolvedStyle, custom: resolvedCustom, targetSettingsPath };
|
|
54
128
|
}
|
|
55
129
|
|
|
130
|
+
loadConfig();
|
|
131
|
+
|
|
56
132
|
// Clean status text and map state and details
|
|
57
133
|
export function beautifyStatus(
|
|
58
134
|
key: string,
|
|
59
135
|
originalVal: string | undefined,
|
|
60
136
|
theme: any,
|
|
61
|
-
style: string
|
|
137
|
+
style: string,
|
|
138
|
+
customCfg?: CustomStyleConfig
|
|
62
139
|
): string | undefined {
|
|
63
140
|
if (originalVal === undefined) return undefined;
|
|
64
141
|
if (style === "off") return originalVal;
|
|
@@ -83,6 +160,9 @@ export function beautifyStatus(
|
|
|
83
160
|
name = name.slice(lastSlash + 1);
|
|
84
161
|
}
|
|
85
162
|
name = name.replace(NAME_SUFFIX_PATTERN, "").toLowerCase();
|
|
163
|
+
if (COMMON_NAMES[name]) {
|
|
164
|
+
name = COMMON_NAMES[name];
|
|
165
|
+
}
|
|
86
166
|
}
|
|
87
167
|
const visualName = padToVisualWidth(sliceToVisualWidth(name, 10), 10);
|
|
88
168
|
|
|
@@ -113,7 +193,8 @@ export function beautifyStatus(
|
|
|
113
193
|
valLower.includes("✗") ||
|
|
114
194
|
valLower.includes("offline") ||
|
|
115
195
|
valLower.includes("error") ||
|
|
116
|
-
valLower.includes("failed")
|
|
196
|
+
valLower.includes("failed") ||
|
|
197
|
+
valLower.includes("stopped")
|
|
117
198
|
) {
|
|
118
199
|
state = "error";
|
|
119
200
|
} else if (
|
|
@@ -123,7 +204,9 @@ export function beautifyStatus(
|
|
|
123
204
|
valLower.includes("warning") ||
|
|
124
205
|
valLower.includes("pause") ||
|
|
125
206
|
valLower.includes("planning") ||
|
|
126
|
-
valLower.includes("plan")
|
|
207
|
+
valLower.includes("plan") ||
|
|
208
|
+
valLower.includes("waiting") ||
|
|
209
|
+
valLower.includes("cleanup")
|
|
127
210
|
) {
|
|
128
211
|
state = "warning";
|
|
129
212
|
} else if (
|
|
@@ -131,45 +214,64 @@ export function beautifyStatus(
|
|
|
131
214
|
valLower.includes("✅") ||
|
|
132
215
|
valLower.includes("✓") ||
|
|
133
216
|
valLower.includes("online") ||
|
|
134
|
-
valLower.includes("success")
|
|
217
|
+
valLower.includes("success") ||
|
|
218
|
+
valLower.includes("ready") ||
|
|
219
|
+
valLower.includes("idle") ||
|
|
220
|
+
valLower.includes("ok") ||
|
|
221
|
+
valLower.includes("done")
|
|
135
222
|
) {
|
|
136
223
|
state = "success";
|
|
137
224
|
} else {
|
|
138
225
|
state = name === "plan" ? "accent" : "success";
|
|
139
226
|
}
|
|
140
227
|
|
|
141
|
-
// 4.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
228
|
+
// 4. Determine state glyph & indicator
|
|
229
|
+
const preset =
|
|
230
|
+
style === "custom"
|
|
231
|
+
? (customCfg || customStyleConfig || {})
|
|
232
|
+
: (BUILTIN_PRESETS[style] || BUILTIN_PRESETS.minimal);
|
|
233
|
+
|
|
234
|
+
const glyphMap = preset.glyphs || {};
|
|
235
|
+
let glyph =
|
|
236
|
+
glyphMap[state] ||
|
|
237
|
+
(state === "warning" ? "◌" : state === "error" ? "▲" : state === "accent" ? "◆" : "●");
|
|
145
238
|
|
|
146
239
|
const hasThemeFg = theme && typeof theme.fg === "function";
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
240
|
+
const coloredGlyph = hasThemeFg ? theme.fg(state, glyph) : glyph;
|
|
241
|
+
const dimmedName = hasThemeFg ? theme.fg("muted", visualName) : visualName;
|
|
242
|
+
|
|
243
|
+
const openBracket = preset.brackets?.[0] ?? "";
|
|
244
|
+
const closeBracket = preset.brackets?.[1] ?? "";
|
|
245
|
+
const prefix = preset.prefix ?? "";
|
|
246
|
+
const suffix = preset.suffix ?? "";
|
|
247
|
+
const sep = preset.separator ?? (preset.brackets ? " " : " ❯ ");
|
|
248
|
+
|
|
249
|
+
// Inverse pill badge (glow preset)
|
|
250
|
+
if (preset.inverse && theme && typeof theme.inverse === "function") {
|
|
251
|
+
const pillContent = ` ${glyph}${details ? ` ${details}` : ""} `;
|
|
252
|
+
const coloredBadge = hasThemeFg ? theme.inverse(theme.fg(state, pillContent)) : `[${pillContent}]`;
|
|
253
|
+
return `${prefix}${dimmedName} ${coloredBadge}${suffix}`;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let badge = coloredGlyph;
|
|
257
|
+
if (details) {
|
|
258
|
+
badge = `${coloredGlyph} ${details}`;
|
|
259
|
+
}
|
|
260
|
+
if (openBracket && closeBracket) {
|
|
261
|
+
const styledOpen = hasThemeFg ? theme.fg("dim", openBracket) : openBracket;
|
|
262
|
+
const styledClose = hasThemeFg ? theme.fg("dim", closeBracket) : closeBracket;
|
|
263
|
+
badge = `${styledOpen}${badge}${styledClose}`;
|
|
264
|
+
} else if (details && !preset.brackets) {
|
|
265
|
+
const detailStr = hasThemeFg ? theme.fg("dim", ` (${details})`) : ` (${details})`;
|
|
266
|
+
badge = `${coloredGlyph}${detailStr}`;
|
|
172
267
|
}
|
|
268
|
+
|
|
269
|
+
const styledSep =
|
|
270
|
+
hasThemeFg && (sep.includes("│") || sep.includes("⁝") || sep.includes("❯") || sep.includes("»"))
|
|
271
|
+
? theme.fg("dim", sep)
|
|
272
|
+
: sep;
|
|
273
|
+
|
|
274
|
+
return `${prefix}${dimmedName}${styledSep}${badge}${suffix}`;
|
|
173
275
|
} catch (_error) {
|
|
174
276
|
return originalVal;
|
|
175
277
|
}
|
|
@@ -278,13 +380,111 @@ export function padToVisualWidth(str: string, targetWidth: number, padChar = " "
|
|
|
278
380
|
return str + padChar.repeat(targetWidth - currentWidth);
|
|
279
381
|
}
|
|
280
382
|
|
|
383
|
+
/**
|
|
384
|
+
* Full-Footer Renderer (Hooked via ctx.ui.setFooter)
|
|
385
|
+
* Formats working folder, Git branch, token metrics, and active model on Line 1,
|
|
386
|
+
* and formats all extension statuses on Line 2.
|
|
387
|
+
*/
|
|
388
|
+
export function renderBeautifiedFooter(
|
|
389
|
+
width: number,
|
|
390
|
+
_tui: any,
|
|
391
|
+
theme: any,
|
|
392
|
+
footerData: any,
|
|
393
|
+
ctx: any,
|
|
394
|
+
style: string,
|
|
395
|
+
customCfg?: CustomStyleConfig
|
|
396
|
+
): string[] {
|
|
397
|
+
const preset =
|
|
398
|
+
style === "custom"
|
|
399
|
+
? (customCfg || customStyleConfig || {})
|
|
400
|
+
: (BUILTIN_PRESETS[style] || BUILTIN_PRESETS.minimal);
|
|
401
|
+
|
|
402
|
+
const hasThemeFg = theme && typeof theme.fg === "function";
|
|
403
|
+
const sep = preset.separator || (preset.brackets ? " " : " ❯ ");
|
|
404
|
+
const styledSep =
|
|
405
|
+
hasThemeFg && (sep.includes("│") || sep.includes("⁝") || sep.includes("❯") || sep.includes("»"))
|
|
406
|
+
? theme.fg("dim", sep)
|
|
407
|
+
: sep;
|
|
408
|
+
|
|
409
|
+
// 1. Folder & Git branch
|
|
410
|
+
const branch = typeof footerData?.getGitBranch === "function" ? footerData.getGitBranch() : "";
|
|
411
|
+
const folder = path.basename(ctx?.cwd || process.cwd()) || "workspace";
|
|
412
|
+
const branchPart = branch ? `${folder} (${branch})` : folder;
|
|
413
|
+
const styledBranch = hasThemeFg ? theme.fg("dim", branchPart) : branchPart;
|
|
414
|
+
|
|
415
|
+
// 2. Model
|
|
416
|
+
const modelName = ctx?.model?.name || ctx?.model?.id || "";
|
|
417
|
+
const styledModel = modelName ? (hasThemeFg ? theme.fg("muted", modelName) : modelName) : "";
|
|
418
|
+
|
|
419
|
+
// 3. Token metrics
|
|
420
|
+
let input = 0, output = 0;
|
|
421
|
+
if (ctx?.sessionManager?.getBranch) {
|
|
422
|
+
try {
|
|
423
|
+
for (const e of ctx.sessionManager.getBranch()) {
|
|
424
|
+
if (e.type === "message" && e.message?.role === "assistant" && e.message?.usage) {
|
|
425
|
+
const u = e.message.usage;
|
|
426
|
+
input += u.input || 0;
|
|
427
|
+
output += u.output || 0;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
} catch (_) {}
|
|
431
|
+
}
|
|
432
|
+
const fmt = (n: number) => (n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`);
|
|
433
|
+
const tokenStr = input > 0 || output > 0 ? `↑${fmt(input)} ↓${fmt(output)}` : "";
|
|
434
|
+
const styledTokens = tokenStr ? (hasThemeFg ? theme.fg("dim", tokenStr) : tokenStr) : "";
|
|
435
|
+
|
|
436
|
+
// Left & right parts of Line 1
|
|
437
|
+
const leftText = styledBranch;
|
|
438
|
+
const rightParts = [styledTokens, styledModel].filter(Boolean);
|
|
439
|
+
const rightText = rightParts.join(styledSep);
|
|
440
|
+
|
|
441
|
+
const leftVis = getStringWidth(leftText);
|
|
442
|
+
const rightVis = getStringWidth(rightText);
|
|
443
|
+
const padLen = Math.max(1, width - leftVis - rightVis);
|
|
444
|
+
const line1 = leftText + " ".repeat(padLen) + rightText;
|
|
445
|
+
|
|
446
|
+
const lines = [line1];
|
|
447
|
+
|
|
448
|
+
// Line 2: Extension status badges
|
|
449
|
+
const extStatuses: Array<[string, string]> = [];
|
|
450
|
+
if (footerData && typeof footerData.getExtensionStatuses === "function") {
|
|
451
|
+
try {
|
|
452
|
+
const map = footerData.getExtensionStatuses();
|
|
453
|
+
if (map && typeof map.entries === "function") {
|
|
454
|
+
for (const [k, v] of map.entries()) {
|
|
455
|
+
if (v) extStatuses.push([k, v]);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
} catch (_) {}
|
|
459
|
+
}
|
|
460
|
+
if (extStatuses.length === 0) {
|
|
461
|
+
for (const [k, v] of originalStatuses.entries()) {
|
|
462
|
+
if (v) extStatuses.push([k, v]);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (extStatuses.length > 0) {
|
|
467
|
+
const badges = extStatuses
|
|
468
|
+
.map(([k, v]) => beautifyStatus(k, v, theme, style, customCfg))
|
|
469
|
+
.filter(Boolean) as string[];
|
|
470
|
+
if (badges.length > 0) {
|
|
471
|
+
lines.push(badges.join(" "));
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return lines;
|
|
476
|
+
}
|
|
477
|
+
|
|
281
478
|
export default function (pi: ExtensionAPI) {
|
|
282
479
|
pi.on("session_start", async (_event, ctx) => {
|
|
283
480
|
if (!ctx.hasUI || !ctx.ui) return;
|
|
284
481
|
|
|
482
|
+
loadConfig(ctx.cwd || process.cwd());
|
|
483
|
+
|
|
285
484
|
originalStatuses.clear();
|
|
286
485
|
lastRenderedStatus.clear();
|
|
287
486
|
|
|
487
|
+
// 1. Hook setStatus for granular extension badge formatting
|
|
288
488
|
const originalSetStatus = ctx.ui.setStatus;
|
|
289
489
|
if (originalSetStatus && !(originalSetStatus as any).__beautifierHooked) {
|
|
290
490
|
const wrapped = function (key: string, value: string | undefined) {
|
|
@@ -303,6 +503,25 @@ export default function (pi: ExtensionAPI) {
|
|
|
303
503
|
(wrapped as any).__beautifierHooked = true;
|
|
304
504
|
ctx.ui.setStatus = wrapped;
|
|
305
505
|
}
|
|
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
|
+
}
|
|
306
525
|
});
|
|
307
526
|
|
|
308
527
|
// Dynamic status bar style controller registry
|
|
@@ -329,15 +548,27 @@ export default function (pi: ExtensionAPI) {
|
|
|
329
548
|
|
|
330
549
|
if (styleKey === "custom") {
|
|
331
550
|
const promptRecipe = [
|
|
332
|
-
"You are acting as my Pi TUI Status Bar designer
|
|
333
|
-
"1.
|
|
334
|
-
"2.
|
|
335
|
-
"
|
|
551
|
+
"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. Cyber Powerline ‹●›, Nordic Minimalist │ ◈, Capsule Pill [●], Tokyo Night » ✦) to consult my visual taste.",
|
|
553
|
+
"2. Once I select or describe my aesthetic, save the configuration directly into `./.pi/settings.json` (or `~/.pi/agent/settings.json`) under `beautifier`:",
|
|
554
|
+
" {",
|
|
555
|
+
' "beautifier": {',
|
|
556
|
+
' "style": "custom",',
|
|
557
|
+
' "custom": {',
|
|
558
|
+
' "separator": " » ",',
|
|
559
|
+
' "brackets": ["«", "»"],',
|
|
560
|
+
' "prefix": "✦ ",',
|
|
561
|
+
' "suffix": "",',
|
|
562
|
+
' "glyphs": { "success": "✦", "warning": "◇", "error": "✖", "accent": "✧" }',
|
|
563
|
+
" }",
|
|
564
|
+
" }",
|
|
565
|
+
" }",
|
|
566
|
+
"Do NOT crawl filesystem or edit extension source code files."
|
|
336
567
|
].join("\n");
|
|
337
568
|
|
|
338
569
|
if (typeof (ctx.ui as any).setEditorText === "function") {
|
|
339
570
|
(ctx.ui as any).setEditorText(promptRecipe);
|
|
340
|
-
ctx.ui.notify("Press Enter to let your agent consult your style preference
|
|
571
|
+
ctx.ui.notify("Press Enter to let your agent consult your style preference with visual previews!", "info");
|
|
341
572
|
} else {
|
|
342
573
|
ctx.ui.notify(promptRecipe, "info");
|
|
343
574
|
}
|
|
@@ -349,23 +580,25 @@ export default function (pi: ExtensionAPI) {
|
|
|
349
580
|
lastRenderedStatus.clear();
|
|
350
581
|
ctx.ui.notify(`TUI status style changed to: ${styleKey}`, "info");
|
|
351
582
|
|
|
352
|
-
|
|
353
|
-
ctx.ui.setStatus(key, val);
|
|
354
|
-
}
|
|
583
|
+
const { targetSettingsPath } = loadConfig(ctx.cwd || process.cwd());
|
|
355
584
|
|
|
356
585
|
try {
|
|
357
|
-
if (fs.existsSync(
|
|
358
|
-
const data = fs.readFileSync(
|
|
586
|
+
if (fs.existsSync(targetSettingsPath)) {
|
|
587
|
+
const data = fs.readFileSync(targetSettingsPath, "utf-8");
|
|
359
588
|
const config = JSON.parse(data);
|
|
360
589
|
if (!config.beautifier) config.beautifier = {};
|
|
361
590
|
config.beautifier.style = styleKey;
|
|
362
|
-
const tempSettings = `${
|
|
591
|
+
const tempSettings = `${targetSettingsPath}.tmp.${process.pid}.${Date.now()}`;
|
|
363
592
|
fs.writeFileSync(tempSettings, JSON.stringify(config, null, 2), "utf-8");
|
|
364
|
-
fs.renameSync(tempSettings,
|
|
593
|
+
fs.renameSync(tempSettings, targetSettingsPath);
|
|
365
594
|
}
|
|
366
595
|
} catch (_e) {
|
|
367
596
|
// Silently ignore disk write errors
|
|
368
597
|
}
|
|
598
|
+
|
|
599
|
+
for (const [key, val] of originalStatuses.entries()) {
|
|
600
|
+
ctx.ui.setStatus(key, val);
|
|
601
|
+
}
|
|
369
602
|
}
|
|
370
603
|
},
|
|
371
604
|
});
|
package/package.json
CHANGED