@danypops/pi-packed 0.12.0 → 0.13.1

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.
@@ -12,13 +12,18 @@
12
12
  * only the footer moves as content height changes. Enter opens a second,
13
13
  * smaller overlay action menu on top of it. U's batch update stays on
14
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).
15
+ * currently being updated, not as a separate screen. Rows render through
16
+ * Malevich's Table (real column-aligned Package/Version/status cells,
17
+ * per-row selection styling baked into each cell since Table's own
18
+ * cellStyle is column-wide, not row-wide) inside this panel's own
19
+ * scroll-window slice -- Table deliberately owns no pagination of its
20
+ * own, so the visible-window-around-selectedIndex math stays here. All
21
+ * data flows through the packed CLI (thin seam).
17
22
  */
18
23
  import type { ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
19
24
  import { DynamicBorder, rawKeyHint } from "@earendil-works/pi-coding-agent";
20
- import { Container, Input, Spacer, truncateToWidth } from "@earendil-works/pi-tui";
21
- import { Envelope, Menu, ProgressBar, type MenuItem } from "malevich-tui-components";
25
+ import { Container, Input, Spacer, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
26
+ import { Envelope, Menu, ProgressBar, Table, type MenuItem, type TableColumn, type TextMeasure } from "malevich-tui-components";
22
27
  import { filterRows, mergeRows, nextMode, visibleRows } from "./model.js";
23
28
  import type { Row, ViewMode } from "./model.js";
24
29
  import type { Natives, PackageResources } from "./packed.js";
@@ -409,6 +414,19 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
409
414
  },
410
415
  };
411
416
 
417
+ // pi-tui's own visibleWidth/truncateToWidth are ANSI-aware (they strip
418
+ // escape codes for measurement, matching the pair Malevich's TextMeasure
419
+ // port expects) -- rows below bake selection/status styling directly
420
+ // into each cell's text since Table's own cellStyle is column-wide, not
421
+ // per-row.
422
+ const measure: TextMeasure = { visibleWidth, truncateToWidth };
423
+ const columns: TableColumn[] = [
424
+ { header: "Package", key: "name" },
425
+ { header: "Version", key: "version" },
426
+ { header: "", key: "status" },
427
+ ];
428
+ const table = new Table({ columns, rows: [], measure, headerStyle: (s) => theme.fg("muted", s) });
429
+
412
430
  const list = {
413
431
  invalidate() {},
414
432
  render(width: number): string[] {
@@ -419,20 +437,25 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
419
437
  lines.push(theme.fg("muted", " No packages"));
420
438
  return lines;
421
439
  }
440
+ // No scrolling of its own (Malevich's Table is deliberately
441
+ // unopinionated about pagination) -- the visible window around
442
+ // selectedIndex stays this panel's own job, same as before.
422
443
  const start = Math.max(0, Math.min(selectedIndex - Math.floor(maxVisible / 2), filtered.length - maxVisible));
423
444
  const end = Math.min(start + maxVisible, filtered.length);
424
- for (let i = start; i < end; i++) {
425
- const row = filtered[i]!;
426
- const selected = i === selectedIndex;
427
- const cursor = selected ? theme.fg("accent", "❯") : " ";
428
- const name = selected ? theme.bold(row.name) : row.name;
429
- const ver = theme.fg("dim", `@${row.version}`);
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}`) : "";
434
- lines.push(truncateToWidth(`${cursor} ${name}${ver}${upd}`, width, ""));
435
- }
445
+ table.setRows(
446
+ filtered.slice(start, end).map((row, offset) => {
447
+ const i = start + offset;
448
+ const selected = i === selectedIndex;
449
+ const cursor = selected ? theme.fg("accent", "❯ ") : " ";
450
+ const name = selected ? theme.bold(row.name) : row.name;
451
+ const isUpdating = updatingRowName === row.name;
452
+ const status = isUpdating
453
+ ? theme.fg("accent", `${updatingBar.format(10)} updating…`)
454
+ : row.hasUpdate ? theme.fg("warning", `↑${row.latest}`) : "";
455
+ return { name: `${cursor}${name}`, version: theme.fg("dim", row.version), status };
456
+ }),
457
+ );
458
+ lines.push(...table.render(width));
436
459
  const hasScroll = start > 0 || end < filtered.length;
437
460
  lines.push(theme.fg("dim", ` ${hasScroll ? `${selectedIndex + 1}/${filtered.length} ` : ""}${mode}`));
438
461
  return lines;
@@ -441,11 +464,18 @@ function renderPanel(ctx: ExtensionCommandContext, natives: Natives, initialRows
441
464
 
442
465
  // Real bordered box (rounded corners), not just a horizontal rule --
443
466
  // title carries the live update badge/status instead of a header line.
467
+ // measure must be explicit: Envelope's own default is ASCII-only (raw
468
+ // .length, blind to ANSI escape codes) and every line below is styled
469
+ // through theme.fg/theme.bold -- without this, Envelope pads each line
470
+ // against its own escape-code-inflated "length" instead of its real
471
+ // visible width, so the right border lands at a different column on
472
+ // every line depending on how much styling that particular line has.
444
473
  const envelope = new Envelope({
445
474
  title: panelTitle(),
446
475
  borderStyle: "rounded",
447
476
  style: (s) => theme.fg("border", s),
448
477
  titleStyle: (s) => theme.bold(theme.fg("accent", s)),
478
+ measure,
449
479
  });
450
480
  const body = {
451
481
  invalidate() { header.invalidate(); list.invalidate(); },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/pi-packed",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "description": "Pi package tools, commands, profiles, and TUI for the Packed daemon",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package"],