@pi-unipi/footer 0.1.2 → 0.1.4
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 -120
- package/src/config.ts +10 -6
- package/src/events.ts +34 -34
- package/src/help.ts +160 -0
- package/src/index.ts +46 -10
- package/src/presets.ts +40 -31
- package/src/registry/index.ts +5 -7
- package/src/rendering/icons.ts +125 -107
- package/src/rendering/renderer.ts +198 -79
- package/src/rendering/theme.ts +56 -29
- package/src/segments/compactor.ts +21 -10
- package/src/segments/core.ts +134 -67
- package/src/segments/kanboard.ts +24 -8
- package/src/segments/mcp.ts +25 -8
- package/src/segments/memory.ts +17 -11
- package/src/segments/notify.ts +16 -5
- package/src/segments/ralph.ts +33 -17
- package/src/segments/status-ext.ts +18 -13
- package/src/segments/workflow.ts +44 -21
- package/src/tps-tracker.ts +204 -0
- package/src/tui/settings-tui.ts +389 -157
- package/src/types.ts +51 -12
package/README.md
CHANGED
|
@@ -1,78 +1,65 @@
|
|
|
1
1
|
# @pi-unipi/footer
|
|
2
2
|
|
|
3
|
-
Persistent status bar
|
|
3
|
+
Persistent status bar at the bottom of the terminal. Shows live stats from all Unipi packages — compactor tokens saved, memory count, MCP status, Ralph loops, workflow state, kanboard tasks, notifications.
|
|
4
4
|
|
|
5
|
-
Subscribes to
|
|
5
|
+
Subscribes to events from every package and renders segments using Pi's `setFooter` + `setWidget` APIs. Responsive layout adjusts to terminal width, with a secondary row for narrow terminals.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Commands
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
9
|
+
| Command | Description |
|
|
10
|
+
|---------|-------------|
|
|
11
|
+
| `/unipi:footer` | Toggle footer on/off |
|
|
12
|
+
| `/unipi:footer <preset>` | Switch preset (default, minimal, compact, full, nerd, ascii) |
|
|
13
|
+
| `/unipi:footer sep:<style>` | Change separator style |
|
|
14
|
+
| `/unipi:footer icon:<style>` | Change icon style (nerd, emoji, text) |
|
|
15
|
+
| `/unipi:footer on` / `/unipi:footer off` | Enable/disable explicitly |
|
|
16
|
+
| `/unipi:footer-settings` | Open settings TUI for per-group/per-segment toggles |
|
|
17
17
|
|
|
18
|
-
##
|
|
18
|
+
## Special Triggers
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
┌─────────────────────────────────────────────────────┐
|
|
22
|
-
│ FooterRenderer (setFooter + setWidget) │ ← Renders to screen
|
|
23
|
-
│ - Responsive layout (top + secondary rows) │
|
|
24
|
-
│ - Preset system, separators, theming │
|
|
25
|
-
├─────────────────────────────────────────────────────┤
|
|
26
|
-
│ FooterRegistry (segment groups) │ ← Manages segments
|
|
27
|
-
│ - Subscribes to UNIPI_EVENTS │
|
|
28
|
-
│ - Per-segment enable/disable │
|
|
29
|
-
│ - Reactive data caching │
|
|
30
|
-
├─────────────────────────────────────────────────────┤
|
|
31
|
-
│ Event Sources (existing packages) │ ← Data providers
|
|
32
|
-
│ - compactor, memory, workflow, ralph, mcp, │
|
|
33
|
-
│ kanboard, notify, core │
|
|
34
|
-
└─────────────────────────────────────────────────────┘
|
|
35
|
-
```
|
|
20
|
+
Footer subscribes to events from every Unipi package:
|
|
36
21
|
|
|
37
|
-
|
|
22
|
+
| Group | Events | Segments |
|
|
23
|
+
|-------|--------|----------|
|
|
24
|
+
| core | Pi SDK | model, thinking, path, git, context_pct, cost, tokens, session |
|
|
25
|
+
| compactor | `COMPACTOR_STATS_UPDATED` | session_events, compactions, tokens_saved, compression_ratio |
|
|
26
|
+
| memory | `MEMORY_STORED`/`DELETED`/`CONSOLIDATED` | project_count, total_count, consolidations |
|
|
27
|
+
| mcp | `MCP_SERVER_STARTED`/`STOPPED`/`ERROR` | servers_total, servers_active, tools_total |
|
|
28
|
+
| ralph | `RALPH_LOOP_START`/`END`/`ITERATION_DONE` | active_loops, total_iterations, loop_status |
|
|
29
|
+
| workflow | `WORKFLOW_START`/`END` | current_command, sandbox_level, command_duration |
|
|
30
|
+
| kanboard | Direct registry read | docs_count, tasks_done, tasks_total, task_pct |
|
|
31
|
+
| notify | `NOTIFICATION_SENT` | platforms_enabled, last_sent |
|
|
38
32
|
|
|
39
|
-
|
|
33
|
+
Footer works even if packages load after it — late-arriving events update the cache.
|
|
40
34
|
|
|
41
|
-
|
|
42
|
-
- `/unipi:footer <preset>` — switch preset (default, minimal, compact, full, nerd, ascii)
|
|
43
|
-
- `/unipi:footer sep:<style>` — change separator style (powerline, powerline-thin, slash, pipe, dot, ascii)
|
|
44
|
-
- `/unipi:footer icon:<style>` — change icon style (nerd, emoji, text)
|
|
45
|
-
- `/unipi:footer on` / `/unipi:footer off` — enable/disable explicitly
|
|
46
|
-
- `/unipi:footer-settings` — open settings TUI for per-group/per-segment toggles
|
|
47
|
-
|
|
48
|
-
## Segment Groups
|
|
35
|
+
## Presets
|
|
49
36
|
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
| **kanboard** | `docs_count`, `tasks_done`, `tasks_total`, `task_pct` | ON | Kanboard registry (direct read) |
|
|
59
|
-
| **notify** | `platforms_enabled`, `last_sent` | OFF | `NOTIFICATION_SENT` event |
|
|
60
|
-
| **status_ext** | `extension_statuses` | ON | `footerData.getExtensionStatuses()` |
|
|
37
|
+
| Preset | Description |
|
|
38
|
+
|--------|-------------|
|
|
39
|
+
| `default` | Balanced: model, thinking, path, git, context, cost + compactor + memory + ralph |
|
|
40
|
+
| `minimal` | Essentials only: path, git, context |
|
|
41
|
+
| `compact` | Core + key stats: model, git, cost, context + compactor + memory |
|
|
42
|
+
| `full` | Everything from all groups |
|
|
43
|
+
| `nerd` | Full + hostname + time + session + extensions |
|
|
44
|
+
| `ascii` | Core segments with ASCII icons |
|
|
61
45
|
|
|
62
|
-
##
|
|
46
|
+
## Segment Groups
|
|
63
47
|
|
|
64
|
-
|
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
48
|
+
| Group | Default | Data Source |
|
|
49
|
+
|-------|---------|-------------|
|
|
50
|
+
| **core** | ON | Pi SDK (ctx.sessionManager, footerData) |
|
|
51
|
+
| **compactor** | ON | `COMPACTOR_STATS_UPDATED` event |
|
|
52
|
+
| **memory** | ON | `MEMORY_STORED`/`DELETED`/`CONSOLIDATED` events |
|
|
53
|
+
| **mcp** | ON | `MCP_SERVER_STARTED`/`STOPPED`/`ERROR` events |
|
|
54
|
+
| **ralph** | ON | `RALPH_LOOP_START`/`END`/`ITERATION_DONE` events |
|
|
55
|
+
| **workflow** | ON | `WORKFLOW_START`/`END` events |
|
|
56
|
+
| **kanboard** | ON | Kanboard registry (direct read) |
|
|
57
|
+
| **notify** | OFF | `NOTIFICATION_SENT` event |
|
|
58
|
+
| **status_ext** | ON | `footerData.getExtensionStatuses()` |
|
|
72
59
|
|
|
73
|
-
##
|
|
60
|
+
## Configurables
|
|
74
61
|
|
|
75
|
-
Settings
|
|
62
|
+
Settings in `~/.pi/agent/settings.json` under `unipi.footer`:
|
|
76
63
|
|
|
77
64
|
```json
|
|
78
65
|
{
|
|
@@ -88,19 +75,7 @@ Settings are stored in `~/.pi/agent/settings.json` under `unipi.footer`:
|
|
|
88
75
|
"segments": {
|
|
89
76
|
"session_events": true,
|
|
90
77
|
"compactions": true,
|
|
91
|
-
"tokens_saved": true
|
|
92
|
-
"compression_ratio": false,
|
|
93
|
-
"indexed_docs": false,
|
|
94
|
-
"sandbox_runs": false,
|
|
95
|
-
"search_queries": false
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
"memory": {
|
|
99
|
-
"show": true,
|
|
100
|
-
"segments": {
|
|
101
|
-
"project_count": true,
|
|
102
|
-
"total_count": true,
|
|
103
|
-
"consolidations": false
|
|
78
|
+
"tokens_saved": true
|
|
104
79
|
}
|
|
105
80
|
}
|
|
106
81
|
}
|
|
@@ -109,98 +84,38 @@ Settings are stored in `~/.pi/agent/settings.json` under `unipi.footer`:
|
|
|
109
84
|
}
|
|
110
85
|
```
|
|
111
86
|
|
|
112
|
-
|
|
87
|
+
### Separator Styles
|
|
113
88
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
89
|
+
| Style | Look |
|
|
90
|
+
|-------|------|
|
|
91
|
+
| `powerline` | Thick powerline arrows |
|
|
92
|
+
| `powerline-thin` | Thin powerline arrows (default) |
|
|
93
|
+
| `slash` | / |
|
|
94
|
+
| `pipe` | \| |
|
|
95
|
+
| `dot` | Middle dot |
|
|
96
|
+
| `ascii` | > < |
|
|
118
97
|
|
|
119
|
-
|
|
120
|
-
┌─ model │ thinking │ path │ git │ context │ cost ───────────────────────────────────────────────┐
|
|
121
|
-
└─ compactions │ tokens_saved │ project_count │ ralph │ workflow ────────────────────────────────┘
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
## Separator Styles
|
|
125
|
-
|
|
126
|
-
| Style | Look | Description |
|
|
127
|
-
|-------|------|-------------|
|
|
128
|
-
| `powerline` | ◀ ▶ | Thick powerline arrows |
|
|
129
|
-
| `powerline-thin` | | Thin powerline arrows (default) |
|
|
130
|
-
| `slash` | / | Slash separator |
|
|
131
|
-
| `pipe` | \| | Pipe separator |
|
|
132
|
-
| `dot` | · | Middle dot separator |
|
|
133
|
-
| `ascii` | > < | ASCII angle brackets |
|
|
134
|
-
|
|
135
|
-
## Icon Styles
|
|
136
|
-
|
|
137
|
-
Three icon styles are available, controlled by `/unipi:footer icon:<style>` or the `iconStyle` setting:
|
|
138
|
-
|
|
139
|
-
| Style | Description | Example |
|
|
140
|
-
-------|-------------|--------|
|
|
141
|
-
| `nerd` | Nerd Font glyphs (default, requires Nerd Font terminal) | , , |
|
|
142
|
-
| `emoji` | Unicode emoji/symbols (works on most terminals) | ⚡, ◧, $] |
|
|
143
|
-
| `text` | Plain text abbreviations (works everywhere, most compact) | evt, cmp, $] |
|
|
98
|
+
### Icon Styles
|
|
144
99
|
|
|
145
|
-
|
|
146
|
-
|
|
100
|
+
| Style | Description |
|
|
101
|
+
|-------|-------------|
|
|
102
|
+
| `nerd` | Nerd Font glyphs (auto-detected) |
|
|
103
|
+
| `emoji` | Unicode symbols (works on most terminals) |
|
|
104
|
+
| `text` | Plain text abbreviations (works everywhere) |
|
|
147
105
|
|
|
148
|
-
|
|
106
|
+
When `iconStyle` is not set, footer auto-detects Nerd Font support and defaults to `nerd` if available, `emoji` otherwise.
|
|
149
107
|
|
|
150
|
-
|
|
151
|
-
- **Data provider failures:** Segments hide when data unavailable (graceful degradation)
|
|
152
|
-
- **Config parse failures:** Fall back to default preset with warning
|
|
153
|
-
- **Module loading order:** Footer works even if packages load after it — late-arriving events update cache
|
|
108
|
+
### Responsive Layout
|
|
154
109
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
# Run tests
|
|
159
|
-
pnpm test
|
|
110
|
+
```
|
|
111
|
+
Wide terminal (>120 cols):
|
|
112
|
+
model | thinking | path | git | context | cost | compactions | tokens_saved | project_count
|
|
160
113
|
|
|
161
|
-
|
|
162
|
-
|
|
114
|
+
Narrow terminal (<120 cols):
|
|
115
|
+
Row 1: model | thinking | path | git | context | cost
|
|
116
|
+
Row 2: compactions | tokens_saved | project_count | ralph | workflow
|
|
163
117
|
```
|
|
164
118
|
|
|
165
|
-
##
|
|
119
|
+
## License
|
|
166
120
|
|
|
167
|
-
|
|
168
|
-
packages/footer/
|
|
169
|
-
├── index.ts # Re-exports
|
|
170
|
-
├── types.ts # Re-exports from src/types.ts
|
|
171
|
-
├── package.json # Package manifest
|
|
172
|
-
├── tsconfig.json # TypeScript config
|
|
173
|
-
├── README.md # This file
|
|
174
|
-
├── src/
|
|
175
|
-
│ ├── index.ts # Extension entry point
|
|
176
|
-
│ ├── types.ts # Type definitions
|
|
177
|
-
│ ├── config.ts # Settings load/save
|
|
178
|
-
│ ├── events.ts # Event subscription wiring
|
|
179
|
-
│ ├── commands.ts # Command registration
|
|
180
|
-
│ ├── presets.ts # Preset definitions
|
|
181
|
-
│ ├── registry/ # FooterRegistry
|
|
182
|
-
│ │ └── index.ts
|
|
183
|
-
│ ├── rendering/ # Rendering engine
|
|
184
|
-
│ │ ├── renderer.ts # FooterRenderer class
|
|
185
|
-
│ │ ├── separators.ts # Separator system
|
|
186
|
-
│ │ ├── theme.ts # Theme color resolution
|
|
187
|
-
│ │ └── icons.ts # Icon system with Nerd Font detection
|
|
188
|
-
│ ├── segments/ # Segment implementations
|
|
189
|
-
│ │ ├── core.ts # Core segments (model, path, git, etc.)
|
|
190
|
-
│ │ ├── compactor.ts # Compactor segments
|
|
191
|
-
│ │ ├── memory.ts # Memory segments
|
|
192
|
-
│ │ ├── mcp.ts # MCP segments
|
|
193
|
-
│ │ ├── ralph.ts # Ralph segments
|
|
194
|
-
│ │ ├── workflow.ts # Workflow segments
|
|
195
|
-
│ │ ├── kanboard.ts # Kanboard segments
|
|
196
|
-
│ │ ├── notify.ts # Notify segments
|
|
197
|
-
│ │ └── status-ext.ts # Extension statuses segment
|
|
198
|
-
│ └── tui/
|
|
199
|
-
│ └── settings-tui.ts # Settings overlay TUI
|
|
200
|
-
└── tests/ # Unit tests
|
|
201
|
-
├── separators.test.ts
|
|
202
|
-
├── registry.test.ts
|
|
203
|
-
├── config.test.ts
|
|
204
|
-
├── segments.test.ts
|
|
205
|
-
└── events.test.ts
|
|
206
|
-
```
|
|
121
|
+
MIT
|
package/package.json
CHANGED
package/src/commands.ts
CHANGED
|
@@ -8,34 +8,9 @@
|
|
|
8
8
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
9
9
|
import { UNIPI_PREFIX, FOOTER_COMMANDS } from "@pi-unipi/core";
|
|
10
10
|
import { loadFooterSettings, saveFooterSettings } from "./config.js";
|
|
11
|
-
import { PRESET_NAMES } from "./presets.js";
|
|
12
11
|
import { showFooterSettings } from "./tui/settings-tui.js";
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
/** Minimal autocomplete item (compatible with pi-tui AutocompleteItem) */
|
|
17
|
-
interface ArgSuggestion {
|
|
18
|
-
value: string;
|
|
19
|
-
label: string;
|
|
20
|
-
description?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/** All valid separator styles */
|
|
24
|
-
const SEPARATOR_STYLES: SeparatorStyle[] = [
|
|
25
|
-
"powerline",
|
|
26
|
-
"powerline-thin",
|
|
27
|
-
"slash",
|
|
28
|
-
"pipe",
|
|
29
|
-
"dot",
|
|
30
|
-
"ascii",
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
/** All valid icon styles */
|
|
34
|
-
const ICON_STYLES: IconStyle[] = [
|
|
35
|
-
"nerd",
|
|
36
|
-
"emoji",
|
|
37
|
-
"text",
|
|
38
|
-
];
|
|
12
|
+
import { showFooterHelp } from "./help.js";
|
|
13
|
+
import type { FooterGroup, FooterSegment } from "./types.js";
|
|
39
14
|
|
|
40
15
|
/** Extension state interface */
|
|
41
16
|
interface FooterState {
|
|
@@ -46,7 +21,9 @@ interface FooterState {
|
|
|
46
21
|
getPresetName(): string;
|
|
47
22
|
resetLayoutCache(): void;
|
|
48
23
|
};
|
|
24
|
+
segmentLookup: Map<string, FooterSegment>;
|
|
49
25
|
piContext: unknown;
|
|
26
|
+
setupUI: ((pi: ExtensionAPI, ctx: any) => void) | null;
|
|
50
27
|
}
|
|
51
28
|
|
|
52
29
|
/**
|
|
@@ -57,75 +34,23 @@ export function registerCommands(
|
|
|
57
34
|
state: FooterState,
|
|
58
35
|
groups?: FooterGroup[],
|
|
59
36
|
): void {
|
|
60
|
-
// /unipi:footer — toggle
|
|
37
|
+
// /unipi:footer — toggle on/off only
|
|
61
38
|
pi.registerCommand(`${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER}`, {
|
|
62
|
-
description: "Toggle footer
|
|
63
|
-
getArgumentCompletions(argumentPrefix: string): ArgSuggestion[] | null {
|
|
64
|
-
const allOptions: ArgSuggestion[] = [
|
|
65
|
-
...PRESET_NAMES.map(p => ({
|
|
66
|
-
value: p,
|
|
67
|
-
label: p,
|
|
68
|
-
description: `Switch to ${p} preset`,
|
|
69
|
-
})),
|
|
70
|
-
...SEPARATOR_STYLES.map(s => ({
|
|
71
|
-
value: `sep:${s}`,
|
|
72
|
-
label: `sep:${s}`,
|
|
73
|
-
description: `Set separator style: ${s}`,
|
|
74
|
-
})),
|
|
75
|
-
...ICON_STYLES.map(s => ({
|
|
76
|
-
value: `icon:${s}`,
|
|
77
|
-
label: `icon:${s}`,
|
|
78
|
-
description: `Set icon style: ${s}`,
|
|
79
|
-
})),
|
|
80
|
-
{
|
|
81
|
-
value: "on",
|
|
82
|
-
label: "on",
|
|
83
|
-
description: "Enable footer",
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
value: "off",
|
|
87
|
-
label: "off",
|
|
88
|
-
description: "Disable footer",
|
|
89
|
-
},
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
if (!argumentPrefix) return allOptions;
|
|
93
|
-
|
|
94
|
-
const prefix = argumentPrefix.toLowerCase();
|
|
95
|
-
const filtered = allOptions.filter(o =>
|
|
96
|
-
o.value.toLowerCase().startsWith(prefix),
|
|
97
|
-
);
|
|
98
|
-
return filtered.length > 0 ? filtered : null;
|
|
99
|
-
},
|
|
39
|
+
description: "Toggle footer on/off",
|
|
100
40
|
handler: async (args, ctx) => {
|
|
101
|
-
|
|
102
|
-
// Toggle on/off
|
|
103
|
-
state.enabled = !state.enabled;
|
|
104
|
-
state.renderer.setActive(state.enabled);
|
|
105
|
-
|
|
106
|
-
if (state.enabled) {
|
|
107
|
-
ctx.ui.notify("Footer enabled", "info");
|
|
108
|
-
} else {
|
|
109
|
-
ctx.ui.setFooter(undefined);
|
|
110
|
-
ctx.ui.setWidget("footer-top", undefined);
|
|
111
|
-
ctx.ui.setWidget("footer-secondary", undefined);
|
|
112
|
-
ctx.ui.notify("Footer disabled", "info");
|
|
113
|
-
}
|
|
41
|
+
const arg = args?.trim().toLowerCase();
|
|
114
42
|
|
|
115
|
-
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const arg = args.trim().toLowerCase();
|
|
120
|
-
|
|
121
|
-
// on / off
|
|
43
|
+
// on
|
|
122
44
|
if (arg === "on") {
|
|
123
45
|
state.enabled = true;
|
|
124
46
|
state.renderer.setActive(true);
|
|
125
47
|
saveFooterSettings({ enabled: true });
|
|
48
|
+
state.setupUI?.(pi, ctx);
|
|
126
49
|
ctx.ui.notify("Footer enabled", "info");
|
|
127
50
|
return;
|
|
128
51
|
}
|
|
52
|
+
|
|
53
|
+
// off
|
|
129
54
|
if (arg === "off") {
|
|
130
55
|
state.enabled = false;
|
|
131
56
|
state.renderer.setActive(false);
|
|
@@ -137,42 +62,21 @@ export function registerCommands(
|
|
|
137
62
|
return;
|
|
138
63
|
}
|
|
139
64
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (SEPARATOR_STYLES.includes(style)) {
|
|
144
|
-
saveFooterSettings({ separator: style });
|
|
145
|
-
state.renderer.resetLayoutCache();
|
|
146
|
-
ctx.ui.notify(`Separator: ${style}`, "info");
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
ctx.ui.notify(`Unknown separator. Available: ${SEPARATOR_STYLES.join(", ")}`, "warning");
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
65
|
+
// Toggle (no args or unknown args)
|
|
66
|
+
state.enabled = !state.enabled;
|
|
67
|
+
state.renderer.setActive(state.enabled);
|
|
152
68
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
ctx.ui.notify(`Unknown icon style. Available: ${ICON_STYLES.join(", ")}`, "warning");
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Preset name
|
|
168
|
-
if (PRESET_NAMES.includes(arg)) {
|
|
169
|
-
state.renderer.setPreset(arg);
|
|
170
|
-
saveFooterSettings({ preset: arg });
|
|
171
|
-
ctx.ui.notify(`Footer preset: ${arg}`, "info");
|
|
172
|
-
return;
|
|
69
|
+
if (state.enabled) {
|
|
70
|
+
state.setupUI?.(pi, ctx);
|
|
71
|
+
ctx.ui.notify("Footer enabled", "info");
|
|
72
|
+
} else {
|
|
73
|
+
ctx.ui.setFooter(undefined);
|
|
74
|
+
ctx.ui.setWidget("footer-top", undefined);
|
|
75
|
+
ctx.ui.setWidget("footer-secondary", undefined);
|
|
76
|
+
ctx.ui.notify("Footer disabled", "info");
|
|
173
77
|
}
|
|
174
78
|
|
|
175
|
-
|
|
79
|
+
saveFooterSettings({ enabled: state.enabled });
|
|
176
80
|
},
|
|
177
81
|
});
|
|
178
82
|
|
|
@@ -186,7 +90,12 @@ export function registerCommands(
|
|
|
186
90
|
}
|
|
187
91
|
|
|
188
92
|
if (groups && groups.length > 0) {
|
|
189
|
-
showFooterSettings(ctx, groups)
|
|
93
|
+
showFooterSettings(ctx, groups, () => {
|
|
94
|
+
// Re-read settings and update renderer
|
|
95
|
+
const updated = loadFooterSettings();
|
|
96
|
+
state.renderer.setPreset(updated.preset);
|
|
97
|
+
state.renderer.resetLayoutCache();
|
|
98
|
+
});
|
|
190
99
|
} else {
|
|
191
100
|
// Fallback: show text summary
|
|
192
101
|
const settings = loadFooterSettings();
|
|
@@ -201,4 +110,13 @@ export function registerCommands(
|
|
|
201
110
|
}
|
|
202
111
|
},
|
|
203
112
|
});
|
|
113
|
+
|
|
114
|
+
// /unipi:footer-help — show help overlay
|
|
115
|
+
pi.registerCommand(`${UNIPI_PREFIX}${FOOTER_COMMANDS.FOOTER_HELP}`, {
|
|
116
|
+
description: "Show footer segment guide (icons, labels, descriptions)",
|
|
117
|
+
handler: async (_args, _ctx) => {
|
|
118
|
+
const allSegments = Array.from(state.segmentLookup.values());
|
|
119
|
+
showFooterHelp(pi, allSegments, state.renderer.getPresetName());
|
|
120
|
+
},
|
|
121
|
+
});
|
|
204
122
|
}
|
package/src/config.ts
CHANGED
|
@@ -17,6 +17,8 @@ export const DEFAULT_FOOTER_SETTINGS: FooterSettings = {
|
|
|
17
17
|
preset: "default",
|
|
18
18
|
separator: "powerline-thin",
|
|
19
19
|
iconStyle: "nerd",
|
|
20
|
+
zoneSeparator: "\u2502", // │
|
|
21
|
+
showFullLabels: false,
|
|
20
22
|
groups: {
|
|
21
23
|
core: { show: true, segments: {} },
|
|
22
24
|
compactor: { show: true, segments: {} },
|
|
@@ -48,8 +50,8 @@ function readSettingsFile(): Record<string, unknown> | null {
|
|
|
48
50
|
if (!fs.existsSync(settingsPath)) return null;
|
|
49
51
|
const raw = fs.readFileSync(settingsPath, "utf-8");
|
|
50
52
|
return JSON.parse(raw) as Record<string, unknown>;
|
|
51
|
-
} catch
|
|
52
|
-
|
|
53
|
+
} catch {
|
|
54
|
+
// Silently ignore — settings read failure falls back to null.
|
|
53
55
|
return null;
|
|
54
56
|
}
|
|
55
57
|
}
|
|
@@ -66,8 +68,8 @@ function writeSettingsFile(settings: Record<string, unknown>): boolean {
|
|
|
66
68
|
}
|
|
67
69
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
68
70
|
return true;
|
|
69
|
-
} catch
|
|
70
|
-
|
|
71
|
+
} catch {
|
|
72
|
+
// Silently ignore — settings write failure is non-blocking.
|
|
71
73
|
return false;
|
|
72
74
|
}
|
|
73
75
|
}
|
|
@@ -92,13 +94,15 @@ export function loadFooterSettings(): FooterSettings {
|
|
|
92
94
|
preset: typeof footer.preset === "string" ? footer.preset : DEFAULT_FOOTER_SETTINGS.preset,
|
|
93
95
|
separator: isValidSeparator(footer.separator) ? footer.separator as SeparatorStyle : DEFAULT_FOOTER_SETTINGS.separator,
|
|
94
96
|
iconStyle: isValidIconStyle(footer.iconStyle) ? footer.iconStyle as IconStyle : DEFAULT_FOOTER_SETTINGS.iconStyle,
|
|
97
|
+
zoneSeparator: typeof footer.zoneSeparator === "string" ? footer.zoneSeparator : DEFAULT_FOOTER_SETTINGS.zoneSeparator,
|
|
98
|
+
showFullLabels: typeof footer.showFullLabels === "boolean" ? footer.showFullLabels : DEFAULT_FOOTER_SETTINGS.showFullLabels,
|
|
95
99
|
groups: mergeGroupSettings(
|
|
96
100
|
DEFAULT_FOOTER_SETTINGS.groups,
|
|
97
101
|
footer.groups as Record<string, FooterGroupSettings> | undefined,
|
|
98
102
|
),
|
|
99
103
|
};
|
|
100
|
-
} catch
|
|
101
|
-
|
|
104
|
+
} catch {
|
|
105
|
+
// Silently ignore — parse failure falls back to defaults.
|
|
102
106
|
return { ...DEFAULT_FOOTER_SETTINGS };
|
|
103
107
|
}
|
|
104
108
|
}
|