pi-tui-status-beautifier 1.1.3 β 1.2.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/README.md +17 -31
- package/extensions/tui-status-beautifier.ts +70 -144
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,45 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# pi-tui-status-beautifier
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Minimalist status bar formatter & AI-customizable layout engine for the [Pi](https://github.com/earendil-works/pi-coding-agent) terminal TUI.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Automatically normalizes verbose extension status strings (`plannotator`, `pi-mcp-adapter`, `pi-subagents`, `toolflow`, etc.) into clean, visually aligned 10-column status badges with zero runtime disk I/O overhead.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
pi install npm:pi-tui-status-beautifier
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
pi install git:github.com/3ZEROS12/pi-tui-status-beautifier
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
### Enable/Disable Styles
|
|
19
|
-
Once installed, open the Pi TUI terminal and run the dynamic interactive switch command:
|
|
20
|
-
```bash
|
|
21
|
-
/beautify
|
|
22
|
-
```
|
|
23
|
-
This renders a live selectable menu to instantly toggle between brand themes (`Cupertino`, `OpenAI`, `Claude`, `Microsoft Fluent`, `Material`) and layout styles (`Minimal`, `Glass`, `Glow badge`, `Matrix frame`, or `Default/Off`).
|
|
24
|
-
|
|
25
|
-
---
|
|
13
|
+
## Usage
|
|
26
14
|
|
|
27
|
-
|
|
15
|
+
Run `/beautify` inside Pi to switch status bar layouts or generate a custom style with your AI agent:
|
|
28
16
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
- **`custom` β [AI Prompt Recipe]**: Pre-fills a structured prompt into your terminal editor so your coding agent can inspect `tui-status-beautifier.ts` and craft a bespoke status bar layout tailored to your exact aesthetic.
|
|
18
|
+
- **`minimal`**: Clean chevron indicator (`name β― β (2)`)
|
|
19
|
+
- **`glass`**: Framed capsule badge (`β name β (2) β`)
|
|
20
|
+
- **`glow`**: High-contrast inverse pill (`name [ β 2 ]`)
|
|
21
|
+
- **`off`**: Pass through raw status strings untouched
|
|
33
22
|
|
|
34
|
-
##
|
|
23
|
+
## Engineering Highlights
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* **Fluent Microsoft (`microsoft`)**: Flat terminal tile layout using Microsoft Fluent block `β`.
|
|
40
|
-
* **Material Google (`google`)**: Google multi-color letters rendering index name values.
|
|
25
|
+
- **Unicode & Surrogate-Pair Safe**: Full `codePointAt(0)` visual cell width measurement across CJK ideographs, Hiragana/Katakana, Hangul, emojis, and zero-width joiners (`0x200d`, `0xfe0f`).
|
|
26
|
+
- **Zero Render-Loop Disk I/O**: Reads `~/.pi/agent/settings.json` once at startup and performs dirty-checked in-memory rendering; atomic temp-file rename (`writeFileSync` + `renameSync`) only when switching styles via `/beautify`.
|
|
27
|
+
- **ReDoS-Safe ANSI Stripping**: Fast-path bypass when no ESC/CSI bytes are present.
|
|
41
28
|
|
|
42
|
-
|
|
29
|
+
## License
|
|
43
30
|
|
|
44
|
-
|
|
45
|
-
The core logic resides in `extensions/tui-status-beautifier.ts`. All status mapping is completely memory-cached to eliminate Paint Loop I/O bottlenecks.
|
|
31
|
+
MIT
|
|
@@ -13,13 +13,11 @@ const COMMON_NAMES: Record<string, string> = {
|
|
|
13
13
|
"wechat-assistant": "wechat",
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
const VALID_STYLES = ["minimal", "glass", "glow", "off"] as const;
|
|
17
|
+
|
|
16
18
|
// Store original statuses to allow dynamic redrawing on configuration changes.
|
|
17
|
-
// Cleaned up on session restart to prevent memory leaks from replaced sessions.
|
|
18
19
|
const originalStatuses = new Map<string, string | undefined>();
|
|
19
20
|
|
|
20
|
-
// Memoization cache for Google colorized text to reduce GC overhead and theme calls.
|
|
21
|
-
const googleColorizeCache = new Map<string, string>();
|
|
22
|
-
|
|
23
21
|
// State Cache for Dirty-Checking
|
|
24
22
|
const lastRenderedStatus = new Map<string, string | undefined>();
|
|
25
23
|
|
|
@@ -29,7 +27,7 @@ const ANSI_STRIP_REGEX =
|
|
|
29
27
|
|
|
30
28
|
// Hoisted regex patterns to prevent instantiation and GC overhead in the render hot-path
|
|
31
29
|
const STATUS_PATTERN =
|
|
32
|
-
/[
|
|
30
|
+
/[π’π΄π‘βͺβΈβ
βββ?βββββπβ¬]|[0-9]+\/[0-9]+|active|online|offline|running|paused|error|success|connected|disconnected/i;
|
|
33
31
|
const NAME_SUFFIX_PATTERN = /-(extension|plugin|assistant|adapter|slash-text|slash|text|widget)$/gi;
|
|
34
32
|
const FRACTION_PATTERN = /(\d+\/\d+|\d+%\s*)/;
|
|
35
33
|
const PARENTHESES_PATTERN = /\((\d+)\)/;
|
|
@@ -40,9 +38,6 @@ const NUMBER_PATTERN = /\b(\d+)\b/;
|
|
|
40
38
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
41
39
|
const settingsPath = path.join(home, ".pi/agent/settings.json");
|
|
42
40
|
|
|
43
|
-
// Command-Driven I/O State:
|
|
44
|
-
// Loaded ONCE synchronously during extension loading, thereafter handled entirely in memory.
|
|
45
|
-
// Disk I/O only occurs when the user triggers the "/beautify" selection command.
|
|
46
41
|
let currentStyle = "minimal";
|
|
47
42
|
|
|
48
43
|
try {
|
|
@@ -50,34 +45,14 @@ try {
|
|
|
50
45
|
const data = fs.readFileSync(settingsPath, "utf-8");
|
|
51
46
|
const config = JSON.parse(data);
|
|
52
47
|
if (config.beautifier && typeof config.beautifier.style === "string") {
|
|
53
|
-
|
|
48
|
+
const saved = config.beautifier.style;
|
|
49
|
+
currentStyle = (VALID_STYLES as readonly string[]).includes(saved) ? saved : "minimal";
|
|
54
50
|
}
|
|
55
51
|
}
|
|
56
|
-
} catch (
|
|
52
|
+
} catch (_e) {
|
|
57
53
|
// Graceful fallback to default in case of JSON parse or read errors
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
// Google playful multi-color letter colorizer with type safeguards and memoization
|
|
61
|
-
export function googleColorize(text: string, theme: any): string {
|
|
62
|
-
if (!theme || typeof theme.fg !== "function") return text;
|
|
63
|
-
|
|
64
|
-
const cached = googleColorizeCache.get(text);
|
|
65
|
-
if (cached !== undefined) return cached;
|
|
66
|
-
|
|
67
|
-
// Google's brand colors cycle: Blue, Red, Yellow, Green
|
|
68
|
-
const colors = ["accent", "error", "warning", "success"];
|
|
69
|
-
const colorized = text
|
|
70
|
-
.split("")
|
|
71
|
-
.map((char, index) => {
|
|
72
|
-
const color = colors[index % colors.length];
|
|
73
|
-
return theme.fg(color, char);
|
|
74
|
-
})
|
|
75
|
-
.join("");
|
|
76
|
-
|
|
77
|
-
googleColorizeCache.set(text, colorized);
|
|
78
|
-
return colorized;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
56
|
// Clean status text and map state and details
|
|
82
57
|
export function beautifyStatus(
|
|
83
58
|
key: string,
|
|
@@ -86,16 +61,14 @@ export function beautifyStatus(
|
|
|
86
61
|
style: string
|
|
87
62
|
): string | undefined {
|
|
88
63
|
if (originalVal === undefined) return undefined;
|
|
89
|
-
if (style === "off") return originalVal;
|
|
64
|
+
if (style === "off") return originalVal;
|
|
90
65
|
|
|
91
66
|
try {
|
|
92
|
-
// Fast-path: bypass Regex search if no ESC or CSI bytes exist to reduce hot redraw latency
|
|
93
67
|
const hasAnsi = originalVal.includes("\u001B") || originalVal.includes("\u009B");
|
|
94
68
|
const cleanVal = hasAnsi ? originalVal.replace(ANSI_STRIP_REGEX, "").trim() : originalVal.trim();
|
|
95
69
|
|
|
96
70
|
if (cleanVal === "") return "";
|
|
97
71
|
|
|
98
|
-
// Safely bypass values that have no status signifiers (such as logs or text widgets)
|
|
99
72
|
if (!STATUS_PATTERN.test(cleanVal)) {
|
|
100
73
|
return originalVal;
|
|
101
74
|
}
|
|
@@ -111,7 +84,6 @@ export function beautifyStatus(
|
|
|
111
84
|
}
|
|
112
85
|
name = name.replace(NAME_SUFFIX_PATTERN, "").toLowerCase();
|
|
113
86
|
}
|
|
114
|
-
// Optimization (Finding 5): Use visual cell width calculations rather than standard .length for CJK/emojis
|
|
115
87
|
const visualName = padToVisualWidth(sliceToVisualWidth(name, 10), 10);
|
|
116
88
|
|
|
117
89
|
// 2. Extract metrics/progress details safely (e.g. (2) or fraction 2/5 or count)
|
|
@@ -141,7 +113,6 @@ export function beautifyStatus(
|
|
|
141
113
|
valLower.includes("β") ||
|
|
142
114
|
valLower.includes("offline") ||
|
|
143
115
|
valLower.includes("error") ||
|
|
144
|
-
valLower.includes("ζͺη»ε½") ||
|
|
145
116
|
valLower.includes("failed")
|
|
146
117
|
) {
|
|
147
118
|
state = "error";
|
|
@@ -160,8 +131,7 @@ export function beautifyStatus(
|
|
|
160
131
|
valLower.includes("β
") ||
|
|
161
132
|
valLower.includes("β") ||
|
|
162
133
|
valLower.includes("online") ||
|
|
163
|
-
valLower.includes("success")
|
|
164
|
-
valLower.includes("ε·²θΏζ₯")
|
|
134
|
+
valLower.includes("success")
|
|
165
135
|
) {
|
|
166
136
|
state = "success";
|
|
167
137
|
} else {
|
|
@@ -176,39 +146,8 @@ export function beautifyStatus(
|
|
|
176
146
|
const hasThemeFg = theme && typeof theme.fg === "function";
|
|
177
147
|
const coloredIndicator = hasThemeFg ? theme.fg(state, char) : char;
|
|
178
148
|
|
|
179
|
-
// 5. Render layout presets
|
|
149
|
+
// 5. Render layout presets (3 curated baseline layouts: minimal, glass, glow)
|
|
180
150
|
switch (style) {
|
|
181
|
-
case "apple": {
|
|
182
|
-
const separator = hasThemeFg ? theme.fg("dim", " β ") : " β ";
|
|
183
|
-
const dimmedName = hasThemeFg ? theme.fg("dim", visualName) : visualName;
|
|
184
|
-
const detailStr = details ? ` (${details})` : "";
|
|
185
|
-
return `${dimmedName}${separator}${coloredIndicator}${detailStr}`;
|
|
186
|
-
}
|
|
187
|
-
case "openai": {
|
|
188
|
-
const spirograph = hasThemeFg ? theme.fg("success", "β") : "β";
|
|
189
|
-
const detailStr = details ? ` [${details}]` : "";
|
|
190
|
-
const dimmedName = hasThemeFg ? theme.fg("muted", visualName) : visualName;
|
|
191
|
-
return `${spirograph} ${dimmedName}${detailStr}`;
|
|
192
|
-
}
|
|
193
|
-
case "anthropic": {
|
|
194
|
-
const organicStar = "β¦";
|
|
195
|
-
const coloredStar = hasThemeFg ? theme.fg(state, organicStar) : organicStar;
|
|
196
|
-
const dimmedName = hasThemeFg ? theme.fg("muted", visualName) : visualName;
|
|
197
|
-
const detailStr = details ? ` β ${details}` : "";
|
|
198
|
-
return `${dimmedName} ${coloredStar}${detailStr}`;
|
|
199
|
-
}
|
|
200
|
-
case "microsoft": {
|
|
201
|
-
const windowsLogo = hasThemeFg ? theme.fg("accent", "β") : "β";
|
|
202
|
-
const detailStr = details ? ` β ${details}` : "";
|
|
203
|
-
const prefix = hasThemeFg ? theme.fg("dim", "[") : "[";
|
|
204
|
-
const suffix = hasThemeFg ? theme.fg("dim", "]") : "]";
|
|
205
|
-
return `${windowsLogo} ${prefix}${visualName}${suffix} ${coloredIndicator}${detailStr}`;
|
|
206
|
-
}
|
|
207
|
-
case "google": {
|
|
208
|
-
const coloredName = googleColorize(visualName, theme);
|
|
209
|
-
const detailStr = details ? `:${details}` : "";
|
|
210
|
-
return `${coloredName} β ${coloredIndicator}${detailStr}`;
|
|
211
|
-
}
|
|
212
151
|
case "glass": {
|
|
213
152
|
const prefix = hasThemeFg ? theme.fg("dim", "β ") : "β ";
|
|
214
153
|
const suffix = hasThemeFg ? theme.fg("dim", " β") : " β";
|
|
@@ -223,30 +162,21 @@ export function beautifyStatus(
|
|
|
223
162
|
const dimmedName = hasThemeFg ? theme.fg("muted", visualName) : visualName;
|
|
224
163
|
return `${dimmedName} ${coloredBadge}`;
|
|
225
164
|
}
|
|
226
|
-
case "
|
|
227
|
-
|
|
228
|
-
const suffix = hasThemeFg ? theme.fg("dim", " \u2398") : " \u2398";
|
|
229
|
-
const separator = hasThemeFg ? theme.fg("dim", " | ") : " | ";
|
|
230
|
-
const detailStr = details ? `${separator}${details}` : "";
|
|
231
|
-
return `${visualName}${prefix}${coloredIndicator}${detailStr}${suffix}`;
|
|
232
|
-
}
|
|
233
|
-
case "minimal": {
|
|
165
|
+
case "minimal":
|
|
166
|
+
default: {
|
|
234
167
|
const separator = hasThemeFg ? theme.fg("dim", " β― ") : " β― ";
|
|
235
168
|
const dimmedName = hasThemeFg ? theme.fg("muted", visualName) : visualName;
|
|
236
169
|
const detailStr = details ? ` (${details})` : "";
|
|
237
170
|
return `${dimmedName}${separator}${coloredIndicator}${detailStr}`;
|
|
238
171
|
}
|
|
239
|
-
default:
|
|
240
|
-
return originalVal;
|
|
241
172
|
}
|
|
242
|
-
} catch (
|
|
243
|
-
// Resilience fallback: prevent rendering exceptions from ever crashing UI updates
|
|
173
|
+
} catch (_error) {
|
|
244
174
|
return originalVal;
|
|
245
175
|
}
|
|
246
176
|
}
|
|
247
177
|
|
|
248
178
|
/**
|
|
249
|
-
* Calculates the visual column cell width of a string
|
|
179
|
+
* Calculates the visual column cell width of a string, taking into account
|
|
250
180
|
* full-width Unicode characters, East Asian ideographs, Kana, Hangul, emojis, and zero-width joiners.
|
|
251
181
|
*/
|
|
252
182
|
export function getStringWidth(str: string): number {
|
|
@@ -257,7 +187,6 @@ export function getStringWidth(str: string): number {
|
|
|
257
187
|
for (const char of clean) {
|
|
258
188
|
const code = char.codePointAt(0) || 0;
|
|
259
189
|
|
|
260
|
-
// Zero-width characters (ZWJ, variation selectors, combining marks)
|
|
261
190
|
if (
|
|
262
191
|
code === 0x200d ||
|
|
263
192
|
code === 0xfe0f ||
|
|
@@ -317,10 +246,16 @@ export function sliceToVisualWidth(str: string, maxWidth: number): string {
|
|
|
317
246
|
|
|
318
247
|
for (const char of str) {
|
|
319
248
|
const code = char.codePointAt(0) || 0;
|
|
320
|
-
const charWidth =
|
|
321
|
-
code === 0x200d ||
|
|
322
|
-
|
|
323
|
-
|
|
249
|
+
const charWidth =
|
|
250
|
+
code === 0x200d ||
|
|
251
|
+
code === 0xfe0f ||
|
|
252
|
+
code === 0xfe0e ||
|
|
253
|
+
(code >= 0x0300 && code <= 0x036f) ||
|
|
254
|
+
(code >= 0x200b && code <= 0x200f)
|
|
255
|
+
? 0
|
|
256
|
+
: isFullWidth(code)
|
|
257
|
+
? 2
|
|
258
|
+
: 1;
|
|
324
259
|
|
|
325
260
|
if (width + charWidth > maxWidth) {
|
|
326
261
|
break;
|
|
@@ -347,21 +282,16 @@ export default function (pi: ExtensionAPI) {
|
|
|
347
282
|
pi.on("session_start", async (_event, ctx) => {
|
|
348
283
|
if (!ctx.hasUI || !ctx.ui) return;
|
|
349
284
|
|
|
350
|
-
// Flush stale statuses from previous sessions to prevent memory leaks and clear color cache
|
|
351
285
|
originalStatuses.clear();
|
|
352
|
-
googleColorizeCache.clear();
|
|
353
286
|
lastRenderedStatus.clear();
|
|
354
287
|
|
|
355
288
|
const originalSetStatus = ctx.ui.setStatus;
|
|
356
289
|
if (originalSetStatus && !(originalSetStatus as any).__beautifierHooked) {
|
|
357
290
|
const wrapped = function (key: string, value: string | undefined) {
|
|
358
|
-
// Track original statuses for dynamic updates
|
|
359
291
|
originalStatuses.set(key, value);
|
|
360
292
|
|
|
361
|
-
// Uses in-memory synchronization (zero file I/O overhead on render pipeline ticks)
|
|
362
293
|
const beautified = beautifyStatus(key, value, ctx.ui.theme, currentStyle);
|
|
363
294
|
|
|
364
|
-
// Dirty-checking (Finding 6): Skip redundant stdout paint updates
|
|
365
295
|
const cacheKey = `${key}:${currentStyle}`;
|
|
366
296
|
if (lastRenderedStatus.get(cacheKey) === beautified) {
|
|
367
297
|
return;
|
|
@@ -377,68 +307,64 @@ export default function (pi: ExtensionAPI) {
|
|
|
377
307
|
|
|
378
308
|
// Dynamic status bar style controller registry
|
|
379
309
|
pi.registerCommand("beautify", {
|
|
380
|
-
description: "Choose TUI status bar beautification style",
|
|
381
|
-
handler: async (
|
|
310
|
+
description: "Choose TUI status bar beautification style or generate a custom style with AI",
|
|
311
|
+
handler: async (_args, ctx) => {
|
|
382
312
|
if (!ctx.hasUI || !ctx.ui) {
|
|
383
313
|
return;
|
|
384
314
|
}
|
|
385
315
|
|
|
386
316
|
const styleOptions = [
|
|
387
|
-
"
|
|
388
|
-
"
|
|
389
|
-
"
|
|
390
|
-
"
|
|
391
|
-
"
|
|
392
|
-
"microsoft (Microsoft Fluent - e.g. β [name] β)",
|
|
393
|
-
"glass (Charm Glass - e.g. β name β β)",
|
|
394
|
-
"glow (Glow Badge - e.g. name [ β ])",
|
|
395
|
-
"matrix (Retro Matrix - e.g. name β¦ β | 2 β¦)",
|
|
396
|
-
"off (use raw output)",
|
|
317
|
+
"custom - [AI Prompt Recipe] Let your agent generate or tailor your own style",
|
|
318
|
+
"minimal - Clean chevron indicator (e.g. name β― β)",
|
|
319
|
+
"glass - Framed capsule badge (e.g. β name β β)",
|
|
320
|
+
"glow - High-contrast inverse pill (e.g. name [ β ])",
|
|
321
|
+
"off - Pass through raw status output",
|
|
397
322
|
];
|
|
398
323
|
|
|
399
324
|
const choice = await ctx.ui.select(`Choose status style (Current: ${currentStyle}):`, styleOptions);
|
|
400
325
|
|
|
401
|
-
if (choice)
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
"
|
|
408
|
-
"
|
|
409
|
-
"
|
|
410
|
-
"
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
326
|
+
if (!choice) return;
|
|
327
|
+
|
|
328
|
+
const styleKey = choice.split(" ")[0].trim();
|
|
329
|
+
|
|
330
|
+
if (styleKey === "custom") {
|
|
331
|
+
const promptRecipe = [
|
|
332
|
+
"You are acting as my Pi TUI Status Bar designer.",
|
|
333
|
+
"1. Detect my primary language from our recent conversation history or environment locale.",
|
|
334
|
+
"2. In that EXACT primary language, proactively ask me what visual aesthetic I want for my status bar, and propose 3 distinct creative ideas (e.g. Cyberpunk Dot-Matrix, Warm Minimalist, High-Contrast Pill).",
|
|
335
|
+
"3. Wait for my preference or feedback, then inspect `tui-status-beautifier.ts` and apply the custom layout for me."
|
|
336
|
+
].join("\n");
|
|
337
|
+
|
|
338
|
+
if (typeof (ctx.ui as any).setEditorText === "function") {
|
|
339
|
+
(ctx.ui as any).setEditorText(promptRecipe);
|
|
340
|
+
ctx.ui.notify("Press Enter to let your agent consult your style preference in your native language!", "info");
|
|
341
|
+
} else {
|
|
342
|
+
ctx.ui.notify(promptRecipe, "info");
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if ((VALID_STYLES as readonly string[]).includes(styleKey)) {
|
|
348
|
+
currentStyle = styleKey;
|
|
349
|
+
lastRenderedStatus.clear();
|
|
350
|
+
ctx.ui.notify(`TUI status style changed to: ${styleKey}`, "info");
|
|
351
|
+
|
|
352
|
+
for (const [key, val] of originalStatuses.entries()) {
|
|
353
|
+
ctx.ui.setStatus(key, val);
|
|
354
|
+
}
|
|
427
355
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
fs.renameSync(tempSettings, settingsPath);
|
|
438
|
-
}
|
|
439
|
-
} catch (e) {
|
|
440
|
-
// Silently fail disk write to avoid crashing active user sessions
|
|
356
|
+
try {
|
|
357
|
+
if (fs.existsSync(settingsPath)) {
|
|
358
|
+
const data = fs.readFileSync(settingsPath, "utf-8");
|
|
359
|
+
const config = JSON.parse(data);
|
|
360
|
+
if (!config.beautifier) config.beautifier = {};
|
|
361
|
+
config.beautifier.style = styleKey;
|
|
362
|
+
const tempSettings = `${settingsPath}.tmp.${process.pid}.${Date.now()}`;
|
|
363
|
+
fs.writeFileSync(tempSettings, JSON.stringify(config, null, 2), "utf-8");
|
|
364
|
+
fs.renameSync(tempSettings, settingsPath);
|
|
441
365
|
}
|
|
366
|
+
} catch (_e) {
|
|
367
|
+
// Silently ignore disk write errors
|
|
442
368
|
}
|
|
443
369
|
}
|
|
444
370
|
},
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-tui-status-beautifier",
|
|
3
|
-
"version": "1.1
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Minimalist status bar formatter & AI-customizable layout engine for Pi TUI.",
|
|
5
5
|
"main": "extensions/tui-status-beautifier.ts",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"pi-package"
|