@pi-unipi/footer 0.1.3 → 2.0.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/README.md +73 -158
- package/package.json +1 -1
- package/src/commands.ts +38 -123
- package/src/config.ts +16 -0
- package/src/help.ts +160 -0
- package/src/index.ts +48 -15
- package/src/presets.ts +41 -32
- package/src/registry/index.ts +1 -1
- package/src/rendering/icons.ts +77 -59
- package/src/rendering/renderer.ts +246 -80
- package/src/rendering/theme.ts +56 -29
- package/src/segments/compactor.ts +76 -30
- package/src/segments/core.ts +124 -15
- package/src/segments/kanboard.ts +25 -9
- package/src/segments/mcp.ts +25 -16
- package/src/segments/memory.ts +9 -6
- package/src/segments/notify.ts +16 -5
- package/src/segments/ralph.ts +23 -8
- package/src/segments/status-ext.ts +1 -1
- package/src/segments/workflow.ts +41 -18
- package/src/tps-tracker.ts +204 -0
- package/src/tui/settings-tui.ts +253 -63
- package/src/types.ts +51 -12
package/src/help.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @pi-unipi/footer — Footer help overlay
|
|
3
|
+
*
|
|
4
|
+
* Shows an overlay listing all enabled segments grouped by zone,
|
|
5
|
+
* with icons, short labels, and descriptions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
import type { FooterSegment, SegmentZone } from "./types.js";
|
|
10
|
+
import { getIcon } from "./rendering/icons.js";
|
|
11
|
+
import { loadFooterSettings, isSegmentEnabled } from "./config.js";
|
|
12
|
+
import { getPreset } from "./presets.js";
|
|
13
|
+
|
|
14
|
+
/** Zone display names and order */
|
|
15
|
+
const ZONE_META: Record<SegmentZone, { title: string; order: number }> = {
|
|
16
|
+
left: { title: "LEFT ZONE (Identity)", order: 0 },
|
|
17
|
+
center: { title: "CENTER ZONE (Metrics)", order: 1 },
|
|
18
|
+
right: { title: "RIGHT ZONE (Time)", order: 2 },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Build the help content lines */
|
|
22
|
+
function buildHelpLines(
|
|
23
|
+
segments: FooterSegment[],
|
|
24
|
+
presetName: string,
|
|
25
|
+
): string[] {
|
|
26
|
+
const settings = loadFooterSettings();
|
|
27
|
+
const preset = getPreset(presetName);
|
|
28
|
+
const enabledIds = new Set([
|
|
29
|
+
...preset.leftSegments,
|
|
30
|
+
...preset.rightSegments,
|
|
31
|
+
...preset.secondarySegments,
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
// Filter to enabled segments only
|
|
35
|
+
const enabled = segments.filter(seg => {
|
|
36
|
+
if (!enabledIds.has(seg.id)) return false;
|
|
37
|
+
return isSegmentEnabled(getGroupForSegment(seg.id), seg.id);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (enabled.length === 0) {
|
|
41
|
+
return ["No segments enabled."];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Group by zone
|
|
45
|
+
const zones: Record<SegmentZone, FooterSegment[]> = { left: [], center: [], right: [] };
|
|
46
|
+
for (const seg of enabled) {
|
|
47
|
+
zones[seg.zone].push(seg);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const lines: string[] = [];
|
|
51
|
+
|
|
52
|
+
for (const zoneKey of (["left", "center", "right"] as SegmentZone[])) {
|
|
53
|
+
const zoneSegs = zones[zoneKey];
|
|
54
|
+
if (zoneSegs.length === 0) continue;
|
|
55
|
+
|
|
56
|
+
const meta = ZONE_META[zoneKey];
|
|
57
|
+
lines.push(` ${meta.title}`);
|
|
58
|
+
lines.push("");
|
|
59
|
+
|
|
60
|
+
for (const seg of zoneSegs) {
|
|
61
|
+
const icon = getIcon(seg.id);
|
|
62
|
+
const label = seg.shortLabel;
|
|
63
|
+
const desc = seg.description;
|
|
64
|
+
const iconStr = icon ? `${icon} ` : " ";
|
|
65
|
+
lines.push(` ${iconStr}${label.padEnd(6)} ${desc}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
lines.push("");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return lines;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Simple group lookup for help */
|
|
75
|
+
function getGroupForSegment(segId: string): string {
|
|
76
|
+
const coreIds = ["model", "api_state", "tool_count", "git", "context_pct", "cost", "tokens_total", "tokens_in", "tokens_out", "session", "hostname", "time", "tps", "clock", "duration", "thinking_level"];
|
|
77
|
+
if (coreIds.includes(segId)) return "core";
|
|
78
|
+
const compactorIds = ["session_events", "compactions", "tokens_saved", "compression_ratio", "cocoindex_status", "sandbox_runs", "search_queries"];
|
|
79
|
+
if (compactorIds.includes(segId)) return "compactor";
|
|
80
|
+
if (["project_count", "total_count", "consolidations"].includes(segId)) return "memory";
|
|
81
|
+
if (["servers_total", "servers_active", "tools_total", "servers_failed"].includes(segId)) return "mcp";
|
|
82
|
+
if (["active_loops", "total_iterations", "loop_status"].includes(segId)) return "ralph";
|
|
83
|
+
if (["current_command", "sandbox_level", "command_duration"].includes(segId)) return "workflow";
|
|
84
|
+
if (["docs_count", "tasks_done", "tasks_total", "task_pct"].includes(segId)) return "kanboard";
|
|
85
|
+
if (["platforms_enabled", "last_sent"].includes(segId)) return "notify";
|
|
86
|
+
if (segId === "extension_statuses") return "status_ext";
|
|
87
|
+
return "core";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Show the footer help overlay.
|
|
92
|
+
* Lists all enabled segments grouped by zone with descriptions.
|
|
93
|
+
*/
|
|
94
|
+
export function showFooterHelp(
|
|
95
|
+
pi: ExtensionAPI,
|
|
96
|
+
segments: FooterSegment[],
|
|
97
|
+
presetName: string,
|
|
98
|
+
): void {
|
|
99
|
+
const lines = buildHelpLines(segments, presetName);
|
|
100
|
+
|
|
101
|
+
// Use pi's custom UI overlay
|
|
102
|
+
const ctx = (pi as any)._ctx;
|
|
103
|
+
if (ctx?.ui?.custom) {
|
|
104
|
+
ctx.ui.custom((tui: import("@mariozechner/pi-tui").TUI) => {
|
|
105
|
+
let scrollOffset = 0;
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
dispose() {},
|
|
109
|
+
render(width: number, height: number): string[] {
|
|
110
|
+
const maxVisible = height - 2; // border lines
|
|
111
|
+
const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);
|
|
112
|
+
|
|
113
|
+
const result: string[] = [];
|
|
114
|
+
|
|
115
|
+
// Top border
|
|
116
|
+
const title = " ? Footer Segment Guide ";
|
|
117
|
+
const borderLen = Math.max(width - 2, title.length + 4);
|
|
118
|
+
result.push(`\x1b[2m┌${"─".repeat(borderLen)}┐\x1b[0m`);
|
|
119
|
+
|
|
120
|
+
// Title
|
|
121
|
+
result.push(`\x1b[2m│\x1b[0m \x1b[1m${title}\x1b[0m${" ".repeat(Math.max(0, borderLen - title.length - 1))}\x1b[2m│\x1b[0m`);
|
|
122
|
+
|
|
123
|
+
// Content
|
|
124
|
+
for (const line of visibleLines) {
|
|
125
|
+
const padded = line.length > borderLen - 2
|
|
126
|
+
? line.slice(0, borderLen - 2)
|
|
127
|
+
: line + " ".repeat(Math.max(0, borderLen - 2 - line.length));
|
|
128
|
+
result.push(`\x1b[2m│\x1b[0m ${padded} \x1b[2m│\x1b[0m`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Bottom border
|
|
132
|
+
result.push(`\x1b[2m├${"─".repeat(borderLen)}┤\x1b[0m`);
|
|
133
|
+
result.push(`\x1b[2m│\x1b[0m \x1b[2m↑↓ scroll · q close\x1b[0m${" ".repeat(Math.max(0, borderLen - 20))} \x1b[2m│\x1b[0m`);
|
|
134
|
+
result.push(`\x1b[2m└${"─".repeat(borderLen)}┘\x1b[0m`);
|
|
135
|
+
|
|
136
|
+
return result;
|
|
137
|
+
},
|
|
138
|
+
handleInput(key: string): boolean {
|
|
139
|
+
if (key === "q" || key === "Escape" || key === "Enter") {
|
|
140
|
+
return false; // Close overlay
|
|
141
|
+
}
|
|
142
|
+
if (key === "ArrowUp" || key === "k") {
|
|
143
|
+
scrollOffset = Math.max(0, scrollOffset - 1);
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
if (key === "ArrowDown" || key === "j") {
|
|
147
|
+
scrollOffset = Math.min(Math.max(0, lines.length - 5), scrollOffset + 1);
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
return true; // Consume all other keys
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
// Fallback: print to console
|
|
156
|
+
for (const line of lines) {
|
|
157
|
+
console.log(line);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* initializes renderer on session_start.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { ExtensionAPI, Theme } from "@mariozechner/pi-coding-agent";
|
|
8
|
+
import type { ExtensionAPI, Theme, ExtensionContext } from "@mariozechner/pi-coding-agent";
|
|
9
|
+
import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
9
10
|
import { UNIPI_EVENTS, emitEvent, UNIPI_PREFIX, FOOTER_COMMANDS } from "@pi-unipi/core";
|
|
10
11
|
import { FooterRegistry, getFooterRegistry } from "./registry/index.js";
|
|
11
12
|
import { FooterRenderer } from "./rendering/renderer.js";
|
|
@@ -27,6 +28,7 @@ import { STATUS_EXT_SEGMENTS } from "./segments/status-ext.js";
|
|
|
27
28
|
|
|
28
29
|
import type { FooterGroup, FooterSegment } from "./types.js";
|
|
29
30
|
import { rainbowBorder } from "./segments/core.js";
|
|
31
|
+
import { tpsTracker } from "./tps-tracker.js";
|
|
30
32
|
|
|
31
33
|
/** All segment groups */
|
|
32
34
|
const ALL_GROUPS: FooterGroup[] = [
|
|
@@ -61,10 +63,10 @@ export interface FooterState {
|
|
|
61
63
|
unsubscribeEvents: (() => void) | null;
|
|
62
64
|
piContext: unknown;
|
|
63
65
|
footerData: unknown;
|
|
64
|
-
tuiRef:
|
|
66
|
+
tuiRef: import("@mariozechner/pi-tui").TUI | null | undefined;
|
|
65
67
|
refreshTimer: ReturnType<typeof setInterval> | null;
|
|
66
68
|
/** Re-register footer + widgets with pi UI (for live enable) */
|
|
67
|
-
setupUI: ((pi: ExtensionAPI, ctx:
|
|
69
|
+
setupUI: ((pi: ExtensionAPI, ctx: ExtensionContext) => void) | null;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
export default function footerExtension(pi: ExtensionAPI): void {
|
|
@@ -77,7 +79,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
77
79
|
registry: getFooterRegistry(),
|
|
78
80
|
renderer: new FooterRenderer(
|
|
79
81
|
getFooterRegistry(),
|
|
80
|
-
{ get: (id: string) => segmentLookup.get(id) },
|
|
82
|
+
{ get: (id: string) => segmentLookup.get(id), allIds: () => Array.from(segmentLookup.keys()) },
|
|
81
83
|
loadFooterSettings().preset,
|
|
82
84
|
),
|
|
83
85
|
segmentLookup,
|
|
@@ -110,7 +112,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
110
112
|
|
|
111
113
|
// Setup footer + widgets
|
|
112
114
|
setupFooterUI(pi, ctx, state);
|
|
113
|
-
state.setupUI = (p: ExtensionAPI, c:
|
|
115
|
+
state.setupUI = (p: ExtensionAPI, c: ExtensionContext) => setupFooterUI(p, c, state);
|
|
114
116
|
});
|
|
115
117
|
|
|
116
118
|
pi.on("session_shutdown", async () => {
|
|
@@ -124,6 +126,7 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
124
126
|
state.refreshTimer = null;
|
|
125
127
|
}
|
|
126
128
|
state.tuiRef = null;
|
|
129
|
+
tpsTracker.reset();
|
|
127
130
|
});
|
|
128
131
|
|
|
129
132
|
// ─── Register commands ──────────────────────────────────────────────────
|
|
@@ -133,10 +136,10 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
133
136
|
// ─── Emit MODULE_READY ──────────────────────────────────────────────────
|
|
134
137
|
|
|
135
138
|
pi.on("session_start", async () => {
|
|
136
|
-
emitEvent(pi
|
|
139
|
+
emitEvent(pi, UNIPI_EVENTS.MODULE_READY, {
|
|
137
140
|
name: "@pi-unipi/footer",
|
|
138
141
|
version: "0.1.0",
|
|
139
|
-
commands: [`${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER}`, `${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER_SETTINGS}`],
|
|
142
|
+
commands: [`${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER}`, `${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER_SETTINGS}`, `${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER_HELP}`],
|
|
140
143
|
tools: [],
|
|
141
144
|
});
|
|
142
145
|
});
|
|
@@ -144,14 +147,36 @@ export default function footerExtension(pi: ExtensionAPI): void {
|
|
|
144
147
|
|
|
145
148
|
// ─── Footer UI setup ────────────────────────────────────────────────────────
|
|
146
149
|
|
|
147
|
-
function setupFooterUI(pi: ExtensionAPI, ctx:
|
|
150
|
+
function setupFooterUI(pi: ExtensionAPI, ctx: ExtensionContext, state: FooterState): void {
|
|
148
151
|
// Register footer (minimal — handles branch changes)
|
|
149
|
-
ctx.ui.setFooter((tui
|
|
152
|
+
ctx.ui.setFooter((tui, _theme, footerData) => {
|
|
150
153
|
state.tuiRef = tui;
|
|
151
154
|
|
|
152
155
|
// Start periodic refresh for time-sensitive segments (e.g. clock)
|
|
153
156
|
if (!state.refreshTimer) {
|
|
154
157
|
state.refreshTimer = setInterval(() => {
|
|
158
|
+
// Feed TPS tracker with per-message data
|
|
159
|
+
try {
|
|
160
|
+
const piCtx = state.piContext as Record<string, unknown> | undefined;
|
|
161
|
+
if (piCtx?.sessionManager) {
|
|
162
|
+
const sm = (piCtx as any).sessionManager;
|
|
163
|
+
const events = sm?.getBranch?.() ?? [];
|
|
164
|
+
let msgIndex = 0;
|
|
165
|
+
for (const e of events) {
|
|
166
|
+
if (!e || typeof e !== "object") continue;
|
|
167
|
+
if (e.type !== "message") continue;
|
|
168
|
+
const m = e.message;
|
|
169
|
+
if (!m || m.role !== "assistant") continue;
|
|
170
|
+
if (m.stopReason === "error" || m.stopReason === "aborted") continue;
|
|
171
|
+
const output = m.usage?.output ?? 0;
|
|
172
|
+
const hasStop = !!m.stopReason;
|
|
173
|
+
tpsTracker.onMessageUpdate(msgIndex, output, hasStop);
|
|
174
|
+
msgIndex++;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// Silently ignore — TPS is best-effort
|
|
179
|
+
}
|
|
155
180
|
state.renderer.resetLayoutCache();
|
|
156
181
|
state.tuiRef?.requestRender();
|
|
157
182
|
}, 1_000);
|
|
@@ -175,7 +200,7 @@ function setupFooterUI(pi: ExtensionAPI, ctx: any, state: FooterState): void {
|
|
|
175
200
|
});
|
|
176
201
|
|
|
177
202
|
// Top row widget
|
|
178
|
-
ctx.ui.setWidget("footer-top", (_tui
|
|
203
|
+
ctx.ui.setWidget("footer-top", (_tui, theme) => {
|
|
179
204
|
// Update the renderer's theme-like
|
|
180
205
|
const themeLike = { fg: (color: string, text: string) => theme.fg(color as any, text) };
|
|
181
206
|
// We need to patch the context with proper theme
|
|
@@ -187,30 +212,38 @@ function setupFooterUI(pi: ExtensionAPI, ctx: any, state: FooterState): void {
|
|
|
187
212
|
state.renderer.resetLayoutCache();
|
|
188
213
|
},
|
|
189
214
|
render(width: number): string[] {
|
|
190
|
-
if (!state.enabled || !state.piContext) return [];
|
|
215
|
+
if (!state.enabled || !state.piContext || width <= 0) return [];
|
|
191
216
|
|
|
192
217
|
// Build layout with proper theme by creating segment contexts
|
|
193
218
|
const layout = state.renderer.computeLayout(width);
|
|
194
|
-
|
|
219
|
+
if (!layout.topContent) return [];
|
|
220
|
+
|
|
221
|
+
// Hard safety net: never return a line wider than the terminal.
|
|
222
|
+
// This catches any edge cases in layout math or visibleWidth()
|
|
223
|
+
// inconsistencies with PUA characters + ANSI codes.
|
|
224
|
+
const line = layout.topContent;
|
|
225
|
+
return [visibleWidth(line) > width ? truncateToWidth(line, width) : line];
|
|
195
226
|
},
|
|
196
227
|
};
|
|
197
228
|
}, { placement: "aboveEditor" });
|
|
198
229
|
|
|
199
230
|
// Secondary row widget
|
|
200
|
-
ctx.ui.setWidget("footer-secondary", (_tui
|
|
231
|
+
ctx.ui.setWidget("footer-secondary", (_tui, _theme) => {
|
|
201
232
|
return {
|
|
202
233
|
dispose() {},
|
|
203
234
|
invalidate() {
|
|
204
235
|
state.renderer.resetLayoutCache();
|
|
205
236
|
},
|
|
206
237
|
render(width: number): string[] {
|
|
207
|
-
if (!state.enabled || !state.piContext) return [];
|
|
238
|
+
if (!state.enabled || !state.piContext || width <= 0) return [];
|
|
208
239
|
|
|
209
240
|
const lines: string[] = [];
|
|
210
241
|
|
|
211
242
|
const layout = state.renderer.computeLayout(width);
|
|
212
243
|
if (layout.secondaryContent) {
|
|
213
|
-
|
|
244
|
+
// Hard safety net: never return a line wider than the terminal.
|
|
245
|
+
const line = layout.secondaryContent;
|
|
246
|
+
lines.push(visibleWidth(line) > width ? truncateToWidth(line, width) : line);
|
|
214
247
|
}
|
|
215
248
|
|
|
216
249
|
return lines;
|
package/src/presets.ts
CHANGED
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
* @pi-unipi/footer — Presets system
|
|
3
3
|
*
|
|
4
4
|
* Preset definitions: default, minimal, compact, full, nerd, ascii.
|
|
5
|
-
* Each preset defines which segments appear
|
|
5
|
+
* Each preset defines which segments appear (left/center/right/secondary),
|
|
6
6
|
* plus separator style and color scheme.
|
|
7
|
+
*
|
|
8
|
+
* Segments are grouped by their zone field regardless of which array they're
|
|
9
|
+
* listed in. The arrays define ordering within the preset.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
import type { PresetDef, SeparatorStyle, ColorScheme } from "./types.js";
|
|
@@ -12,100 +15,106 @@ import { getDefaultColors } from "./rendering/theme.js";
|
|
|
12
15
|
/** Default preset — balanced view */
|
|
13
16
|
const DEFAULT_PRESET: PresetDef = {
|
|
14
17
|
leftSegments: [
|
|
15
|
-
"model", "api_state", "tool_count", "git",
|
|
18
|
+
"model", "api_state", "tool_count", "git",
|
|
16
19
|
],
|
|
17
20
|
rightSegments: [
|
|
18
|
-
"
|
|
21
|
+
"tps", "context_pct", "cost",
|
|
22
|
+
"compactions", "tokens_saved", "project_count",
|
|
23
|
+
"current_command", "loop_status", "extension_statuses",
|
|
24
|
+
"clock", "duration",
|
|
19
25
|
],
|
|
20
26
|
secondarySegments: [
|
|
21
|
-
"
|
|
27
|
+
"session",
|
|
22
28
|
],
|
|
23
|
-
separator: "powerline-thin",
|
|
24
29
|
colors: getDefaultColors(),
|
|
25
30
|
};
|
|
26
31
|
|
|
27
32
|
/** Minimal preset — just the essentials */
|
|
28
33
|
const MINIMAL_PRESET: PresetDef = {
|
|
29
34
|
leftSegments: [
|
|
30
|
-
"
|
|
35
|
+
"model", "git",
|
|
36
|
+
],
|
|
37
|
+
rightSegments: [
|
|
38
|
+
"context_pct",
|
|
39
|
+
"clock",
|
|
31
40
|
],
|
|
32
|
-
rightSegments: [],
|
|
33
41
|
secondarySegments: [],
|
|
34
|
-
separator: "pipe",
|
|
35
42
|
colors: getDefaultColors(),
|
|
36
43
|
};
|
|
37
44
|
|
|
38
45
|
/** Compact preset — core + key stats */
|
|
39
46
|
const COMPACT_PRESET: PresetDef = {
|
|
40
47
|
leftSegments: [
|
|
41
|
-
"model", "git",
|
|
48
|
+
"model", "git",
|
|
42
49
|
],
|
|
43
50
|
rightSegments: [
|
|
44
|
-
"
|
|
51
|
+
"tps", "context_pct", "cost",
|
|
52
|
+
"clock", "duration",
|
|
45
53
|
],
|
|
46
54
|
secondarySegments: [],
|
|
47
|
-
separator: "dot",
|
|
48
55
|
colors: getDefaultColors(),
|
|
49
56
|
};
|
|
50
57
|
|
|
51
58
|
/** Full preset — everything */
|
|
52
59
|
const FULL_PRESET: PresetDef = {
|
|
53
60
|
leftSegments: [
|
|
54
|
-
"model", "api_state", "tool_count", "git", "
|
|
55
|
-
"tokens_total", "tokens_in", "tokens_out",
|
|
61
|
+
"model", "api_state", "tool_count", "git", "current_command", "session",
|
|
56
62
|
],
|
|
57
63
|
rightSegments: [
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"project_count", "total_count",
|
|
61
|
-
"servers_total", "servers_active", "tools_total",
|
|
62
|
-
"active_loops", "
|
|
63
|
-
"
|
|
64
|
-
"docs_count", "tasks_done", "tasks_total", "task_pct",
|
|
64
|
+
"tps", "context_pct", "cost", "tokens_total",
|
|
65
|
+
"session_events", "compactions", "tokens_saved",
|
|
66
|
+
"project_count", "total_count",
|
|
67
|
+
"servers_total", "servers_active", "tools_total",
|
|
68
|
+
"active_loops", "loop_status",
|
|
69
|
+
"docs_count", "tasks_done", "task_pct",
|
|
65
70
|
"extension_statuses",
|
|
71
|
+
"clock", "duration",
|
|
66
72
|
],
|
|
67
73
|
secondarySegments: [
|
|
68
|
-
"hostname",
|
|
74
|
+
"hostname",
|
|
75
|
+
"tokens_in", "tokens_out",
|
|
76
|
+
"compression_ratio", "cocoindex_status",
|
|
69
77
|
"platforms_enabled", "last_sent",
|
|
78
|
+
"thinking_level",
|
|
70
79
|
],
|
|
71
|
-
separator: "powerline-thin",
|
|
72
80
|
colors: getDefaultColors(),
|
|
73
81
|
};
|
|
74
82
|
|
|
75
83
|
/** Nerd preset — maximum detail for Nerd Font users */
|
|
76
84
|
const NERD_PRESET: PresetDef = {
|
|
77
85
|
leftSegments: [
|
|
78
|
-
"model", "api_state", "tool_count", "git", "
|
|
79
|
-
"tokens_total",
|
|
86
|
+
"model", "api_state", "tool_count", "git", "current_command", "session",
|
|
80
87
|
],
|
|
81
88
|
rightSegments: [
|
|
89
|
+
"tps", "context_pct", "cost", "tokens_total",
|
|
82
90
|
"session_events", "compactions", "tokens_saved",
|
|
83
91
|
"project_count", "total_count",
|
|
84
92
|
"servers_total", "servers_active", "tools_total",
|
|
85
93
|
"active_loops", "loop_status",
|
|
86
|
-
"
|
|
87
|
-
"docs_count", "tasks_done", "tasks_total", "task_pct",
|
|
94
|
+
"docs_count", "tasks_done", "task_pct",
|
|
88
95
|
"extension_statuses",
|
|
96
|
+
"clock", "duration",
|
|
89
97
|
],
|
|
90
98
|
secondarySegments: [
|
|
91
|
-
"
|
|
92
|
-
"
|
|
99
|
+
"hostname",
|
|
100
|
+
"tokens_in", "tokens_out",
|
|
101
|
+
"compression_ratio", "cocoindex_status",
|
|
93
102
|
"platforms_enabled", "last_sent",
|
|
103
|
+
"thinking_level",
|
|
94
104
|
],
|
|
95
|
-
separator: "powerline",
|
|
96
105
|
colors: getDefaultColors(),
|
|
97
106
|
};
|
|
98
107
|
|
|
99
108
|
/** ASCII preset — safe for any terminal */
|
|
100
109
|
const ASCII_PRESET: PresetDef = {
|
|
101
110
|
leftSegments: [
|
|
102
|
-
"model", "git",
|
|
111
|
+
"model", "git",
|
|
103
112
|
],
|
|
104
113
|
rightSegments: [
|
|
105
|
-
"
|
|
114
|
+
"tps", "context_pct", "cost",
|
|
115
|
+
"clock", "duration",
|
|
106
116
|
],
|
|
107
117
|
secondarySegments: [],
|
|
108
|
-
separator: "ascii",
|
|
109
118
|
colors: getDefaultColors(),
|
|
110
119
|
};
|
|
111
120
|
|
package/src/registry/index.ts
CHANGED
|
@@ -156,5 +156,5 @@ export function resetFooterRegistry(): void {
|
|
|
156
156
|
|
|
157
157
|
// Expose on globalThis for cross-package access
|
|
158
158
|
if (typeof globalThis !== "undefined") {
|
|
159
|
-
|
|
159
|
+
globalThis.__unipi_footer_registry = getFooterRegistry();
|
|
160
160
|
}
|