@narumitw/pi-statusline 0.49.3 → 0.49.5
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 +4 -7
- package/package.json +4 -8
- package/src/ansi.ts +26 -0
- package/src/command-contract.ts +13 -0
- package/src/commands.ts +925 -0
- package/src/directory.ts +54 -0
- package/src/extension-status.ts +321 -0
- package/src/git-status.ts +148 -0
- package/src/index.ts +1 -1
- package/src/information-profiles.ts +40 -0
- package/src/powerline.ts +170 -0
- package/src/presets/candy.ts +9 -0
- package/src/presets/create-ramp.ts +41 -0
- package/src/presets/custom.ts +11 -0
- package/src/presets/forest.ts +9 -0
- package/src/presets/index.ts +55 -0
- package/src/presets/mono.ts +9 -0
- package/src/presets/neon.ts +9 -0
- package/src/presets/ocean.ts +9 -0
- package/src/presets/sunset.ts +9 -0
- package/src/presets/tokyo-night.ts +13 -0
- package/src/presets/types.ts +12 -0
- package/src/render.ts +357 -0
- package/src/settings.ts +702 -0
- package/src/statusline.ts +376 -0
- package/src/terminal.ts +60 -0
- package/src/types.ts +88 -0
- package/src/usage.ts +53 -0
- package/dist/chunks/chunk-6BDIM4OT.ts +0 -776
- package/dist/chunks/chunk-6BDIM4OT.ts.map +0 -7
- package/dist/chunks/commands-FUT5RTFK.ts +0 -774
- package/dist/chunks/commands-FUT5RTFK.ts.map +0 -7
- package/dist/statusline.ts +0 -1144
- package/dist/statusline.ts.map +0 -7
|
@@ -1,776 +0,0 @@
|
|
|
1
|
-
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
|
-
// @ts-nocheck -- generated JavaScript uses a .ts extension so Pi routes chunks through Jiti.
|
|
3
|
-
|
|
4
|
-
// src/command-contract.ts
|
|
5
|
-
var STATUSLINE_SUBCOMMANDS = [
|
|
6
|
-
{ value: "settings", label: "settings", description: "Edit pi-statusline.json" },
|
|
7
|
-
{ value: "status", label: "status", description: "Show effective statusline settings" },
|
|
8
|
-
{ value: "help", label: "help", description: "Show configuration help" }
|
|
9
|
-
];
|
|
10
|
-
function completeStatuslineArguments(prefix) {
|
|
11
|
-
const normalized = prefix.trim().toLowerCase();
|
|
12
|
-
const matches = STATUSLINE_SUBCOMMANDS.filter((item) => item.value.startsWith(normalized));
|
|
13
|
-
return matches.length > 0 ? [...matches] : null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// src/types.ts
|
|
17
|
-
var SEGMENT_NAMES = [
|
|
18
|
-
"brand",
|
|
19
|
-
"provider",
|
|
20
|
-
"model",
|
|
21
|
-
"thinking",
|
|
22
|
-
"cwd",
|
|
23
|
-
"branch",
|
|
24
|
-
"tools",
|
|
25
|
-
"context",
|
|
26
|
-
"tokens",
|
|
27
|
-
"cache",
|
|
28
|
-
"cost",
|
|
29
|
-
"time",
|
|
30
|
-
"turn"
|
|
31
|
-
];
|
|
32
|
-
var LINE_BREAK_SEGMENT_NAME = "line_break";
|
|
33
|
-
var PALETTE_NAMES = [
|
|
34
|
-
"tokyo-night",
|
|
35
|
-
"ocean",
|
|
36
|
-
"sunset",
|
|
37
|
-
"forest",
|
|
38
|
-
"candy",
|
|
39
|
-
"neon",
|
|
40
|
-
"mono"
|
|
41
|
-
];
|
|
42
|
-
var PALETTE_PRESET_NAMES = [...PALETTE_NAMES, "custom"];
|
|
43
|
-
var DENSITIES = ["compact", "cozy"];
|
|
44
|
-
var SEPARATOR_NAMES = ["none", "dot", "bar", "powerline", "round"];
|
|
45
|
-
var TRUNCATION_DIRECTIONS = ["start", "middle", "end"];
|
|
46
|
-
|
|
47
|
-
// src/presets/create-ramp.ts
|
|
48
|
-
var BLOCK_NAMES = ["header", "directory", "git", "runtime", "meter"];
|
|
49
|
-
function createRampPreset(backgrounds) {
|
|
50
|
-
return {
|
|
51
|
-
lead: backgrounds[0],
|
|
52
|
-
blocks: Object.fromEntries(
|
|
53
|
-
BLOCK_NAMES.map((block, index) => {
|
|
54
|
-
const background = backgrounds[index];
|
|
55
|
-
return [block, { fg: contrastColor(background), bg: background }];
|
|
56
|
-
})
|
|
57
|
-
),
|
|
58
|
-
extensionSeparator: backgrounds[2]
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function contrastColor(hex) {
|
|
62
|
-
const background = relativeLuminance(hex);
|
|
63
|
-
const dark = relativeLuminance("#090c0c");
|
|
64
|
-
const light = relativeLuminance("#f0f0f0");
|
|
65
|
-
return contrastRatio(background, dark) >= contrastRatio(background, light) ? "#090c0c" : "#f0f0f0";
|
|
66
|
-
}
|
|
67
|
-
function contrastRatio(left, right) {
|
|
68
|
-
return (Math.max(left, right) + 0.05) / (Math.min(left, right) + 0.05);
|
|
69
|
-
}
|
|
70
|
-
function relativeLuminance(hex) {
|
|
71
|
-
const normalized = hex.slice(1);
|
|
72
|
-
const channels = [0, 2, 4].map((offset) => {
|
|
73
|
-
const value = Number.parseInt(normalized.slice(offset, offset + 2), 16) / 255;
|
|
74
|
-
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
75
|
-
});
|
|
76
|
-
return 0.2126 * (channels[0] ?? 0) + 0.7152 * (channels[1] ?? 0) + 0.0722 * (channels[2] ?? 0);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// src/presets/candy.ts
|
|
80
|
-
var CANDY_PRESET = createRampPreset([
|
|
81
|
-
"#f5c2e7",
|
|
82
|
-
"#cba6f7",
|
|
83
|
-
"#89b4fa",
|
|
84
|
-
"#745f9a",
|
|
85
|
-
"#403a5c"
|
|
86
|
-
]);
|
|
87
|
-
|
|
88
|
-
// src/presets/custom.ts
|
|
89
|
-
var CUSTOM_PRESET = {
|
|
90
|
-
blocks: {
|
|
91
|
-
header: {},
|
|
92
|
-
directory: {},
|
|
93
|
-
git: {},
|
|
94
|
-
runtime: {},
|
|
95
|
-
meter: {}
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// src/presets/forest.ts
|
|
100
|
-
var FOREST_PRESET = createRampPreset([
|
|
101
|
-
"#a7c080",
|
|
102
|
-
"#83c092",
|
|
103
|
-
"#5f9f75",
|
|
104
|
-
"#3f6f55",
|
|
105
|
-
"#293f35"
|
|
106
|
-
]);
|
|
107
|
-
|
|
108
|
-
// src/presets/mono.ts
|
|
109
|
-
var MONO_PRESET = createRampPreset([
|
|
110
|
-
"#d4d4d4",
|
|
111
|
-
"#a3a3a3",
|
|
112
|
-
"#686868",
|
|
113
|
-
"#404040",
|
|
114
|
-
"#262626"
|
|
115
|
-
]);
|
|
116
|
-
|
|
117
|
-
// src/presets/neon.ts
|
|
118
|
-
var NEON_PRESET = createRampPreset([
|
|
119
|
-
"#39ff14",
|
|
120
|
-
"#00f5ff",
|
|
121
|
-
"#ff4fd8",
|
|
122
|
-
"#7a2cf3",
|
|
123
|
-
"#29134f"
|
|
124
|
-
]);
|
|
125
|
-
|
|
126
|
-
// src/presets/ocean.ts
|
|
127
|
-
var OCEAN_PRESET = createRampPreset([
|
|
128
|
-
"#7dcfff",
|
|
129
|
-
"#4f9fba",
|
|
130
|
-
"#2d6f88",
|
|
131
|
-
"#23475b",
|
|
132
|
-
"#182b3a"
|
|
133
|
-
]);
|
|
134
|
-
|
|
135
|
-
// src/presets/sunset.ts
|
|
136
|
-
var SUNSET_PRESET = createRampPreset([
|
|
137
|
-
"#ffcf70",
|
|
138
|
-
"#f59e6f",
|
|
139
|
-
"#dc718a",
|
|
140
|
-
"#8f5b78",
|
|
141
|
-
"#493447"
|
|
142
|
-
]);
|
|
143
|
-
|
|
144
|
-
// src/presets/tokyo-night.ts
|
|
145
|
-
var TOKYO_NIGHT_PRESET = {
|
|
146
|
-
lead: "#a3aed2",
|
|
147
|
-
blocks: {
|
|
148
|
-
header: { fg: "#090c0c", bg: "#a3aed2" },
|
|
149
|
-
directory: { fg: "#e3e5e5", bg: "#769ff0" },
|
|
150
|
-
git: { fg: "#769ff0", bg: "#394260" },
|
|
151
|
-
runtime: { fg: "#769ff0", bg: "#212736" },
|
|
152
|
-
meter: { fg: "#a0a9cb", bg: "#1d2230" }
|
|
153
|
-
},
|
|
154
|
-
extensionSeparator: "#394260"
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// src/presets/index.ts
|
|
158
|
-
var PRESETS = {
|
|
159
|
-
"tokyo-night": TOKYO_NIGHT_PRESET,
|
|
160
|
-
ocean: OCEAN_PRESET,
|
|
161
|
-
sunset: SUNSET_PRESET,
|
|
162
|
-
forest: FOREST_PRESET,
|
|
163
|
-
candy: CANDY_PRESET,
|
|
164
|
-
neon: NEON_PRESET,
|
|
165
|
-
mono: MONO_PRESET,
|
|
166
|
-
custom: CUSTOM_PRESET
|
|
167
|
-
};
|
|
168
|
-
var SEGMENT_BLOCKS = {
|
|
169
|
-
brand: "header",
|
|
170
|
-
provider: "header",
|
|
171
|
-
model: "header",
|
|
172
|
-
thinking: "header",
|
|
173
|
-
cwd: "directory",
|
|
174
|
-
branch: "git",
|
|
175
|
-
tools: "runtime",
|
|
176
|
-
context: "runtime",
|
|
177
|
-
tokens: "runtime",
|
|
178
|
-
cache: "runtime",
|
|
179
|
-
cost: "meter",
|
|
180
|
-
time: "meter",
|
|
181
|
-
turn: "meter"
|
|
182
|
-
};
|
|
183
|
-
function resolvePreset(preset) {
|
|
184
|
-
return PRESETS[preset];
|
|
185
|
-
}
|
|
186
|
-
function segmentPaletteForPreset(preset) {
|
|
187
|
-
const blocks = PRESETS[preset].blocks;
|
|
188
|
-
return Object.fromEntries(
|
|
189
|
-
SEGMENT_NAMES.map((name) => [name, { ...blocks[SEGMENT_BLOCKS[name]] }])
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// src/information-profiles.ts
|
|
194
|
-
var INFORMATION_PROFILE_NAMES = ["minimal", "balanced", "detailed"];
|
|
195
|
-
var INFORMATION_PROFILES = {
|
|
196
|
-
minimal: ["model", "cwd", "branch", "context"],
|
|
197
|
-
balanced: ["model", "thinking", "cwd", "branch", "tools", "context", "time"],
|
|
198
|
-
detailed: [
|
|
199
|
-
"provider",
|
|
200
|
-
"model",
|
|
201
|
-
"thinking",
|
|
202
|
-
"cwd",
|
|
203
|
-
"branch",
|
|
204
|
-
"tools",
|
|
205
|
-
"context",
|
|
206
|
-
"tokens",
|
|
207
|
-
"cache",
|
|
208
|
-
"cost",
|
|
209
|
-
"time"
|
|
210
|
-
]
|
|
211
|
-
};
|
|
212
|
-
function inferInformationProfile(segments) {
|
|
213
|
-
for (const name of INFORMATION_PROFILE_NAMES) {
|
|
214
|
-
const profile = INFORMATION_PROFILES[name];
|
|
215
|
-
if (segments.length === profile.length && segments.every((segment, index) => segment === profile[index])) {
|
|
216
|
-
return name;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return "custom";
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// src/settings.ts
|
|
223
|
-
import { randomUUID } from "node:crypto";
|
|
224
|
-
import { lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
225
|
-
import { dirname, join } from "node:path";
|
|
226
|
-
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
227
|
-
var SETTINGS_FILE_NAME = "pi-statusline.json";
|
|
228
|
-
var LEGACY_SETTINGS_FILE_NAME = "pi-statusline-settings.json";
|
|
229
|
-
var DEFAULT_DOCUMENT_EXTENSION_STATUS_ICONS = {
|
|
230
|
-
accounts: "\u{1F464}",
|
|
231
|
-
caffeinate: "\u{1F48A}",
|
|
232
|
-
"chrome-devtools": "\u{1F310}",
|
|
233
|
-
firecrawl: "\u{1F525}",
|
|
234
|
-
"github-pr": "\u{1F50E}",
|
|
235
|
-
goal: "\u{1F3AF}",
|
|
236
|
-
"google-genai": "\u2728",
|
|
237
|
-
lsp: "\u{1F9F0}",
|
|
238
|
-
"plan-mode": "\u{1F4DD}",
|
|
239
|
-
retry: "\u{1F501}",
|
|
240
|
-
subagents: "\u{1F9D1}\u200D\u{1F91D}\u200D\u{1F9D1}",
|
|
241
|
-
sync: "\u{1F504}",
|
|
242
|
-
usage: "\u{1F4CA}"
|
|
243
|
-
};
|
|
244
|
-
var DEFAULT_EXTENSION_STATUS_ICONS = {
|
|
245
|
-
...DEFAULT_DOCUMENT_EXTENSION_STATUS_ICONS,
|
|
246
|
-
"codex-usage": "\u{1F4CA}",
|
|
247
|
-
pisync: "\u{1F504}",
|
|
248
|
-
"unknown-error-retry": "\u{1F501}"
|
|
249
|
-
};
|
|
250
|
-
var LEGACY_STATUS_ICON_KEYS = {
|
|
251
|
-
pisync: "sync",
|
|
252
|
-
"unknown-error-retry": "retry"
|
|
253
|
-
};
|
|
254
|
-
var DEFAULT_SEGMENTS = [...INFORMATION_PROFILES.balanced];
|
|
255
|
-
var DEFAULT_STATUSLINE_CONFIG = {
|
|
256
|
-
palettePreset: "tokyo-night",
|
|
257
|
-
palette: segmentPaletteForPreset("tokyo-night"),
|
|
258
|
-
density: "compact",
|
|
259
|
-
separator: "none",
|
|
260
|
-
segments: DEFAULT_SEGMENTS,
|
|
261
|
-
segmentText: {
|
|
262
|
-
brand: { prefix: "", suffix: "" },
|
|
263
|
-
provider: { prefix: "\u{1F50C} ", suffix: "" },
|
|
264
|
-
model: {
|
|
265
|
-
prefix: "\u{1F916} ",
|
|
266
|
-
suffix: "",
|
|
267
|
-
truncationLength: 36,
|
|
268
|
-
truncationSymbol: "\u2026",
|
|
269
|
-
truncationDirection: "start"
|
|
270
|
-
},
|
|
271
|
-
thinking: { prefix: "\u{1F9E0} ", suffix: "" },
|
|
272
|
-
cwd: { prefix: "\u{1F4C1} ", suffix: "" },
|
|
273
|
-
branch: { prefix: "\u{1F33F} ", suffix: "" },
|
|
274
|
-
tools: { prefix: "", suffix: "" },
|
|
275
|
-
context: { prefix: "\u{1FA9F} ctx ", suffix: "" },
|
|
276
|
-
tokens: { prefix: "\u{1F522} ", suffix: "" },
|
|
277
|
-
cache: { prefix: "\u{1F4E6} ", suffix: "" },
|
|
278
|
-
cost: { prefix: "\u{1F4B8} $", suffix: "" },
|
|
279
|
-
time: { prefix: "\u{1F552} ", suffix: "" },
|
|
280
|
-
turn: { prefix: "\u{1F501} #", suffix: "" }
|
|
281
|
-
},
|
|
282
|
-
extensionStatusIcons: DEFAULT_EXTENSION_STATUS_ICONS
|
|
283
|
-
};
|
|
284
|
-
var DEFAULT_STATUSLINE_DOCUMENT_CONFIG = {
|
|
285
|
-
palettePreset: DEFAULT_STATUSLINE_CONFIG.palettePreset,
|
|
286
|
-
density: DEFAULT_STATUSLINE_CONFIG.density,
|
|
287
|
-
separator: DEFAULT_STATUSLINE_CONFIG.separator,
|
|
288
|
-
segments: DEFAULT_SEGMENTS,
|
|
289
|
-
segmentText: DEFAULT_STATUSLINE_CONFIG.segmentText,
|
|
290
|
-
extensionStatusIcons: DEFAULT_DOCUMENT_EXTENSION_STATUS_ICONS
|
|
291
|
-
};
|
|
292
|
-
var DEFAULT_STATUSLINE_DOCUMENT = `${JSON.stringify(
|
|
293
|
-
DEFAULT_STATUSLINE_DOCUMENT_CONFIG,
|
|
294
|
-
null,
|
|
295
|
-
" "
|
|
296
|
-
)}
|
|
297
|
-
`;
|
|
298
|
-
var pendingSettingsNotice;
|
|
299
|
-
function settingsFilePath(agentDir = getAgentDir()) {
|
|
300
|
-
return join(agentDir, SETTINGS_FILE_NAME);
|
|
301
|
-
}
|
|
302
|
-
function createDefaultConfig() {
|
|
303
|
-
return cloneConfig(DEFAULT_STATUSLINE_CONFIG);
|
|
304
|
-
}
|
|
305
|
-
function normalizeStatuslineConfig(value) {
|
|
306
|
-
const config = createDefaultConfig();
|
|
307
|
-
const diagnostics = [];
|
|
308
|
-
if (!isRecord(value)) {
|
|
309
|
-
return {
|
|
310
|
-
config,
|
|
311
|
-
diagnostics: [invalidDiagnostic("", "Settings must contain a JSON object", "error")]
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
const knownRoot = /* @__PURE__ */ new Set([
|
|
315
|
-
"palettePreset",
|
|
316
|
-
"palette",
|
|
317
|
-
"density",
|
|
318
|
-
"separator",
|
|
319
|
-
"segments",
|
|
320
|
-
"segmentText",
|
|
321
|
-
"extensionStatusIcons"
|
|
322
|
-
]);
|
|
323
|
-
for (const key of Object.keys(value)) {
|
|
324
|
-
if (!knownRoot.has(key)) diagnostics.push(unknownDiagnostic(key));
|
|
325
|
-
}
|
|
326
|
-
normalizePalette(value.palette, config, diagnostics);
|
|
327
|
-
normalizeEnum(value, "palettePreset", PALETTE_PRESET_NAMES, config, diagnostics);
|
|
328
|
-
if (!isRecord(value.palette) && isPaletteName(config.palettePreset)) {
|
|
329
|
-
config.palette = segmentPaletteForPreset(config.palettePreset);
|
|
330
|
-
}
|
|
331
|
-
normalizeEnum(value, "density", DENSITIES, config, diagnostics);
|
|
332
|
-
normalizeEnum(value, "separator", SEPARATOR_NAMES, config, diagnostics);
|
|
333
|
-
if (value.segments !== void 0) {
|
|
334
|
-
if (!Array.isArray(value.segments)) {
|
|
335
|
-
diagnostics.push(invalidDiagnostic("segments", "Expected an array of segment names"));
|
|
336
|
-
} else {
|
|
337
|
-
const segments = [];
|
|
338
|
-
const seen = /* @__PURE__ */ new Set();
|
|
339
|
-
for (const [index, item] of value.segments.entries()) {
|
|
340
|
-
const path = `segments[${index}]`;
|
|
341
|
-
if (typeof item !== "string" || !isConfigSegmentName(item)) {
|
|
342
|
-
diagnostics.push(invalidDiagnostic(path, "Unknown or non-string segment name"));
|
|
343
|
-
continue;
|
|
344
|
-
}
|
|
345
|
-
if (item === LINE_BREAK_SEGMENT_NAME) {
|
|
346
|
-
if (segments.at(-1) === LINE_BREAK_SEGMENT_NAME) {
|
|
347
|
-
diagnostics.push(
|
|
348
|
-
invalidDiagnostic(path, "Consecutive line_break segments are not allowed")
|
|
349
|
-
);
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
segments.push(item);
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
if (seen.has(item)) {
|
|
356
|
-
diagnostics.push(invalidDiagnostic(path, `Duplicate segment ${JSON.stringify(item)}`));
|
|
357
|
-
continue;
|
|
358
|
-
}
|
|
359
|
-
seen.add(item);
|
|
360
|
-
segments.push(item);
|
|
361
|
-
}
|
|
362
|
-
config.segments = segments;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (value.segmentText !== void 0) {
|
|
366
|
-
if (!isRecord(value.segmentText)) {
|
|
367
|
-
diagnostics.push(invalidDiagnostic("segmentText", "Expected an object"));
|
|
368
|
-
} else {
|
|
369
|
-
for (const [name, presentation] of Object.entries(value.segmentText)) {
|
|
370
|
-
const path = `segmentText.${name}`;
|
|
371
|
-
if (!isSegmentName(name)) {
|
|
372
|
-
diagnostics.push(unknownDiagnostic(path));
|
|
373
|
-
continue;
|
|
374
|
-
}
|
|
375
|
-
if (!isRecord(presentation)) {
|
|
376
|
-
diagnostics.push(invalidDiagnostic(path, "Expected an object"));
|
|
377
|
-
continue;
|
|
378
|
-
}
|
|
379
|
-
const knownFields = /* @__PURE__ */ new Set([
|
|
380
|
-
"prefix",
|
|
381
|
-
"suffix",
|
|
382
|
-
...name === "model" ? ["truncationLength", "truncationSymbol", "truncationDirection"] : []
|
|
383
|
-
]);
|
|
384
|
-
for (const key of Object.keys(presentation)) {
|
|
385
|
-
if (!knownFields.has(key)) diagnostics.push(unknownDiagnostic(`${path}.${key}`));
|
|
386
|
-
}
|
|
387
|
-
for (const field of ["prefix", "suffix"]) {
|
|
388
|
-
const fieldValue = presentation[field];
|
|
389
|
-
if (fieldValue === void 0) continue;
|
|
390
|
-
if (!isSafeSegmentText(fieldValue, `${path}.${field}`, diagnostics)) continue;
|
|
391
|
-
config.segmentText[name][field] = fieldValue;
|
|
392
|
-
}
|
|
393
|
-
if (name === "model") {
|
|
394
|
-
normalizeModelTruncation(presentation, config, diagnostics);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
if (value.extensionStatusIcons !== void 0) {
|
|
400
|
-
if (!isRecord(value.extensionStatusIcons)) {
|
|
401
|
-
diagnostics.push(invalidDiagnostic("extensionStatusIcons", "Expected an object"));
|
|
402
|
-
} else {
|
|
403
|
-
for (const [key, icon] of Object.entries(value.extensionStatusIcons)) {
|
|
404
|
-
if (typeof icon !== "string") {
|
|
405
|
-
diagnostics.push(invalidDiagnostic(`extensionStatusIcons.${key}`, "Expected a string"));
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
Object.defineProperty(config.extensionStatusIcons, key, {
|
|
409
|
-
value: icon,
|
|
410
|
-
enumerable: true,
|
|
411
|
-
configurable: true,
|
|
412
|
-
writable: true
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
for (const [legacyKey, canonicalKey] of Object.entries(LEGACY_STATUS_ICON_KEYS)) {
|
|
416
|
-
const legacyIcon = Object.hasOwn(value.extensionStatusIcons, legacyKey) ? value.extensionStatusIcons[legacyKey] : void 0;
|
|
417
|
-
const canonicalIcon = Object.hasOwn(value.extensionStatusIcons, canonicalKey) ? value.extensionStatusIcons[canonicalKey] : void 0;
|
|
418
|
-
const targetKey = typeof canonicalIcon === "string" ? legacyKey : canonicalKey;
|
|
419
|
-
const inheritedIcon = typeof canonicalIcon === "string" ? canonicalIcon : typeof legacyIcon === "string" ? legacyIcon : void 0;
|
|
420
|
-
if (inheritedIcon === void 0) continue;
|
|
421
|
-
Object.defineProperty(config.extensionStatusIcons, targetKey, {
|
|
422
|
-
value: inheritedIcon,
|
|
423
|
-
enumerable: true,
|
|
424
|
-
configurable: true,
|
|
425
|
-
writable: true
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return { config, diagnostics };
|
|
431
|
-
}
|
|
432
|
-
function loadStatuslineSettings(settingsPath) {
|
|
433
|
-
let rawDocument;
|
|
434
|
-
try {
|
|
435
|
-
rawDocument = readFileSync(settingsPath, "utf8");
|
|
436
|
-
} catch (error) {
|
|
437
|
-
if (error.code === "ENOENT" && !pathExists(settingsPath)) {
|
|
438
|
-
return builtInSettings(settingsPath);
|
|
439
|
-
}
|
|
440
|
-
return builtInSettings(settingsPath, [
|
|
441
|
-
diagnostic("error", "io", "", `Unable to read settings: ${formatError(error)}`)
|
|
442
|
-
]);
|
|
443
|
-
}
|
|
444
|
-
let parsed;
|
|
445
|
-
try {
|
|
446
|
-
parsed = JSON.parse(rawDocument);
|
|
447
|
-
} catch (error) {
|
|
448
|
-
return {
|
|
449
|
-
...builtInSettings(settingsPath, [
|
|
450
|
-
diagnostic("error", "parse", "", `Unable to parse JSON: ${formatError(error)}`)
|
|
451
|
-
]),
|
|
452
|
-
rawDocument
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
const normalized = normalizeStatuslineConfig(parsed);
|
|
456
|
-
return {
|
|
457
|
-
config: normalized.config,
|
|
458
|
-
source: normalized.diagnostics.some((item) => item.severity === "error") ? "built-in" : "user",
|
|
459
|
-
settingsPath,
|
|
460
|
-
rawDocument,
|
|
461
|
-
diagnostics: normalized.diagnostics
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
function loadStatuslineSettingsForAgent(agentDir = getAgentDir()) {
|
|
465
|
-
pendingSettingsNotice = void 0;
|
|
466
|
-
const canonicalPath = settingsFilePath(agentDir);
|
|
467
|
-
const legacyPath = join(agentDir, LEGACY_SETTINGS_FILE_NAME);
|
|
468
|
-
const canonical = loadStatuslineSettings(canonicalPath);
|
|
469
|
-
if (!isMissingStatuslineSettings(canonical)) {
|
|
470
|
-
if (!isMissingStatuslineSettings(loadStatuslineSettings(legacyPath))) {
|
|
471
|
-
pendingSettingsNotice = `${LEGACY_SETTINGS_FILE_NAME} ignored because ${SETTINGS_FILE_NAME} takes precedence.`;
|
|
472
|
-
}
|
|
473
|
-
return canonical;
|
|
474
|
-
}
|
|
475
|
-
const legacy = loadStatuslineSettings(legacyPath);
|
|
476
|
-
return isMissingStatuslineSettings(legacy) ? canonical : migrateLegacySettings(canonicalPath, legacy);
|
|
477
|
-
}
|
|
478
|
-
function migrateLegacySettings(canonicalPath, legacy) {
|
|
479
|
-
const racedCanonical = loadStatuslineSettings(canonicalPath);
|
|
480
|
-
if (!isMissingStatuslineSettings(racedCanonical)) {
|
|
481
|
-
pendingSettingsNotice = `${LEGACY_SETTINGS_FILE_NAME} ignored because ${SETTINGS_FILE_NAME} was created concurrently.`;
|
|
482
|
-
return racedCanonical;
|
|
483
|
-
}
|
|
484
|
-
if (legacy.source !== "user" || legacy.rawDocument === void 0 || blockingDiagnostics(legacy.diagnostics).length > 0) {
|
|
485
|
-
pendingSettingsNotice = `${LEGACY_SETTINGS_FILE_NAME} is invalid and was ignored.`;
|
|
486
|
-
return legacy;
|
|
487
|
-
}
|
|
488
|
-
pendingSettingsNotice = `Using legacy ${LEGACY_SETTINGS_FILE_NAME}; rename it to ${SETTINGS_FILE_NAME}. Future saves write ${SETTINGS_FILE_NAME} without modifying the legacy file.`;
|
|
489
|
-
return legacy;
|
|
490
|
-
}
|
|
491
|
-
function isMissingStatuslineSettings(settings) {
|
|
492
|
-
return settings.source === "built-in" && settings.rawDocument === void 0 && settings.diagnostics.length === 0;
|
|
493
|
-
}
|
|
494
|
-
function saveStatuslineSettingsDocument(settingsPath, rawDocument, overrides = {}) {
|
|
495
|
-
let parsed;
|
|
496
|
-
try {
|
|
497
|
-
parsed = JSON.parse(rawDocument);
|
|
498
|
-
} catch (error) {
|
|
499
|
-
throw new Error(`Unable to parse JSON: ${formatError(error)}`);
|
|
500
|
-
}
|
|
501
|
-
const normalized = normalizeStatuslineConfig(parsed);
|
|
502
|
-
const blocking = blockingDiagnostics(normalized.diagnostics);
|
|
503
|
-
if (blocking.length > 0) {
|
|
504
|
-
throw new Error(blocking.map((item) => `${item.path || "root"}: ${item.message}`).join("\n"));
|
|
505
|
-
}
|
|
506
|
-
const fs = { mkdirSync, writeFileSync, renameSync, rmSync, ...overrides };
|
|
507
|
-
const replaceExisting = pathEntryExists(settingsPath);
|
|
508
|
-
const temporaryPath = temporarySettingsPath(settingsPath);
|
|
509
|
-
let fileIdentity;
|
|
510
|
-
try {
|
|
511
|
-
fs.mkdirSync(dirname(settingsPath), { recursive: true });
|
|
512
|
-
fs.writeFileSync(temporaryPath, rawDocument, { encoding: "utf8", flag: "wx" });
|
|
513
|
-
const info = lstatSync(temporaryPath);
|
|
514
|
-
fileIdentity = { dev: info.dev, ino: info.ino };
|
|
515
|
-
if (!replaceExisting && pathEntryExists(settingsPath)) {
|
|
516
|
-
throw new Error(`${SETTINGS_FILE_NAME} was created concurrently; reopen settings and retry.`);
|
|
517
|
-
}
|
|
518
|
-
fs.renameSync(temporaryPath, settingsPath);
|
|
519
|
-
} finally {
|
|
520
|
-
removeTemporaryFile(fs.rmSync, temporaryPath);
|
|
521
|
-
}
|
|
522
|
-
return {
|
|
523
|
-
config: normalized.config,
|
|
524
|
-
source: "user",
|
|
525
|
-
settingsPath,
|
|
526
|
-
rawDocument,
|
|
527
|
-
...fileIdentity ? { fileIdentity } : {},
|
|
528
|
-
diagnostics: normalized.diagnostics
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
function removeStatuslineSettingsDocumentIfMatches(settingsPath, expectedRawDocument, expectedIdentity) {
|
|
532
|
-
const quarantinePath = join(
|
|
533
|
-
dirname(settingsPath),
|
|
534
|
-
`.${SETTINGS_FILE_NAME}.${randomUUID()}.rollback`
|
|
535
|
-
);
|
|
536
|
-
const before = lstatSync(settingsPath);
|
|
537
|
-
if (before.dev !== expectedIdentity.dev || before.ino !== expectedIdentity.ino) {
|
|
538
|
-
throw new Error("Statusline settings changed concurrently; the newer file was preserved");
|
|
539
|
-
}
|
|
540
|
-
renameSync(settingsPath, quarantinePath);
|
|
541
|
-
const quarantined = lstatSync(quarantinePath);
|
|
542
|
-
const quarantinedSavedFile = quarantined.isFile() && !quarantined.isSymbolicLink() && quarantined.dev === expectedIdentity.dev && quarantined.ino === expectedIdentity.ino;
|
|
543
|
-
if (quarantinedSavedFile && readFileSync(quarantinePath, "utf8") === expectedRawDocument) {
|
|
544
|
-
rmSync(quarantinePath);
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
if (quarantinedSavedFile && !pathEntryExists(settingsPath)) {
|
|
548
|
-
try {
|
|
549
|
-
renameSync(quarantinePath, settingsPath);
|
|
550
|
-
} catch {
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
throw new Error("Statusline settings changed concurrently; the newer file was preserved");
|
|
554
|
-
}
|
|
555
|
-
function consumeStatuslineSettingsNotice() {
|
|
556
|
-
const notice = pendingSettingsNotice;
|
|
557
|
-
pendingSettingsNotice = void 0;
|
|
558
|
-
return notice;
|
|
559
|
-
}
|
|
560
|
-
function readStatuslineSettings(settingsPath) {
|
|
561
|
-
return settingsPath ? loadStatuslineSettings(settingsPath).config : loadStatuslineSettingsForAgent().config;
|
|
562
|
-
}
|
|
563
|
-
function normalizeStatuslineSettings(value) {
|
|
564
|
-
return normalizeStatuslineConfig(value).config;
|
|
565
|
-
}
|
|
566
|
-
function normalizePalette(value, config, diagnostics) {
|
|
567
|
-
if (value === void 0) return;
|
|
568
|
-
if (typeof value === "string") {
|
|
569
|
-
if (!PALETTE_NAMES.includes(value)) {
|
|
570
|
-
diagnostics.push(
|
|
571
|
-
invalidDiagnostic(
|
|
572
|
-
"palette",
|
|
573
|
-
`Expected a palette object or one of: ${PALETTE_NAMES.join(", ")}`
|
|
574
|
-
)
|
|
575
|
-
);
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
config.palettePreset = value;
|
|
579
|
-
return;
|
|
580
|
-
}
|
|
581
|
-
if (!isRecord(value)) {
|
|
582
|
-
diagnostics.push(invalidDiagnostic("palette", "Expected a palette object"));
|
|
583
|
-
return;
|
|
584
|
-
}
|
|
585
|
-
const palette = {};
|
|
586
|
-
config.palette = palette;
|
|
587
|
-
for (const [name, colors] of Object.entries(value)) {
|
|
588
|
-
const path = `palette.${name}`;
|
|
589
|
-
if (!isSegmentName(name)) {
|
|
590
|
-
diagnostics.push(unknownDiagnostic(path));
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
if (!isRecord(colors)) {
|
|
594
|
-
diagnostics.push(invalidDiagnostic(path, "Expected an object"));
|
|
595
|
-
continue;
|
|
596
|
-
}
|
|
597
|
-
const normalizedColors = {};
|
|
598
|
-
palette[name] = normalizedColors;
|
|
599
|
-
for (const [field, color] of Object.entries(colors)) {
|
|
600
|
-
const colorPath = `${path}.${field}`;
|
|
601
|
-
if (field !== "fg" && field !== "bg") {
|
|
602
|
-
diagnostics.push(unknownDiagnostic(colorPath));
|
|
603
|
-
continue;
|
|
604
|
-
}
|
|
605
|
-
if (typeof color !== "string" || !/^#[0-9a-f]{6}$/iu.test(color)) {
|
|
606
|
-
diagnostics.push(invalidDiagnostic(colorPath, "Expected a full #RRGGBB hexadecimal color"));
|
|
607
|
-
continue;
|
|
608
|
-
}
|
|
609
|
-
normalizedColors[field] = color.toLowerCase();
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
config.palette = palette;
|
|
613
|
-
config.palettePreset = "custom";
|
|
614
|
-
}
|
|
615
|
-
function normalizeModelTruncation(presentation, config, diagnostics) {
|
|
616
|
-
const path = "segmentText.model";
|
|
617
|
-
const length = presentation.truncationLength;
|
|
618
|
-
if (length !== void 0) {
|
|
619
|
-
if (typeof length !== "number" || !Number.isInteger(length) || length < 0 || length > 1e3) {
|
|
620
|
-
diagnostics.push(
|
|
621
|
-
invalidDiagnostic(`${path}.truncationLength`, "Expected an integer from 0 through 1000")
|
|
622
|
-
);
|
|
623
|
-
} else config.segmentText.model.truncationLength = length;
|
|
624
|
-
}
|
|
625
|
-
const symbol = presentation.truncationSymbol;
|
|
626
|
-
if (symbol !== void 0 && isSafeSegmentText(symbol, `${path}.truncationSymbol`, diagnostics)) {
|
|
627
|
-
config.segmentText.model.truncationSymbol = symbol;
|
|
628
|
-
}
|
|
629
|
-
const direction = presentation.truncationDirection;
|
|
630
|
-
if (direction !== void 0) {
|
|
631
|
-
if (typeof direction !== "string" || !TRUNCATION_DIRECTIONS.includes(direction)) {
|
|
632
|
-
diagnostics.push(
|
|
633
|
-
invalidDiagnostic(
|
|
634
|
-
`${path}.truncationDirection`,
|
|
635
|
-
`Expected one of: ${TRUNCATION_DIRECTIONS.join(", ")}`
|
|
636
|
-
)
|
|
637
|
-
);
|
|
638
|
-
} else {
|
|
639
|
-
config.segmentText.model.truncationDirection = direction;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
function isSafeSegmentText(value, path, diagnostics) {
|
|
644
|
-
if (typeof value !== "string") {
|
|
645
|
-
diagnostics.push(invalidDiagnostic(path, "Expected a string"));
|
|
646
|
-
return false;
|
|
647
|
-
}
|
|
648
|
-
if (/[\r\n\u2028\u2029]/u.test(value)) {
|
|
649
|
-
diagnostics.push(invalidDiagnostic(path, "Line breaks are not allowed; use line_break"));
|
|
650
|
-
return false;
|
|
651
|
-
}
|
|
652
|
-
if (hasControlCharacter(value)) {
|
|
653
|
-
diagnostics.push(invalidDiagnostic(path, "Control characters are not allowed"));
|
|
654
|
-
return false;
|
|
655
|
-
}
|
|
656
|
-
return true;
|
|
657
|
-
}
|
|
658
|
-
function normalizeEnum(value, field, accepted, config, diagnostics) {
|
|
659
|
-
const candidate = value[field];
|
|
660
|
-
if (candidate === void 0) return;
|
|
661
|
-
if (typeof candidate !== "string" || !accepted.includes(candidate)) {
|
|
662
|
-
diagnostics.push(
|
|
663
|
-
invalidDiagnostic(field, `Expected one of: ${accepted.map(String).join(", ")}`)
|
|
664
|
-
);
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
config[field] = candidate;
|
|
668
|
-
}
|
|
669
|
-
function cloneSegmentPalette(palette) {
|
|
670
|
-
return Object.fromEntries(
|
|
671
|
-
Object.entries(palette).map(([name, colors]) => [name, { ...colors }])
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
function cloneConfig(config) {
|
|
675
|
-
return {
|
|
676
|
-
...config,
|
|
677
|
-
palette: cloneSegmentPalette(config.palette),
|
|
678
|
-
segments: [...config.segments],
|
|
679
|
-
segmentText: Object.fromEntries(
|
|
680
|
-
SEGMENT_NAMES.map((name) => [name, { ...config.segmentText[name] }])
|
|
681
|
-
),
|
|
682
|
-
extensionStatusIcons: { ...config.extensionStatusIcons }
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
function builtInSettings(settingsPath, diagnostics = []) {
|
|
686
|
-
return {
|
|
687
|
-
config: createDefaultConfig(),
|
|
688
|
-
source: "built-in",
|
|
689
|
-
settingsPath,
|
|
690
|
-
diagnostics
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
function hasControlCharacter(value) {
|
|
694
|
-
for (const character of value) {
|
|
695
|
-
const codePoint = character.codePointAt(0) ?? 0;
|
|
696
|
-
if (codePoint <= 31 || codePoint >= 127 && codePoint <= 159) return true;
|
|
697
|
-
}
|
|
698
|
-
return false;
|
|
699
|
-
}
|
|
700
|
-
function isRecord(value) {
|
|
701
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
702
|
-
}
|
|
703
|
-
function isConfigSegmentName(value) {
|
|
704
|
-
return value === LINE_BREAK_SEGMENT_NAME || isSegmentName(value);
|
|
705
|
-
}
|
|
706
|
-
function isPaletteName(value) {
|
|
707
|
-
return PALETTE_NAMES.includes(value);
|
|
708
|
-
}
|
|
709
|
-
function isSegmentName(value) {
|
|
710
|
-
return SEGMENT_NAMES.includes(value);
|
|
711
|
-
}
|
|
712
|
-
function blockingDiagnostics(diagnostics) {
|
|
713
|
-
return diagnostics.filter((item) => item.code !== "unknown");
|
|
714
|
-
}
|
|
715
|
-
function unknownDiagnostic(path) {
|
|
716
|
-
return diagnostic("warning", "unknown", path, `Unknown setting ${JSON.stringify(path)}`);
|
|
717
|
-
}
|
|
718
|
-
function invalidDiagnostic(path, message, severity = "warning") {
|
|
719
|
-
return diagnostic(severity, "invalid", path, message);
|
|
720
|
-
}
|
|
721
|
-
function diagnostic(severity, code, path, message) {
|
|
722
|
-
return { severity, code, path, message };
|
|
723
|
-
}
|
|
724
|
-
function temporarySettingsPath(settingsPath) {
|
|
725
|
-
return join(dirname(settingsPath), `.${SETTINGS_FILE_NAME}.${randomUUID()}.tmp`);
|
|
726
|
-
}
|
|
727
|
-
function removeTemporaryFile(remove, temporaryPath) {
|
|
728
|
-
try {
|
|
729
|
-
remove(temporaryPath, { force: true });
|
|
730
|
-
} catch {
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
function pathExists(path) {
|
|
734
|
-
try {
|
|
735
|
-
lstatSync(path);
|
|
736
|
-
return true;
|
|
737
|
-
} catch {
|
|
738
|
-
return false;
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
function pathEntryExists(path) {
|
|
742
|
-
try {
|
|
743
|
-
lstatSync(path);
|
|
744
|
-
return true;
|
|
745
|
-
} catch (error) {
|
|
746
|
-
if (error.code === "ENOENT") return false;
|
|
747
|
-
throw error;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
function formatError(error) {
|
|
751
|
-
return error instanceof Error ? error.message : String(error);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
export {
|
|
755
|
-
completeStatuslineArguments,
|
|
756
|
-
SEGMENT_NAMES,
|
|
757
|
-
LINE_BREAK_SEGMENT_NAME,
|
|
758
|
-
PALETTE_NAMES,
|
|
759
|
-
PALETTE_PRESET_NAMES,
|
|
760
|
-
resolvePreset,
|
|
761
|
-
segmentPaletteForPreset,
|
|
762
|
-
INFORMATION_PROFILE_NAMES,
|
|
763
|
-
INFORMATION_PROFILES,
|
|
764
|
-
inferInformationProfile,
|
|
765
|
-
DEFAULT_EXTENSION_STATUS_ICONS,
|
|
766
|
-
DEFAULT_STATUSLINE_DOCUMENT,
|
|
767
|
-
settingsFilePath,
|
|
768
|
-
loadStatuslineSettings,
|
|
769
|
-
loadStatuslineSettingsForAgent,
|
|
770
|
-
saveStatuslineSettingsDocument,
|
|
771
|
-
removeStatuslineSettingsDocumentIfMatches,
|
|
772
|
-
consumeStatuslineSettingsNotice,
|
|
773
|
-
readStatuslineSettings,
|
|
774
|
-
normalizeStatuslineSettings
|
|
775
|
-
};
|
|
776
|
-
//# sourceMappingURL=chunk-6BDIM4OT.ts.map
|