@danypops/pi-packed 0.11.0 → 0.12.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/tui.ts +58 -57
- package/package.json +2 -2
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
|
|
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.
|
|
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/tui.ts
CHANGED
|
@@ -6,18 +6,19 @@
|
|
|
6
6
|
* only (x remove -- there is no safe "remove every installed package"
|
|
7
7
|
* bulk analog, so no X is bound). Adds d disable/enable this package's
|
|
8
8
|
* extensions, c jump to full resource config, f find/install new
|
|
9
|
-
* packages, s settings. The panel itself is a
|
|
10
|
-
* (`ctx.ui.custom` with overlay:true
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
9
|
+
* packages, s settings. The panel itself is a real bordered (rounded)
|
|
10
|
+
* floating overlay (`ctx.ui.custom` with overlay:true, Malevich's
|
|
11
|
+
* Envelope for the box), anchored to the top so its header stays put and
|
|
12
|
+
* only the footer moves as content height changes. Enter opens a second,
|
|
13
|
+
* smaller overlay action menu on top of it. U's batch update stays on
|
|
14
|
+
* this same package list -- progress renders inline next to the row
|
|
15
|
+
* currently being updated, not as a separate screen. All data flows
|
|
16
|
+
* through the packed CLI (thin seam).
|
|
16
17
|
*/
|
|
17
18
|
import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
|
|
18
19
|
import { DynamicBorder, rawKeyHint } from "@earendil-works/pi-coding-agent";
|
|
19
|
-
import { Container, Input, Spacer, truncateToWidth
|
|
20
|
-
import { Menu, ProgressBar, type MenuItem } from "malevich-tui-components";
|
|
20
|
+
import { Container, Input, Spacer, truncateToWidth } from "@earendil-works/pi-tui";
|
|
21
|
+
import { Envelope, Menu, ProgressBar, type MenuItem } from "malevich-tui-components";
|
|
21
22
|
import { filterRows, mergeRows, nextMode, visibleRows } from "./model.js";
|
|
22
23
|
import type { Row, ViewMode } from "./model.js";
|
|
23
24
|
import type { Natives, PackageResources } from "./packed.js";
|
|
@@ -329,9 +330,11 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
329
330
|
let searchActive = false;
|
|
330
331
|
let filtered = visibleRows(rows, mode);
|
|
331
332
|
let selectedIndex = 0;
|
|
332
|
-
// Set only while U's batch update is running
|
|
333
|
-
//
|
|
334
|
-
|
|
333
|
+
// Set only while U's batch update is running. The list stays fully
|
|
334
|
+
// visible throughout -- this just names which row the shared bar
|
|
335
|
+
// (below) is currently sitting next to.
|
|
336
|
+
let updatingRowName: string | undefined;
|
|
337
|
+
const updatingBar = new ProgressBar({ value: 0, max: 1, width: 10, style: (s) => theme.fg("accent", s) });
|
|
335
338
|
|
|
336
339
|
const maxVisible = 20;
|
|
337
340
|
|
|
@@ -341,22 +344,23 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
341
344
|
}
|
|
342
345
|
|
|
343
346
|
/** U -- runs the whole approve+update+notify+reload flow without ever
|
|
344
|
-
* closing this panel
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
+
* closing this panel or replacing the list: each step's progress
|
|
348
|
+
* appears inline next to the row currently being updated, via
|
|
349
|
+
* updatingRowName/updatingBar, which list's own render checks. Rows
|
|
350
|
+
* refresh in place afterward unless a reload already ended the
|
|
351
|
+
* session. */
|
|
347
352
|
async function runUpdateAllInline(): Promise<void> {
|
|
348
353
|
const outdated = rows.filter((row) => row.hasUpdate);
|
|
349
354
|
const outcome = await approveAndRunUpdateAll(outdated, natives, ctx, (batch, approved) => {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
tui.requestRender();
|
|
355
|
+
updatingBar.setValue(0);
|
|
356
|
+
updatingBar.setMax(batch.length);
|
|
353
357
|
return performUpdateAll(batch, natives, approved, (event) => {
|
|
354
|
-
|
|
355
|
-
if (event.phase === "done")
|
|
358
|
+
updatingRowName = event.row.name;
|
|
359
|
+
if (event.phase === "done") updatingBar.setValue(event.index + 1);
|
|
356
360
|
tui.requestRender();
|
|
357
361
|
});
|
|
358
362
|
});
|
|
359
|
-
|
|
363
|
+
updatingRowName = undefined;
|
|
360
364
|
if (outcome === "changed") {
|
|
361
365
|
done(undefined); // ctx.reload() already replaced the session
|
|
362
366
|
return;
|
|
@@ -368,12 +372,15 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
368
372
|
tui.requestRender();
|
|
369
373
|
}
|
|
370
374
|
|
|
375
|
+
function panelTitle(): string {
|
|
376
|
+
if (updatingRowName) return "Packages · updating…";
|
|
377
|
+
const outdated = rows.filter((r) => r.hasUpdate).length;
|
|
378
|
+
return outdated > 0 ? `Packages · ${outdated} update(s)` : "Packages";
|
|
379
|
+
}
|
|
380
|
+
|
|
371
381
|
const header = {
|
|
372
382
|
invalidate() {},
|
|
373
383
|
render(width: number): string[] {
|
|
374
|
-
const title = theme.bold("Packages");
|
|
375
|
-
const outdated = rows.filter((r) => r.hasUpdate).length;
|
|
376
|
-
const badge = outdated > 0 ? theme.fg("warning", ` ${outdated} update(s)`) : "";
|
|
377
384
|
const hint = searchActive
|
|
378
385
|
? rawKeyHint("esc", "clear")
|
|
379
386
|
: rawKeyHint("enter", "menu") +
|
|
@@ -391,8 +398,7 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
391
398
|
rawKeyHint("s", "settings") +
|
|
392
399
|
theme.fg("muted", " · ") +
|
|
393
400
|
rawKeyHint("esc", "close");
|
|
394
|
-
const
|
|
395
|
-
const line1 = truncateToWidth(`${title}${badge}${" ".repeat(spacing)}${hint}`, width, "");
|
|
401
|
+
const line1 = truncateToWidth(hint, width, "");
|
|
396
402
|
const dot = "·";
|
|
397
403
|
const line2 = truncateToWidth(
|
|
398
404
|
theme.fg("muted", `view: ${mode} ${dot} / filter ${dot} tab view ${dot} r refresh ${dot} ${rows.length} installed`),
|
|
@@ -421,7 +427,10 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
421
427
|
const cursor = selected ? theme.fg("accent", "❯") : " ";
|
|
422
428
|
const name = selected ? theme.bold(row.name) : row.name;
|
|
423
429
|
const ver = theme.fg("dim", `@${row.version}`);
|
|
424
|
-
const
|
|
430
|
+
const isUpdating = updatingRowName === row.name;
|
|
431
|
+
const upd = isUpdating
|
|
432
|
+
? theme.fg("accent", ` ${updatingBar.format(10)} updating…`)
|
|
433
|
+
: row.hasUpdate ? theme.fg("warning", ` ↑${row.latest}`) : "";
|
|
425
434
|
lines.push(truncateToWidth(`${cursor} ${name}${ver}${upd}`, width, ""));
|
|
426
435
|
}
|
|
427
436
|
const hasScroll = start > 0 || end < filtered.length;
|
|
@@ -430,38 +439,30 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
430
439
|
},
|
|
431
440
|
};
|
|
432
441
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
function buildInstallingContainer(bar: ProgressBar): Container {
|
|
449
|
-
const container = new Container();
|
|
450
|
-
container.addChild(new Spacer(1));
|
|
451
|
-
container.addChild(border());
|
|
452
|
-
container.addChild({ invalidate() {}, render: (_width: number) => [theme.bold("Updating packages")] });
|
|
453
|
-
container.addChild(new Spacer(1));
|
|
454
|
-
container.addChild(bar);
|
|
455
|
-
container.addChild(new Spacer(1));
|
|
456
|
-
container.addChild(border());
|
|
457
|
-
return container;
|
|
458
|
-
}
|
|
442
|
+
// Real bordered box (rounded corners), not just a horizontal rule --
|
|
443
|
+
// title carries the live update badge/status instead of a header line.
|
|
444
|
+
const envelope = new Envelope({
|
|
445
|
+
title: panelTitle(),
|
|
446
|
+
borderStyle: "rounded",
|
|
447
|
+
style: (s) => theme.fg("border", s),
|
|
448
|
+
titleStyle: (s) => theme.bold(theme.fg("accent", s)),
|
|
449
|
+
});
|
|
450
|
+
const body = {
|
|
451
|
+
invalidate() { header.invalidate(); list.invalidate(); },
|
|
452
|
+
render(width: number): string[] {
|
|
453
|
+
return [...header.render(width), "", ...list.render(width)];
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
envelope.setContent(body);
|
|
459
457
|
|
|
460
458
|
return {
|
|
461
|
-
render
|
|
462
|
-
|
|
459
|
+
render(width: number): string[] {
|
|
460
|
+
envelope.setTitle(panelTitle());
|
|
461
|
+
return envelope.render(width);
|
|
462
|
+
},
|
|
463
|
+
invalidate: () => envelope.invalidate(),
|
|
463
464
|
handleInput(data: string) {
|
|
464
|
-
if (
|
|
465
|
+
if (updatingRowName) return; // the installer owns input until it finishes
|
|
465
466
|
|
|
466
467
|
if (searchActive) {
|
|
467
468
|
if (data === "\x1b") {
|
|
@@ -545,5 +546,5 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
|
|
|
545
546
|
tui.requestRender();
|
|
546
547
|
},
|
|
547
548
|
};
|
|
548
|
-
}, { overlay: true, overlayOptions: { width: "70%", maxHeight: "70%", anchor: "center" } });
|
|
549
|
+
}, { overlay: true, overlayOptions: { width: "70%", maxHeight: "70%", anchor: "top-center", offsetY: 1 } });
|
|
549
550
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-packed",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Pi package tools, commands, profiles, and TUI for the Packed daemon",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": ["pi-package"],
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"@danypops/packed": "^0.2.0",
|
|
16
16
|
"@danypops/vehicle-client": "^0.1.1",
|
|
17
17
|
"@danypops/vehicle-client-pi": "^0.1.5",
|
|
18
|
-
"malevich-tui-components": "^0.
|
|
18
|
+
"malevich-tui-components": "^0.8.0"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@earendil-works/pi-coding-agent": "*",
|