@selesai/code 0.7.0 → 0.8.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/CHANGELOG.md +11 -0
- package/README.md +4 -2
- package/dist/extensions/agent-browser.test.ts +195 -0
- package/dist/extensions/agent-browser.ts +158 -0
- package/dist/extensions/inline-skills.test.ts +14 -0
- package/dist/extensions/inline-skills.ts +14 -7
- package/dist/extensions/package.json +2 -1
- package/dist/extensions/pi-powerline-footer/.github/workflows/test.yml +27 -0
- package/dist/extensions/pi-powerline-footer/CHANGELOG.md +153 -0
- package/dist/extensions/pi-powerline-footer/CONTRIBUTING.md +16 -0
- package/dist/extensions/pi-powerline-footer/README.md +192 -44
- package/dist/extensions/pi-powerline-footer/bash-mode/completion.ts +19 -4
- package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +80 -25
- package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +81 -29
- package/dist/extensions/pi-powerline-footer/bash-mode/types.ts +1 -1
- package/dist/extensions/pi-powerline-footer/cd-command.ts +190 -0
- package/dist/extensions/pi-powerline-footer/colors.ts +2 -1
- package/dist/extensions/pi-powerline-footer/context-usage.ts +121 -6
- package/dist/extensions/pi-powerline-footer/currency-rates.ts +142 -0
- package/dist/extensions/pi-powerline-footer/editor-composition.ts +32 -0
- package/dist/extensions/pi-powerline-footer/git-status.ts +108 -6
- package/dist/extensions/pi-powerline-footer/icons.ts +11 -2
- package/dist/extensions/pi-powerline-footer/index.ts +1056 -324
- package/dist/extensions/pi-powerline-footer/lifecycle.ts +10 -0
- package/dist/extensions/pi-powerline-footer/package.json +10 -6
- package/dist/extensions/pi-powerline-footer/paths.ts +22 -0
- package/dist/extensions/pi-powerline-footer/powerline-config.ts +297 -15
- package/dist/extensions/pi-powerline-footer/presets.ts +6 -25
- package/dist/extensions/pi-powerline-footer/queue/store.ts +361 -0
- package/dist/extensions/pi-powerline-footer/queue/types.ts +54 -0
- package/dist/extensions/pi-powerline-footer/render-scheduler.ts +12 -7
- package/dist/extensions/pi-powerline-footer/segments.ts +114 -107
- package/dist/extensions/pi-powerline-footer/session-usage.ts +1 -1
- package/dist/extensions/pi-powerline-footer/shortcuts.ts +17 -3
- package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +189 -17
- package/dist/extensions/pi-powerline-footer/tests/cd-command.test.ts +144 -0
- package/dist/extensions/pi-powerline-footer/tests/context-usage.test.ts +120 -2
- package/dist/extensions/pi-powerline-footer/tests/custom-items.test.ts +245 -22
- package/dist/extensions/pi-powerline-footer/tests/editor-composition.test.ts +48 -0
- package/dist/extensions/pi-powerline-footer/tests/editor-responsiveness.test.ts +34 -0
- package/dist/extensions/pi-powerline-footer/tests/fixed-editor-border.test.ts +39 -0
- package/dist/extensions/pi-powerline-footer/tests/git-status.test.ts +92 -0
- package/dist/extensions/pi-powerline-footer/tests/guide.test.ts +4 -1
- package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +33 -197
- package/dist/extensions/pi-powerline-footer/tests/mode-aware-trigger.test.ts +79 -0
- package/dist/extensions/pi-powerline-footer/tests/paths.test.ts +63 -0
- package/dist/extensions/pi-powerline-footer/tests/queue-store.test.ts +195 -0
- package/dist/extensions/pi-powerline-footer/tests/quit-cursor.test.ts +14 -0
- package/dist/extensions/pi-powerline-footer/tests/remaining-regressions.test.ts +167 -0
- package/dist/extensions/pi-powerline-footer/tests/stash-shortcut.test.ts +23 -18
- package/dist/extensions/pi-powerline-footer/tests/thinking-segment.test.ts +16 -1
- package/dist/extensions/pi-powerline-footer/tests/token-stats.test.ts +249 -0
- package/dist/extensions/pi-powerline-footer/tests/usage-display.test.ts +237 -0
- package/dist/extensions/pi-powerline-footer/tests/welcome.test.ts +199 -0
- package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +191 -100
- package/dist/extensions/pi-powerline-footer/theme.ts +32 -18
- package/dist/extensions/pi-powerline-footer/token-stats.ts +304 -0
- package/dist/extensions/pi-powerline-footer/tsconfig.json +13 -0
- package/dist/extensions/pi-powerline-footer/types.ts +74 -29
- package/dist/extensions/pi-powerline-footer/welcome-dismiss.ts +34 -0
- package/dist/extensions/pi-powerline-footer/welcome.ts +65 -10
- package/dist/extensions/pi-powerline-footer/working-vibes.ts +44 -11
- package/dist/extensions/pi-subagents/package-lock.json +2 -2
- package/dist/extensions/pi-subagents/package.json +1 -1
- package/dist/extensions/pi-subagents/src/slash/inline-subagents.ts +57 -24
- package/dist/extensions/pi-subagents/test/unit/inline-subagents.test.ts +82 -7
- package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +1 -1
- package/dist/extensions/ponytail/index.js +1 -31
- package/dist/extensions/ponytail/test/extension.test.js +5 -22
- package/dist/extensions/rtk.test.ts +27 -12
- package/dist/extensions/rtk.ts +41 -35
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function shouldShowStartupWelcome(reason: unknown, welcomeEnabled: boolean): boolean {
|
|
2
|
+
return reason === "startup" && welcomeEnabled;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isStaleExtensionContextError(error: unknown): boolean {
|
|
6
|
+
return error instanceof Error && (
|
|
7
|
+
error.message.includes("This extension instance is stale")
|
|
8
|
+
|| error.message.includes("This extension ctx is stale")
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-powerline-footer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"description": "Powerline-style status bar extension for pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"*.json",
|
|
10
10
|
"!progress.md",
|
|
11
11
|
"bash-mode/**/*.ts",
|
|
12
|
-
"fixed-editor/**/*.ts"
|
|
12
|
+
"fixed-editor/**/*.ts",
|
|
13
|
+
"queue/**/*.ts"
|
|
13
14
|
],
|
|
14
15
|
"keywords": [
|
|
15
16
|
"pi-package",
|
|
@@ -26,17 +27,20 @@
|
|
|
26
27
|
"url": "git+https://github.com/nicobailon/pi-powerline-footer.git"
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
29
31
|
"test": "node --experimental-strip-types --test tests/**/*.test.ts"
|
|
30
32
|
},
|
|
31
33
|
"peerDependencies": {
|
|
32
|
-
"@earendil-works/pi-ai": "
|
|
34
|
+
"@earendil-works/pi-ai": ">=0.81.0 <0.84.0",
|
|
33
35
|
"@selesai/code": "*",
|
|
34
|
-
"@earendil-works/pi-tui": "
|
|
36
|
+
"@earendil-works/pi-tui": ">=0.81.0 <0.84.0"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
|
-
"@earendil-works/pi-ai": "^0.
|
|
39
|
+
"@earendil-works/pi-ai": "^0.83.0",
|
|
38
40
|
"@selesai/code": "^0.1.0",
|
|
39
|
-
"@earendil-works/pi-tui": "^0.
|
|
41
|
+
"@earendil-works/pi-tui": "^0.83.0",
|
|
42
|
+
"@types/node": "24.13.3",
|
|
43
|
+
"typescript": "5.9.3"
|
|
40
44
|
},
|
|
41
45
|
"pi": {
|
|
42
46
|
"extensions": [
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { CONFIG_DIR_NAME, getAgentDir } from "@selesai/code";
|
|
5
|
+
|
|
6
|
+
export function getHomeDir(): string {
|
|
7
|
+
return process.env.HOME || process.env.USERPROFILE || homedir();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getAgentPath(...segments: string[]): string {
|
|
11
|
+
return join(getAgentDir(), ...segments);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getLegacyPiPath(...segments: string[]): string {
|
|
15
|
+
return join(getHomeDir(), CONFIG_DIR_NAME, ...segments);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getAgentSessionDirs(): string[] {
|
|
19
|
+
const primary = getAgentPath("sessions");
|
|
20
|
+
const legacy = getLegacyPiPath("sessions");
|
|
21
|
+
return existsSync(legacy) && legacy !== primary ? [primary, legacy] : [primary];
|
|
22
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
2
|
-
import
|
|
2
|
+
import { normalizeCostCurrency } from "./currency-rates.ts";
|
|
3
|
+
import { BUILTIN_STATUS_LINE_SEGMENT_IDS } from "./types.ts";
|
|
4
|
+
import type { ColorValue, CustomItemPosition, CustomStatusItem, PowerlinePlacement, PresetDef, StatusLineLayout, StatusLinePreset, StatusLineSegmentId, StatusLineSegmentOptions, StatusLineSeparatorStyle } from "./types.ts";
|
|
3
5
|
|
|
4
6
|
export interface PowerlineConfig {
|
|
5
7
|
preset: StatusLinePreset;
|
|
@@ -7,6 +9,17 @@ export interface PowerlineConfig {
|
|
|
7
9
|
mouseScroll: boolean;
|
|
8
10
|
fixedEditor: boolean;
|
|
9
11
|
fixedEditorPromptGlyph: string;
|
|
12
|
+
disabledSegments: StatusLineSegmentId[];
|
|
13
|
+
invalidDisabledSegments: string[];
|
|
14
|
+
layout: StatusLineLayout | null;
|
|
15
|
+
invalidLayoutSegments: string[];
|
|
16
|
+
separator: StatusLineSeparatorStyle | null;
|
|
17
|
+
segmentOptions: StatusLineSegmentOptions;
|
|
18
|
+
placement: PowerlinePlacement;
|
|
19
|
+
invalidPlacement: string | null;
|
|
20
|
+
welcome: boolean;
|
|
21
|
+
stashSharpSShortcut: boolean;
|
|
22
|
+
queue: { captureSigil: string | false };
|
|
10
23
|
}
|
|
11
24
|
|
|
12
25
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -19,6 +32,41 @@ function normalizePreset(value: unknown, presets: readonly StatusLinePreset[]):
|
|
|
19
32
|
return (presets as readonly string[]).includes(normalized) ? (normalized as StatusLinePreset) : null;
|
|
20
33
|
}
|
|
21
34
|
|
|
35
|
+
function normalizePlacement(value: unknown): { placement: PowerlinePlacement; invalidPlacement: string | null } {
|
|
36
|
+
if (value === undefined) return { placement: "above", invalidPlacement: null };
|
|
37
|
+
|
|
38
|
+
const normalized = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
39
|
+
if (normalized === "above" || normalized === "below") {
|
|
40
|
+
return { placement: normalized, invalidPlacement: null };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
placement: "above",
|
|
45
|
+
invalidPlacement: typeof value === "string" ? value.trim() : String(value),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const SEPARATOR_STYLES = [
|
|
50
|
+
"powerline",
|
|
51
|
+
"powerline-thin",
|
|
52
|
+
"slash",
|
|
53
|
+
"pipe",
|
|
54
|
+
"block",
|
|
55
|
+
"none",
|
|
56
|
+
"ascii",
|
|
57
|
+
"dot",
|
|
58
|
+
"chevron",
|
|
59
|
+
"star",
|
|
60
|
+
] as const satisfies readonly StatusLineSeparatorStyle[];
|
|
61
|
+
|
|
62
|
+
function normalizeSeparator(value: unknown): StatusLineSeparatorStyle | null {
|
|
63
|
+
if (typeof value !== "string") return null;
|
|
64
|
+
const normalized = value.trim().toLowerCase();
|
|
65
|
+
return (SEPARATOR_STYLES as readonly string[]).includes(normalized)
|
|
66
|
+
? (normalized as StatusLineSeparatorStyle)
|
|
67
|
+
: null;
|
|
68
|
+
}
|
|
69
|
+
|
|
22
70
|
function normalizeCustomItemId(value: unknown): string | null {
|
|
23
71
|
if (typeof value !== "string") return null;
|
|
24
72
|
const normalized = value.trim();
|
|
@@ -43,6 +91,13 @@ function normalizeCustomPrefix(value: unknown): string | undefined {
|
|
|
43
91
|
return normalized ? normalized : undefined;
|
|
44
92
|
}
|
|
45
93
|
|
|
94
|
+
function normalizeCaptureSigil(value: unknown): string | false {
|
|
95
|
+
if (value === false) return false;
|
|
96
|
+
if (typeof value !== "string") return "#";
|
|
97
|
+
const normalized = value.trim();
|
|
98
|
+
return normalized && !/\s/.test(normalized) ? normalized : "#";
|
|
99
|
+
}
|
|
100
|
+
|
|
46
101
|
function normalizeCustomStatusItem(raw: unknown, idOverride?: string): CustomStatusItem | null {
|
|
47
102
|
if (!isRecord(raw)) return null;
|
|
48
103
|
const id = normalizeCustomItemId(idOverride ?? raw.id);
|
|
@@ -84,40 +139,267 @@ function normalizeCustomItems(raw: unknown): CustomStatusItem[] {
|
|
|
84
139
|
return [...deduped.values()];
|
|
85
140
|
}
|
|
86
141
|
|
|
142
|
+
const BUILTIN_STATUS_LINE_SEGMENT_ID_SET = new Set<string>(BUILTIN_STATUS_LINE_SEGMENT_IDS);
|
|
143
|
+
|
|
144
|
+
function normalizeStatusLineSegmentId(value: unknown, customItemIds: ReadonlySet<string>): StatusLineSegmentId | null {
|
|
145
|
+
if (typeof value !== "string") return null;
|
|
146
|
+
|
|
147
|
+
const normalized = value.trim();
|
|
148
|
+
if (BUILTIN_STATUS_LINE_SEGMENT_ID_SET.has(normalized)) {
|
|
149
|
+
return normalized as StatusLineSegmentId;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const customId = normalized.startsWith("custom:")
|
|
153
|
+
? normalizeCustomItemId(normalized.slice("custom:".length))
|
|
154
|
+
: null;
|
|
155
|
+
return customId && customItemIds.has(customId) ? `custom:${customId}` : null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function normalizeDisabledSegments(
|
|
159
|
+
raw: unknown,
|
|
160
|
+
customItems: readonly CustomStatusItem[],
|
|
161
|
+
): { disabledSegments: StatusLineSegmentId[]; invalidDisabledSegments: string[] } {
|
|
162
|
+
if (!Array.isArray(raw)) return { disabledSegments: [], invalidDisabledSegments: [] };
|
|
163
|
+
|
|
164
|
+
const disabledSegments: StatusLineSegmentId[] = [];
|
|
165
|
+
const invalidDisabledSegments: string[] = [];
|
|
166
|
+
const customItemIds = new Set(customItems.map((item) => item.id));
|
|
167
|
+
const seen = new Set<StatusLineSegmentId>();
|
|
168
|
+
|
|
169
|
+
for (const entry of raw) {
|
|
170
|
+
const segmentId = normalizeStatusLineSegmentId(entry, customItemIds);
|
|
171
|
+
if (!segmentId) {
|
|
172
|
+
invalidDisabledSegments.push(typeof entry === "string" ? entry.trim() : String(entry));
|
|
173
|
+
} else if (!seen.has(segmentId)) {
|
|
174
|
+
seen.add(segmentId);
|
|
175
|
+
disabledSegments.push(segmentId);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { disabledSegments, invalidDisabledSegments };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function normalizeLayout(
|
|
183
|
+
raw: unknown,
|
|
184
|
+
customItems: readonly CustomStatusItem[],
|
|
185
|
+
): { layout: StatusLineLayout | null; invalidLayoutSegments: string[] } {
|
|
186
|
+
if (!isRecord(raw)) return { layout: null, invalidLayoutSegments: [] };
|
|
187
|
+
|
|
188
|
+
const layout: StatusLineLayout = {};
|
|
189
|
+
const invalidLayoutSegments: string[] = [];
|
|
190
|
+
const customItemIds = new Set(customItems.map((item) => item.id));
|
|
191
|
+
const globallyPlaced = new Set<StatusLineSegmentId>();
|
|
192
|
+
|
|
193
|
+
for (const row of ["left", "right", "secondary"] as const) {
|
|
194
|
+
const entries = raw[row];
|
|
195
|
+
if (!Array.isArray(entries)) continue;
|
|
196
|
+
|
|
197
|
+
const segments: StatusLineSegmentId[] = [];
|
|
198
|
+
const seen = new Set<StatusLineSegmentId>();
|
|
199
|
+
for (const entry of entries) {
|
|
200
|
+
const segmentId = normalizeStatusLineSegmentId(entry, customItemIds);
|
|
201
|
+
if (!segmentId) {
|
|
202
|
+
invalidLayoutSegments.push(`${row}:${typeof entry === "string" ? entry.trim() : String(entry)}`);
|
|
203
|
+
} else if (!seen.has(segmentId)) {
|
|
204
|
+
seen.add(segmentId);
|
|
205
|
+
if (globallyPlaced.has(segmentId)) {
|
|
206
|
+
invalidLayoutSegments.push(`${row}:${segmentId}`);
|
|
207
|
+
} else {
|
|
208
|
+
globallyPlaced.add(segmentId);
|
|
209
|
+
segments.push(segmentId);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
layout[row] = segments;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return Object.keys(layout).length > 0
|
|
217
|
+
? { layout, invalidLayoutSegments }
|
|
218
|
+
: { layout: null, invalidLayoutSegments };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function normalizeSegmentOptions(raw: Record<string, unknown>): StatusLineSegmentOptions {
|
|
222
|
+
const options: StatusLineSegmentOptions = {};
|
|
223
|
+
|
|
224
|
+
if (isRecord(raw.model)) {
|
|
225
|
+
options.model = {
|
|
226
|
+
...(typeof raw.model.showThinkingLevel === "boolean" ? { showThinkingLevel: raw.model.showThinkingLevel } : {}),
|
|
227
|
+
...(raw.model.display === "name" || raw.model.display === "qualified" ? { display: raw.model.display } : {}),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (isRecord(raw.path)) {
|
|
232
|
+
options.path = {
|
|
233
|
+
...(raw.path.mode === "basename" || raw.path.mode === "abbreviated" || raw.path.mode === "full" ? { mode: raw.path.mode } : {}),
|
|
234
|
+
...(typeof raw.path.maxLength === "number" && Number.isFinite(raw.path.maxLength) && raw.path.maxLength > 0
|
|
235
|
+
? { maxLength: Math.floor(raw.path.maxLength) }
|
|
236
|
+
: {}),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (isRecord(raw.git)) {
|
|
241
|
+
options.git = {
|
|
242
|
+
...(typeof raw.git.showBranch === "boolean" ? { showBranch: raw.git.showBranch } : {}),
|
|
243
|
+
...(typeof raw.git.showStaged === "boolean" ? { showStaged: raw.git.showStaged } : {}),
|
|
244
|
+
...(typeof raw.git.showUnstaged === "boolean" ? { showUnstaged: raw.git.showUnstaged } : {}),
|
|
245
|
+
...(typeof raw.git.showUntracked === "boolean" ? { showUntracked: raw.git.showUntracked } : {}),
|
|
246
|
+
...(raw.git.polling === "full" || raw.git.polling === "branch" || raw.git.polling === "off" ? { polling: raw.git.polling } : {}),
|
|
247
|
+
...(typeof raw.git.hostIcon === "boolean" ? { hostIcon: raw.git.hostIcon } : {}),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (isRecord(raw.time)) {
|
|
252
|
+
options.time = {
|
|
253
|
+
...(raw.time.format === "12h" || raw.time.format === "24h" ? { format: raw.time.format } : {}),
|
|
254
|
+
...(typeof raw.time.showSeconds === "boolean" ? { showSeconds: raw.time.showSeconds } : {}),
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (isRecord(raw.cost)) {
|
|
259
|
+
const currency = normalizeCostCurrency(raw.cost.currency);
|
|
260
|
+
options.cost = {
|
|
261
|
+
...(raw.cost.subscriptionDisplay === "subscription"
|
|
262
|
+
|| raw.cost.subscriptionDisplay === "reported-cost"
|
|
263
|
+
|| raw.cost.subscriptionDisplay === "both"
|
|
264
|
+
? { subscriptionDisplay: raw.cost.subscriptionDisplay }
|
|
265
|
+
: {}),
|
|
266
|
+
...(currency ? { currency } : {}),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (isRecord(raw.context)) {
|
|
271
|
+
options.context = {
|
|
272
|
+
...(raw.context.format === "full" || raw.context.format === "percent" ? { format: raw.context.format } : {}),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (isRecord(raw.cache_read)) {
|
|
277
|
+
options.cache_read = {
|
|
278
|
+
...(raw.cache_read.format === "tokens" || raw.cache_read.format === "percent" || raw.cache_read.format === "both"
|
|
279
|
+
? { format: raw.cache_read.format }
|
|
280
|
+
: {}),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return options;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function mergeSegmentOptions(
|
|
288
|
+
defaults: StatusLineSegmentOptions = {},
|
|
289
|
+
overrides: StatusLineSegmentOptions = {},
|
|
290
|
+
): StatusLineSegmentOptions {
|
|
291
|
+
return {
|
|
292
|
+
...defaults,
|
|
293
|
+
...overrides,
|
|
294
|
+
model: { ...defaults.model, ...overrides.model },
|
|
295
|
+
path: { ...defaults.path, ...overrides.path },
|
|
296
|
+
git: { ...defaults.git, ...overrides.git },
|
|
297
|
+
time: { ...defaults.time, ...overrides.time },
|
|
298
|
+
cost: { ...defaults.cost, ...overrides.cost },
|
|
299
|
+
context: { ...defaults.context, ...overrides.context },
|
|
300
|
+
cache_read: { ...defaults.cache_read, ...overrides.cache_read },
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
87
304
|
export function parsePowerlineConfig(value: unknown, presets: readonly StatusLinePreset[]): PowerlineConfig {
|
|
88
|
-
const defaultConfig: PowerlineConfig = {
|
|
305
|
+
const defaultConfig: PowerlineConfig = {
|
|
306
|
+
preset: "default",
|
|
307
|
+
customItems: [],
|
|
308
|
+
mouseScroll: true,
|
|
309
|
+
fixedEditor: true,
|
|
310
|
+
fixedEditorPromptGlyph: "",
|
|
311
|
+
disabledSegments: [],
|
|
312
|
+
invalidDisabledSegments: [],
|
|
313
|
+
layout: null,
|
|
314
|
+
invalidLayoutSegments: [],
|
|
315
|
+
separator: null,
|
|
316
|
+
segmentOptions: {},
|
|
317
|
+
placement: "above",
|
|
318
|
+
invalidPlacement: null,
|
|
319
|
+
welcome: true,
|
|
320
|
+
stashSharpSShortcut: false,
|
|
321
|
+
queue: { captureSigil: "#" },
|
|
322
|
+
};
|
|
89
323
|
|
|
90
324
|
const directPreset = normalizePreset(value, presets);
|
|
91
325
|
if (directPreset) return { ...defaultConfig, preset: directPreset };
|
|
92
326
|
|
|
93
327
|
if (!isRecord(value)) return defaultConfig;
|
|
94
328
|
|
|
329
|
+
const customItems = normalizeCustomItems(value.customItems);
|
|
330
|
+
const { disabledSegments, invalidDisabledSegments } = normalizeDisabledSegments(value.disabledSegments, customItems);
|
|
331
|
+
const { layout, invalidLayoutSegments } = normalizeLayout(value.layout, customItems);
|
|
332
|
+
const { placement, invalidPlacement } = normalizePlacement(value.placement);
|
|
333
|
+
const queue = isRecord(value.queue)
|
|
334
|
+
? { captureSigil: normalizeCaptureSigil(value.queue.captureSigil) }
|
|
335
|
+
: defaultConfig.queue;
|
|
336
|
+
|
|
95
337
|
return {
|
|
96
338
|
preset: normalizePreset(value.preset, presets) ?? defaultConfig.preset,
|
|
97
|
-
customItems
|
|
339
|
+
customItems,
|
|
98
340
|
mouseScroll: value.mouseScroll !== false,
|
|
99
341
|
fixedEditor: value.fixedEditor !== false,
|
|
100
342
|
fixedEditorPromptGlyph: typeof value.fixedEditorPromptGlyph === "string" ? value.fixedEditorPromptGlyph : defaultConfig.fixedEditorPromptGlyph,
|
|
343
|
+
disabledSegments,
|
|
344
|
+
invalidDisabledSegments,
|
|
345
|
+
layout,
|
|
346
|
+
invalidLayoutSegments,
|
|
347
|
+
separator: normalizeSeparator(value.separator),
|
|
348
|
+
segmentOptions: normalizeSegmentOptions(value),
|
|
349
|
+
placement,
|
|
350
|
+
invalidPlacement,
|
|
351
|
+
welcome: value.welcome !== false,
|
|
352
|
+
stashSharpSShortcut: value.stashSharpSShortcut === true,
|
|
353
|
+
queue,
|
|
101
354
|
};
|
|
102
355
|
}
|
|
103
356
|
|
|
104
|
-
export function mergeSegmentsWithCustomItems(
|
|
357
|
+
export function mergeSegmentsWithCustomItems(
|
|
358
|
+
presetDef: PresetDef,
|
|
359
|
+
customItems: readonly CustomStatusItem[],
|
|
360
|
+
options: {
|
|
361
|
+
layout?: StatusLineLayout | null;
|
|
362
|
+
disabledSegments?: readonly StatusLineSegmentId[];
|
|
363
|
+
} = {},
|
|
364
|
+
): {
|
|
105
365
|
leftSegments: StatusLineSegmentId[];
|
|
106
366
|
rightSegments: StatusLineSegmentId[];
|
|
107
367
|
secondarySegments: StatusLineSegmentId[];
|
|
108
368
|
} {
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
369
|
+
const layout = options.layout ?? null;
|
|
370
|
+
const explicitlyPlaced = new Set([
|
|
371
|
+
...(layout?.left ?? []),
|
|
372
|
+
...(layout?.right ?? []),
|
|
373
|
+
...(layout?.secondary ?? []),
|
|
374
|
+
]);
|
|
375
|
+
const disabled = new Set(options.disabledSegments ?? []);
|
|
112
376
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
377
|
+
const buildRow = (
|
|
378
|
+
position: CustomItemPosition,
|
|
379
|
+
configured: StatusLineSegmentId[] | undefined,
|
|
380
|
+
presetSegments: readonly StatusLineSegmentId[],
|
|
381
|
+
): StatusLineSegmentId[] => {
|
|
382
|
+
const segments = configured !== undefined
|
|
383
|
+
? [...configured]
|
|
384
|
+
: presetSegments.filter((id) => !explicitlyPlaced.has(id));
|
|
385
|
+
|
|
386
|
+
if (configured === undefined) {
|
|
387
|
+
for (const item of customItems) {
|
|
388
|
+
const segmentId: StatusLineSegmentId = `custom:${item.id}`;
|
|
389
|
+
if (item.position === position && !explicitlyPlaced.has(segmentId)) {
|
|
390
|
+
segments.push(segmentId);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
119
394
|
|
|
120
|
-
|
|
395
|
+
return segments.filter((id) => !disabled.has(id));
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
leftSegments: buildRow("left", layout?.left, presetDef.leftSegments),
|
|
400
|
+
rightSegments: buildRow("right", layout?.right, presetDef.rightSegments),
|
|
401
|
+
secondarySegments: buildRow("secondary", layout?.secondary, presetDef.secondarySegments ?? []),
|
|
402
|
+
};
|
|
121
403
|
}
|
|
122
404
|
|
|
123
405
|
export function nextPowerlineSettingWithPreset(existingPowerlineSetting: unknown, preset: StatusLinePreset): unknown {
|
|
@@ -129,7 +411,7 @@ export function nextPowerlineSettingWithPreset(existingPowerlineSetting: unknown
|
|
|
129
411
|
|
|
130
412
|
export function nextPowerlineSettingWithOptions(
|
|
131
413
|
existingPowerlineSetting: unknown,
|
|
132
|
-
updates: Partial<Pick<PowerlineConfig, "
|
|
414
|
+
updates: Partial<Pick<PowerlineConfig, "welcome" | "stashSharpSShortcut" | "placement">>,
|
|
133
415
|
currentPreset: StatusLinePreset,
|
|
134
416
|
): unknown {
|
|
135
417
|
if (!isRecord(existingPowerlineSetting)) {
|
|
@@ -23,9 +23,9 @@ const NERD_COLORS: ColorScheme = {
|
|
|
23
23
|
|
|
24
24
|
export const PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
25
25
|
default: {
|
|
26
|
-
leftSegments: ["model", "thinking", "shell_mode", "path", "git", "context_pct", "cache_read", "cost"],
|
|
26
|
+
leftSegments: ["model", "thinking", "shell_mode", "path", "git", "queue", "context_pct", "cache_read", "cost"],
|
|
27
27
|
rightSegments: [],
|
|
28
|
-
secondarySegments: ["
|
|
28
|
+
secondarySegments: ["extension_statuses"],
|
|
29
29
|
separator: "powerline-thin",
|
|
30
30
|
colors: DEFAULT_COLORS,
|
|
31
31
|
segmentOptions: {
|
|
@@ -38,7 +38,6 @@ export const PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
38
38
|
minimal: {
|
|
39
39
|
leftSegments: ["shell_mode", "path", "git"],
|
|
40
40
|
rightSegments: ["context_pct"],
|
|
41
|
-
secondarySegments: ["session_name"],
|
|
42
41
|
separator: "slash",
|
|
43
42
|
colors: MINIMAL_COLORS,
|
|
44
43
|
segmentOptions: {
|
|
@@ -49,8 +48,7 @@ export const PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
49
48
|
|
|
50
49
|
compact: {
|
|
51
50
|
leftSegments: ["model", "shell_mode", "git"],
|
|
52
|
-
rightSegments: ["cost", "context_pct"],
|
|
53
|
-
secondarySegments: ["session_name"],
|
|
51
|
+
rightSegments: ["queue", "cost", "context_pct"],
|
|
54
52
|
separator: "powerline-thin",
|
|
55
53
|
colors: DEFAULT_COLORS,
|
|
56
54
|
segmentOptions: {
|
|
@@ -60,9 +58,8 @@ export const PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
60
58
|
},
|
|
61
59
|
|
|
62
60
|
full: {
|
|
63
|
-
leftSegments: ["hostname", "model", "thinking", "shell_mode", "path", "git", "subagents"],
|
|
61
|
+
leftSegments: ["hostname", "model", "thinking", "shell_mode", "path", "git", "queue", "subagents"],
|
|
64
62
|
rightSegments: ["token_in", "token_out", "cache_read", "cost", "context_pct", "time_spent", "time", "extension_statuses"],
|
|
65
|
-
secondarySegments: ["session_name"],
|
|
66
63
|
separator: "powerline",
|
|
67
64
|
colors: DEFAULT_COLORS,
|
|
68
65
|
segmentOptions: {
|
|
@@ -74,9 +71,8 @@ export const PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
74
71
|
},
|
|
75
72
|
|
|
76
73
|
nerd: {
|
|
77
|
-
leftSegments: ["hostname", "model", "thinking", "shell_mode", "path", "git", "session", "subagents"],
|
|
74
|
+
leftSegments: ["hostname", "model", "thinking", "shell_mode", "path", "git", "queue", "session", "subagents"],
|
|
78
75
|
rightSegments: ["token_in", "token_out", "cache_read", "cache_write", "cost", "context_pct", "context_total", "time_spent", "time", "extension_statuses"],
|
|
79
|
-
secondarySegments: ["session_name"],
|
|
80
76
|
separator: "powerline",
|
|
81
77
|
colors: NERD_COLORS,
|
|
82
78
|
segmentOptions: {
|
|
@@ -89,8 +85,7 @@ export const PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
89
85
|
|
|
90
86
|
ascii: {
|
|
91
87
|
leftSegments: ["model", "shell_mode", "path", "git"],
|
|
92
|
-
rightSegments: ["token_total", "cost", "context_pct"],
|
|
93
|
-
secondarySegments: ["session_name"],
|
|
88
|
+
rightSegments: ["queue", "token_total", "cost", "context_pct"],
|
|
94
89
|
separator: "ascii",
|
|
95
90
|
colors: MINIMAL_COLORS,
|
|
96
91
|
segmentOptions: {
|
|
@@ -100,20 +95,6 @@ export const PRESETS: Record<StatusLinePreset, PresetDef> = {
|
|
|
100
95
|
},
|
|
101
96
|
},
|
|
102
97
|
|
|
103
|
-
custom: {
|
|
104
|
-
// {model} {thinking-without-think-prefix} | {path-full-relative-home} | {git} | {subagents}
|
|
105
|
-
// | {token_in}({cache_read}) · {token_out} {token_total} · {cost} | {context_pct}
|
|
106
|
-
leftSegments: ["identity_group", "path", "git", "subagents"],
|
|
107
|
-
rightSegments: ["context_pct"],
|
|
108
|
-
secondarySegments: ["session_name", "usage_group"],
|
|
109
|
-
separator: "pipe",
|
|
110
|
-
colors: DEFAULT_COLORS,
|
|
111
|
-
segmentOptions: {
|
|
112
|
-
model: { showThinkingLevel: false },
|
|
113
|
-
path: { mode: "full" },
|
|
114
|
-
git: { showBranch: true, showStaged: true, showUnstaged: true, showUntracked: true },
|
|
115
|
-
},
|
|
116
|
-
},
|
|
117
98
|
};
|
|
118
99
|
|
|
119
100
|
export function getPreset(name: StatusLinePreset): PresetDef {
|