@danypops/pi-packed 0.13.1 → 0.14.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 +1 -1
- package/extension/src/reload.ts +25 -0
- package/extension/src/resource-config.ts +2 -2
- package/extension/src/tui.ts +56 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The extension connects to Packed's authenticated user daemon and starts the pack
|
|
|
11
11
|
## Commands
|
|
12
12
|
|
|
13
13
|
- `/packed` -- a floating overlay panel: every installed Pi package, with update availability. Mnemonics follow lazy.nvim's own convention (uppercase acts on every row, lowercase on the one under the cursor):
|
|
14
|
-
- `u` / `U` -- update the selected package / update every outdated package, one combined confirmation and one reload. `U`'s progress bar appears inline next to the package currently updating -- the list stays visible throughout, nothing swaps to a separate screen.
|
|
14
|
+
- `u` / `U` -- update the selected package / update every outdated package, one combined confirmation and one reload. `U`'s progress bar appears inline next to the package currently updating -- the list stays visible throughout, nothing swaps to a separate screen. A reload is confirmed separately from the update itself -- decline it to defer: the update already happened, only picking it up in this Pi session is deferred until `/reload`.
|
|
15
15
|
- `x` -- remove the selected package.
|
|
16
16
|
- `d` -- disable (or re-enable) the selected package's own extensions.
|
|
17
17
|
- `c` -- jump to resource config for the selected package (skills, prompts, themes).
|
package/extension/src/reload.ts
CHANGED
|
@@ -14,3 +14,28 @@ export function reloadWarning(operation: PackageOperation): string {
|
|
|
14
14
|
? "This will require a Pi reload (/reload) to deactivate it."
|
|
15
15
|
: "This will likely require a Pi reload (/reload) to activate its resources.";
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
export interface ReloadConfirmContext {
|
|
19
|
+
hasUI: boolean;
|
|
20
|
+
ui: { confirm(title: string, message: string): Promise<boolean> };
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The second, separate decision point: reloadWarning above is a still-just-
|
|
25
|
+
* likely warning shown before a mutation runs; this is asked only once the
|
|
26
|
+
* mutation has actually succeeded and a reload is now definitely needed,
|
|
27
|
+
* not merely predicted. Declining defers it -- the mutation itself already
|
|
28
|
+
* happened (the package really did install/update/toggle); only Pi's own
|
|
29
|
+
* currently-loaded resources are stale until /reload runs later. No-UI
|
|
30
|
+
* contexts reload immediately: there's no one to ask, and staying silently
|
|
31
|
+
* stale forever is worse than reloading.
|
|
32
|
+
*
|
|
33
|
+
* Extracted from resource-config.ts's own pre-existing pendingReload gate
|
|
34
|
+
* (the first surface to implement this pattern) so every mutation surface
|
|
35
|
+
* shares identical wording and behavior instead of drifting apart --
|
|
36
|
+
* reload.ts's whole reason for existing.
|
|
37
|
+
*/
|
|
38
|
+
export async function confirmReload(ctx: ReloadConfirmContext): Promise<boolean> {
|
|
39
|
+
if (!ctx.hasUI) return true;
|
|
40
|
+
return ctx.ui.confirm("Reload Pi now?", "This change only takes effect after a reload.");
|
|
41
|
+
}
|
|
@@ -14,6 +14,7 @@ import { Container, Input, Spacer, truncateToWidth, visibleWidth } from "@earend
|
|
|
14
14
|
import type { PackageResources, ResourceField } from "./packed.js";
|
|
15
15
|
import type { Natives } from "./packed.js";
|
|
16
16
|
import { packagePermissionDecision } from "./permission.js";
|
|
17
|
+
import { confirmReload } from "./reload.js";
|
|
17
18
|
|
|
18
19
|
type Scope = "global" | "project";
|
|
19
20
|
const RESOURCE_FIELDS = ["extensions", "skills", "prompts", "themes"] as const satisfies readonly ResourceField[];
|
|
@@ -132,8 +133,7 @@ export async function showResourceConfig(ctx: ExtensionCommandContext, natives:
|
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
if (!pendingReload) return;
|
|
135
|
-
|
|
136
|
-
if (confirmed) await ctx.reload();
|
|
136
|
+
if (await confirmReload(ctx)) await ctx.reload();
|
|
137
137
|
else ctx.ui.notify("Extension changes pending -- run /reload when ready.", "warning");
|
|
138
138
|
}
|
|
139
139
|
|
package/extension/src/tui.ts
CHANGED
|
@@ -8,16 +8,22 @@
|
|
|
8
8
|
* extensions, c jump to full resource config, f find/install new
|
|
9
9
|
* packages, s settings. The panel itself is a real bordered (rounded)
|
|
10
10
|
* floating overlay (`ctx.ui.custom` with overlay:true, Malevich's
|
|
11
|
-
* Envelope for the box),
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* Envelope for the box), positioned at 40% of terminal height (pi-tui's
|
|
12
|
+
* own row percentage, not a fixed row count -- scales with the real
|
|
13
|
+
* terminal, unlike the anchor:"top-center"+offsetY this replaced, which
|
|
14
|
+
* was pinned 1 row below the absolute top on any terminal size). Enter
|
|
15
|
+
* opens a second, smaller overlay action menu on top of it. U's batch update stays on
|
|
14
16
|
* this same package list -- progress renders inline next to the row
|
|
15
17
|
* currently being updated, not as a separate screen. Rows render through
|
|
16
18
|
* Malevich's Table (real column-aligned Package/Version/status cells,
|
|
17
19
|
* per-row selection styling baked into each cell since Table's own
|
|
18
20
|
* cellStyle is column-wide, not row-wide) inside this panel's own
|
|
19
21
|
* scroll-window slice -- Table deliberately owns no pagination of its
|
|
20
|
-
* own, so the visible-window-around-selectedIndex math stays here.
|
|
22
|
+
* own, so the visible-window-around-selectedIndex math stays here. Every
|
|
23
|
+
* mutation that actually changes something asks confirmReload separately
|
|
24
|
+
* from the earlier mutation-approval confirm -- declining defers the
|
|
25
|
+
* reload (the mutation itself already happened) and keeps the panel open
|
|
26
|
+
* with refreshed rows instead of ending the session. All
|
|
21
27
|
* data flows through the packed CLI (thin seam).
|
|
22
28
|
*/
|
|
23
29
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
@@ -32,6 +38,7 @@ import { showPackedSettings } from "./security-tui.js";
|
|
|
32
38
|
import { showResourceConfig, applyResourceToggle } from "./resource-config.js";
|
|
33
39
|
import { showDiscoverPanel } from "./discover.js";
|
|
34
40
|
import { menuTheme } from "./menu-theme.js";
|
|
41
|
+
import { confirmReload } from "./reload.js";
|
|
35
42
|
|
|
36
43
|
interface PanelAction {
|
|
37
44
|
type: "update" | "remove" | "disable" | "config" | "find" | "refresh" | "settings";
|
|
@@ -41,8 +48,11 @@ interface PanelAction {
|
|
|
41
48
|
/** Outcome of a confirmed row action, resolved after any real mutation and
|
|
42
49
|
* reload decision -- "changed" means the daemon state changed and Pi has
|
|
43
50
|
* already been reloaded (the caller should stop showing the stale panel);
|
|
51
|
+
* "deferred" means the mutation itself genuinely happened but the user
|
|
52
|
+
* declined confirmReload's separate reload gate -- Pi's session is still
|
|
53
|
+
* alive and the panel should refresh its rows and keep running, not close;
|
|
44
54
|
* "unchanged"/"cancelled" mean the panel keeps running as-is. */
|
|
45
|
-
export type PackageChoiceOutcome = "changed" | "unchanged" | "cancelled";
|
|
55
|
+
export type PackageChoiceOutcome = "changed" | "unchanged" | "cancelled" | "deferred";
|
|
46
56
|
|
|
47
57
|
export async function applyPackageChoice(
|
|
48
58
|
choice: string | undefined,
|
|
@@ -68,6 +78,10 @@ export async function applyPackageChoice(
|
|
|
68
78
|
return "unchanged";
|
|
69
79
|
}
|
|
70
80
|
const transition = outcome.previousVersion && outcome.currentVersion ? ` (${outcome.previousVersion} → ${outcome.currentVersion})` : "";
|
|
81
|
+
if (!(await confirmReload(ctx))) {
|
|
82
|
+
ctx.ui.notify(`Updated ${row.name}${transition}; reload pending -- run /reload when ready.`, "warning");
|
|
83
|
+
return "deferred";
|
|
84
|
+
}
|
|
71
85
|
ctx.ui.notify(`Updated ${row.name}${transition}; reloading Pi resources.`, "info");
|
|
72
86
|
await ctx.reload();
|
|
73
87
|
return "changed";
|
|
@@ -84,6 +98,10 @@ export async function applyPackageChoice(
|
|
|
84
98
|
return "cancelled";
|
|
85
99
|
}
|
|
86
100
|
await natives.remove(row.name, approval.approved);
|
|
101
|
+
if (!(await confirmReload(ctx))) {
|
|
102
|
+
ctx.ui.notify(`Removed ${row.name}; reload pending -- run /reload when ready.`, "warning");
|
|
103
|
+
return "deferred";
|
|
104
|
+
}
|
|
87
105
|
ctx.ui.notify(`Removed ${row.name}; reloading Pi resources.`, "info");
|
|
88
106
|
await ctx.reload();
|
|
89
107
|
return "changed";
|
|
@@ -154,7 +172,12 @@ async function approveAndRunUpdateAll(
|
|
|
154
172
|
ctx.ui.notify(failedNames.length > 0 ? `No packages updated; ${failedNames.length} failed.` : "All packages already up to date.", failedNames.length > 0 ? "warning" : "info");
|
|
155
173
|
return failedNames.length > 0 ? "cancelled" : "unchanged";
|
|
156
174
|
}
|
|
157
|
-
|
|
175
|
+
const failedSuffix = failedNames.length > 0 ? `, ${failedNames.length} failed` : "";
|
|
176
|
+
if (!(await confirmReload(ctx))) {
|
|
177
|
+
ctx.ui.notify(`Updated ${changed} package(s)${failedSuffix}; reload pending -- run /reload when ready.`, "warning");
|
|
178
|
+
return "deferred";
|
|
179
|
+
}
|
|
180
|
+
ctx.ui.notify(`Updated ${changed} package(s)${failedSuffix}; reloading Pi resources.`, "info");
|
|
158
181
|
await ctx.reload();
|
|
159
182
|
return "changed";
|
|
160
183
|
}
|
|
@@ -237,7 +260,12 @@ export async function applyDisableExtensions(row: Row, natives: Natives, ctx: Ex
|
|
|
237
260
|
else if (outcome === "cancelled") return toggled > 0 ? "changed" : "cancelled";
|
|
238
261
|
}
|
|
239
262
|
if (toggled === 0) return "unchanged";
|
|
240
|
-
|
|
263
|
+
const verb = disabling ? "Disabled" : "Enabled";
|
|
264
|
+
if (!(await confirmReload(ctx))) {
|
|
265
|
+
ctx.ui.notify(`${verb} ${toggled} extension(s) for ${row.name}; reload pending -- run /reload when ready.`, "warning");
|
|
266
|
+
return "deferred";
|
|
267
|
+
}
|
|
268
|
+
ctx.ui.notify(`${verb} ${toggled} extension(s) for ${row.name}; reloading Pi resources.`, "info");
|
|
241
269
|
await ctx.reload();
|
|
242
270
|
return "changed";
|
|
243
271
|
}
|
|
@@ -285,6 +313,14 @@ export async function showPackedPanel(ctx: ExtensionCommandContext, natives: Nat
|
|
|
285
313
|
return;
|
|
286
314
|
}
|
|
287
315
|
|
|
316
|
+
// A deferred reload means the mutation itself genuinely happened but
|
|
317
|
+
// ctx.reload() was declined -- the session is still alive, so refresh
|
|
318
|
+
// rows (real on-disk versions) and keep the panel open, same as "refresh".
|
|
319
|
+
async function refreshRows(): Promise<void> {
|
|
320
|
+
({ rows, error } = await loadRows(natives));
|
|
321
|
+
if (error) ctx.ui.notify(`refresh failed: ${error}`, "error");
|
|
322
|
+
}
|
|
323
|
+
|
|
288
324
|
// Panel loop: actions resolve the component, run outside it, then reopen.
|
|
289
325
|
// U/updateAll is handled entirely inside renderPanel itself (progress
|
|
290
326
|
// rendered on the same already-open overlay) and never reaches here.
|
|
@@ -293,8 +329,7 @@ export async function showPackedPanel(ctx: ExtensionCommandContext, natives: Nat
|
|
|
293
329
|
if (!action) return; // closed
|
|
294
330
|
|
|
295
331
|
if (action.type === "refresh") {
|
|
296
|
-
|
|
297
|
-
if (error) ctx.ui.notify(`refresh failed: ${error}`, "error");
|
|
332
|
+
await refreshRows();
|
|
298
333
|
continue;
|
|
299
334
|
}
|
|
300
335
|
|
|
@@ -319,11 +354,13 @@ export async function showPackedPanel(ctx: ExtensionCommandContext, natives: Nat
|
|
|
319
354
|
if (action.type === "disable") {
|
|
320
355
|
const outcome = await applyDisableExtensions(row, natives, ctx);
|
|
321
356
|
if (outcome === "changed") return;
|
|
357
|
+
if (outcome === "deferred") await refreshRows();
|
|
322
358
|
continue;
|
|
323
359
|
}
|
|
324
360
|
|
|
325
361
|
const outcome = await applyPackageChoice(action.type === "update" ? `Update to ${row.latest}` : "Remove", row, natives, ctx);
|
|
326
362
|
if (outcome === "changed") return; // ctx.reload() already replaced the session
|
|
363
|
+
if (outcome === "deferred") await refreshRows();
|
|
327
364
|
}
|
|
328
365
|
}
|
|
329
366
|
|
|
@@ -576,5 +613,14 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
576
613
|
tui.requestRender();
|
|
577
614
|
},
|
|
578
615
|
};
|
|
579
|
-
|
|
616
|
+
// row is a real percentage of terminal height (0%=top, 100%=bottom) --
|
|
617
|
+
// unlike anchor:"top-center"+offsetY (a fixed row count, effectively
|
|
618
|
+
// pinned to the very top on any terminal size), this scales with the
|
|
619
|
+
// actual screen. anchor:"center" still governs horizontal centering
|
|
620
|
+
// (row overrides only the vertical resolution). Note this isn't fully
|
|
621
|
+
// content-height-independent -- pi-tui's own row-percentage formula
|
|
622
|
+
// interpolates over the range of positions that still fit the current
|
|
623
|
+
// content height, so a shorter/taller row count shifts this somewhat;
|
|
624
|
+
// only exactly 0%/100% are perfectly jitter-free in that library.
|
|
625
|
+
}, { overlay: true, overlayOptions: { width: "70%", maxHeight: "70%", anchor: "center", row: "40%" } });
|
|
580
626
|
}
|