@danypops/pi-packed 0.8.0 → 0.10.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/extension/src/discover.ts +13 -21
- package/extension/src/menu-theme.ts +14 -0
- package/extension/src/resource-config.ts +3 -2
- package/extension/src/tui.ts +72 -40
- package/package.json +3 -2
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
* itself never mutates; only Install/Cancel (behind the standard
|
|
6
6
|
* approval) does.
|
|
7
7
|
*/
|
|
8
|
-
import type { ExtensionCommandContext
|
|
8
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { DynamicBorder, rawKeyHint } from "@earendil-works/pi-coding-agent";
|
|
10
|
-
import { Container, Input,
|
|
10
|
+
import { Container, Input, Spacer, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
11
|
+
import { Menu, type MenuItem } from "malevich-tui-components";
|
|
11
12
|
import { shouldSearch } from "./discover-model.js";
|
|
12
13
|
import type { Natives, PackageSummary } from "./packed.js";
|
|
13
14
|
import { InstallServiceError } from "./packed.js";
|
|
14
15
|
import { approvePackageOperation } from "./tools.js";
|
|
16
|
+
import { menuTheme } from "./menu-theme.js";
|
|
15
17
|
|
|
16
18
|
const SEARCH_LIMIT = 20;
|
|
17
19
|
|
|
@@ -51,28 +53,17 @@ export async function applyInstall(result: PackageSummary, natives: Natives, ctx
|
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
function selectListTheme(theme: Theme): SelectListTheme {
|
|
55
|
-
return {
|
|
56
|
-
selectedPrefix: (text) => theme.fg("accent", text),
|
|
57
|
-
selectedText: (text) => theme.fg("accent", text),
|
|
58
|
-
description: (text) => theme.fg("muted", text),
|
|
59
|
-
scrollInfo: (text) => theme.fg("muted", text),
|
|
60
|
-
noMatch: (text) => theme.fg("muted", text),
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
56
|
async function showInstallMenu(ctx: ExtensionCommandContext, result: PackageSummary): Promise<boolean> {
|
|
65
|
-
|
|
66
|
-
const choice = await ctx.ui.custom<"install" | "cancel" | undefined>(
|
|
57
|
+
return ctx.ui.custom<boolean>(
|
|
67
58
|
(_tui, theme, _kb, done) => {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
59
|
+
const items: MenuItem[] = [
|
|
60
|
+
{ label: `Install ${result.name}@${result.version}`, action: () => done(true) },
|
|
61
|
+
{ label: "Cancel", action: () => done(false) },
|
|
62
|
+
];
|
|
63
|
+
return new Menu({ items, theme: menuTheme(theme), onClose: () => done(false) });
|
|
72
64
|
},
|
|
73
65
|
{ overlay: true, overlayOptions: { width: 40, anchor: "center" } },
|
|
74
66
|
);
|
|
75
|
-
return choice === "install";
|
|
76
67
|
}
|
|
77
68
|
|
|
78
69
|
export async function showDiscoverPanel(ctx: ExtensionCommandContext, natives: Natives): Promise<void> {
|
|
@@ -158,15 +149,16 @@ function renderDiscoverPanel(ctx: ExtensionCommandContext, natives: Natives): Pr
|
|
|
158
149
|
},
|
|
159
150
|
};
|
|
160
151
|
|
|
152
|
+
const border = () => new DynamicBorder((s) => theme.fg("border", s));
|
|
161
153
|
const container = new Container();
|
|
162
154
|
container.addChild(new Spacer(1));
|
|
163
|
-
container.addChild(
|
|
155
|
+
container.addChild(border());
|
|
164
156
|
container.addChild(new Spacer(1));
|
|
165
157
|
container.addChild(header);
|
|
166
158
|
container.addChild(new Spacer(1));
|
|
167
159
|
container.addChild(list);
|
|
168
160
|
container.addChild(new Spacer(1));
|
|
169
|
-
container.addChild(
|
|
161
|
+
container.addChild(border());
|
|
170
162
|
|
|
171
163
|
return {
|
|
172
164
|
render: (width: number) => container.render(width),
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { MenuTheme } from "malevich-tui-components";
|
|
3
|
+
|
|
4
|
+
/** Maps Pi's own Theme onto Malevich's Menu -- the one place this mapping
|
|
5
|
+
* exists, shared by every ctx.ui.custom overlay menu in this extension. */
|
|
6
|
+
export function menuTheme(theme: Theme): MenuTheme {
|
|
7
|
+
return {
|
|
8
|
+
border: (s) => theme.fg("border", s),
|
|
9
|
+
selected: (s) => theme.fg("accent", s),
|
|
10
|
+
normal: (s) => s,
|
|
11
|
+
dim: (s) => theme.fg("muted", s),
|
|
12
|
+
title: (s) => theme.fg("accent", s),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -190,15 +190,16 @@ function renderConfig(ctx: ExtensionCommandContext, data: { global: PackageResou
|
|
|
190
190
|
},
|
|
191
191
|
};
|
|
192
192
|
|
|
193
|
+
const border = () => new DynamicBorder((s) => theme.fg("border", s));
|
|
193
194
|
const container = new Container();
|
|
194
195
|
container.addChild(new Spacer(1));
|
|
195
|
-
container.addChild(
|
|
196
|
+
container.addChild(border());
|
|
196
197
|
container.addChild(new Spacer(1));
|
|
197
198
|
container.addChild(header);
|
|
198
199
|
container.addChild(new Spacer(1));
|
|
199
200
|
container.addChild(list);
|
|
200
201
|
container.addChild(new Spacer(1));
|
|
201
|
-
container.addChild(
|
|
202
|
+
container.addChild(border());
|
|
202
203
|
|
|
203
204
|
return {
|
|
204
205
|
render: (width: number) => container.render(width),
|
package/extension/src/tui.ts
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
* underneath never disappears. All data flows through the packed CLI
|
|
12
12
|
* (thin seam).
|
|
13
13
|
*/
|
|
14
|
-
import type { ExtensionCommandContext
|
|
14
|
+
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
15
15
|
import { DynamicBorder, rawKeyHint } from "@earendil-works/pi-coding-agent";
|
|
16
|
-
import { Container, Input,
|
|
16
|
+
import { Container, Input, Spacer, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
17
|
+
import { Menu, ProgressBar, type MenuItem } from "malevich-tui-components";
|
|
17
18
|
import { filterRows, mergeRows, nextMode, visibleRows } from "./model.js";
|
|
18
19
|
import type { Row, ViewMode } from "./model.js";
|
|
19
20
|
import type { Natives, PackageResources } from "./packed.js";
|
|
@@ -21,6 +22,7 @@ import { approvePackageOperation } from "./tools.js";
|
|
|
21
22
|
import { showPackedSettings } from "./security-tui.js";
|
|
22
23
|
import { showResourceConfig, applyResourceToggle } from "./resource-config.js";
|
|
23
24
|
import { showDiscoverPanel } from "./discover.js";
|
|
25
|
+
import { menuTheme } from "./menu-theme.js";
|
|
24
26
|
|
|
25
27
|
interface PanelAction {
|
|
26
28
|
type: "update" | "updateAll" | "remove" | "disable" | "config" | "find" | "refresh" | "settings";
|
|
@@ -84,6 +86,57 @@ export async function applyPackageChoice(
|
|
|
84
86
|
return "cancelled";
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
interface UpdateAllResult { changed: number; failedNames: string[]; }
|
|
90
|
+
|
|
91
|
+
/** Floats a live progress bar over the still-open panel while the batch
|
|
92
|
+
* runs sequentially -- the lazy.nvim-style "Updating N package(s)" window,
|
|
93
|
+
* instead of one static toast with no visible progress until it's all
|
|
94
|
+
* done. Resolves on its own once the last package finishes; nothing the
|
|
95
|
+
* user presses closes it early. */
|
|
96
|
+
async function runUpdatesWithProgress(
|
|
97
|
+
outdated: Row[],
|
|
98
|
+
natives: Natives,
|
|
99
|
+
approved: boolean | undefined,
|
|
100
|
+
ctx: ExtensionCommandContext,
|
|
101
|
+
): Promise<UpdateAllResult> {
|
|
102
|
+
return ctx.ui.custom<UpdateAllResult>(
|
|
103
|
+
(tui, theme, _kb, done) => {
|
|
104
|
+
const bar = new ProgressBar({ value: 0, max: outdated.length, label: `${outdated[0]?.name ?? ""} (1/${outdated.length})`, style: (s) => theme.fg("accent", s) });
|
|
105
|
+
const border = () => new DynamicBorder((s) => theme.fg("border", s));
|
|
106
|
+
const container = new Container();
|
|
107
|
+
container.addChild(new Spacer(1));
|
|
108
|
+
container.addChild(border());
|
|
109
|
+
container.addChild({ invalidate() {}, render: (_width: number) => [theme.bold("Updating packages")] });
|
|
110
|
+
container.addChild(new Spacer(1));
|
|
111
|
+
container.addChild(bar);
|
|
112
|
+
container.addChild(new Spacer(1));
|
|
113
|
+
container.addChild(border());
|
|
114
|
+
|
|
115
|
+
void (async () => {
|
|
116
|
+
let changed = 0;
|
|
117
|
+
const failedNames: string[] = [];
|
|
118
|
+
for (let i = 0; i < outdated.length; i++) {
|
|
119
|
+
const row = outdated[i]!;
|
|
120
|
+
bar.setLabel(`${row.name} (${i + 1}/${outdated.length})`);
|
|
121
|
+
tui.requestRender();
|
|
122
|
+
try {
|
|
123
|
+
const outcome = await natives.update(`npm:${row.name}`, approved);
|
|
124
|
+
if (outcome.reloadRequired) changed += 1;
|
|
125
|
+
} catch (e) {
|
|
126
|
+
failedNames.push(`${row.name}: ${e instanceof Error ? e.message : e}`);
|
|
127
|
+
}
|
|
128
|
+
bar.setValue(i + 1);
|
|
129
|
+
tui.requestRender();
|
|
130
|
+
}
|
|
131
|
+
done({ changed, failedNames });
|
|
132
|
+
})();
|
|
133
|
+
|
|
134
|
+
return { render: (width: number) => container.render(width), invalidate: () => container.invalidate(), handleInput() {} };
|
|
135
|
+
},
|
|
136
|
+
{ overlay: true, overlayOptions: { width: 50, anchor: "center" } },
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
87
140
|
/** U -- update every outdated row with one combined approval and one
|
|
88
141
|
* reload, instead of applyPackageChoice's own per-call reload (which
|
|
89
142
|
* would end the session after the first successful update). */
|
|
@@ -103,23 +156,13 @@ export async function applyUpdateAll(rows: Row[], natives: Natives, ctx: Extensi
|
|
|
103
156
|
ctx.ui.notify(approval.message ?? "update denied", "warning");
|
|
104
157
|
return "cancelled";
|
|
105
158
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
let failed = 0;
|
|
109
|
-
for (const row of outdated) {
|
|
110
|
-
try {
|
|
111
|
-
const outcome = await natives.update(`npm:${row.name}`, approval.approved);
|
|
112
|
-
if (outcome.reloadRequired) changed += 1;
|
|
113
|
-
} catch (e) {
|
|
114
|
-
failed += 1;
|
|
115
|
-
ctx.ui.notify(`${row.name} update failed: ${e instanceof Error ? e.message : e}`, "error");
|
|
116
|
-
}
|
|
117
|
-
}
|
|
159
|
+
const { changed, failedNames } = await runUpdatesWithProgress(outdated, natives, approval.approved, ctx);
|
|
160
|
+
for (const failure of failedNames) ctx.ui.notify(`update failed: ${failure}`, "error");
|
|
118
161
|
if (changed === 0) {
|
|
119
|
-
ctx.ui.notify(
|
|
120
|
-
return
|
|
162
|
+
ctx.ui.notify(failedNames.length > 0 ? `No packages updated; ${failedNames.length} failed.` : "All packages already up to date.", failedNames.length > 0 ? "warning" : "info");
|
|
163
|
+
return failedNames.length > 0 ? "cancelled" : "unchanged";
|
|
121
164
|
}
|
|
122
|
-
ctx.ui.notify(`Updated ${changed} package(s)${
|
|
165
|
+
ctx.ui.notify(`Updated ${changed} package(s)${failedNames.length > 0 ? `, ${failedNames.length} failed` : ""}; reloading Pi resources.`, "info");
|
|
123
166
|
await ctx.reload();
|
|
124
167
|
return "changed";
|
|
125
168
|
}
|
|
@@ -173,32 +216,20 @@ async function loadRows(natives: Natives): Promise<{ rows: Row[]; error?: string
|
|
|
173
216
|
}
|
|
174
217
|
}
|
|
175
218
|
|
|
176
|
-
function selectListTheme(theme: Theme): SelectListTheme {
|
|
177
|
-
return {
|
|
178
|
-
selectedPrefix: (text) => theme.fg("accent", text),
|
|
179
|
-
selectedText: (text) => theme.fg("accent", text),
|
|
180
|
-
description: (text) => theme.fg("muted", text),
|
|
181
|
-
scrollInfo: (text) => theme.fg("muted", text),
|
|
182
|
-
noMatch: (text) => theme.fg("muted", text),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
219
|
/** Enter's action menu, floated on top of the still-open packages panel
|
|
187
220
|
* via ctx.ui.custom's own overlay:true -- no full-screen teardown, unlike
|
|
188
221
|
* the ctx.ui.select this replaced. */
|
|
189
222
|
async function showActionMenu(ctx: ExtensionCommandContext, row: Row): Promise<"update" | "remove" | "disable" | "config" | undefined> {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
{ value: "disable", label: "Disable/enable extensions" },
|
|
193
|
-
{ value: "config", label: "Configure resources" },
|
|
194
|
-
{ value: "remove", label: "Remove" },
|
|
195
|
-
];
|
|
196
|
-
return ctx.ui.custom<"update" | "remove" | "disable" | "config" | undefined>(
|
|
223
|
+
type Choice = "update" | "remove" | "disable" | "config";
|
|
224
|
+
return ctx.ui.custom<Choice | undefined>(
|
|
197
225
|
(_tui, theme, _kb, done) => {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
226
|
+
const items: MenuItem[] = [
|
|
227
|
+
...(row.hasUpdate ? [{ label: `Update to ${row.latest}`, action: () => done("update" as Choice) }] : []),
|
|
228
|
+
{ label: "Disable/enable extensions", action: () => done("disable") },
|
|
229
|
+
{ label: "Configure resources", action: () => done("config") },
|
|
230
|
+
{ label: "Remove", action: () => done("remove") },
|
|
231
|
+
];
|
|
232
|
+
return new Menu({ items, theme: menuTheme(theme), onClose: () => done(undefined) });
|
|
202
233
|
},
|
|
203
234
|
{ overlay: true, overlayOptions: { width: 40, anchor: "center" } },
|
|
204
235
|
);
|
|
@@ -339,15 +370,16 @@ function renderPanel(ctx: ExtensionCommandContext, rows: Row[]): Promise<PanelAc
|
|
|
339
370
|
},
|
|
340
371
|
};
|
|
341
372
|
|
|
373
|
+
const border = () => new DynamicBorder((s) => theme.fg("border", s));
|
|
342
374
|
const container = new Container();
|
|
343
375
|
container.addChild(new Spacer(1));
|
|
344
|
-
container.addChild(
|
|
376
|
+
container.addChild(border());
|
|
345
377
|
container.addChild(new Spacer(1));
|
|
346
378
|
container.addChild(header);
|
|
347
379
|
container.addChild(new Spacer(1));
|
|
348
380
|
container.addChild(list);
|
|
349
381
|
container.addChild(new Spacer(1));
|
|
350
|
-
container.addChild(
|
|
382
|
+
container.addChild(border());
|
|
351
383
|
|
|
352
384
|
return {
|
|
353
385
|
render: (width: number) => container.render(width),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-packed",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Pi package tools, commands, profiles, and TUI for the Packed daemon",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": ["pi-package"],
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@danypops/packed": "^0.2.0",
|
|
16
16
|
"@danypops/vehicle-client": "^0.1.1",
|
|
17
|
-
"@danypops/vehicle-client-pi": "^0.1.5"
|
|
17
|
+
"@danypops/vehicle-client-pi": "^0.1.5",
|
|
18
|
+
"malevich-tui-components": "^0.7.0"
|
|
18
19
|
},
|
|
19
20
|
"peerDependencies": {
|
|
20
21
|
"@earendil-works/pi-coding-agent": "*",
|