@danypops/pi-packed 0.20.0 → 0.20.2
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
CHANGED
|
@@ -10,7 +10,7 @@ The extension connects to Packed's authenticated user daemon and starts the pack
|
|
|
10
10
|
|
|
11
11
|
## Commands
|
|
12
12
|
|
|
13
|
-
- `/packed` -- one floating overlay, four tabs (Packages/Find/Config/Settings), always shown in a persistent tab bar at the top: the focused tab
|
|
13
|
+
- `/packed` -- one floating overlay, four tabs (Packages/Find/Config/Settings), always shown in a persistent tab bar at the top: the focused tab reverses the theme's text color for high contrast in both dark and light modes, unfocused tabs show only a foreground color with their own first letter highlighted as a jump mnemonic (the focused tab needs no mnemonic -- there's nothing to jump to when you're already there). `Tab`/`Shift-Tab` (or `←`/`→`) sweep between them -- recognized under legacy CSI, xterm's modifyOtherKeys, and the Kitty keyboard protocol alike, not just one hardcoded encoding; from any tab other than Packages, `p`/`f`/`c`/`s` also jump straight to the matching one (never stolen while that tab's own filter/query box is capturing text). `Esc` returns to Packages from anywhere else, or closes the panel from Packages itself. Every approval and reload confirmation, on any tab, renders inline on this same panel (`y`/`n` keys), never a separate popup or native dialog. A standing test (`keymap.test.ts`, using Malevich's tree-style `findMnemonicConflicts`) verifies no two actions genuinely reachable at once share a key.
|
|
14
14
|
- **Packages** (the default tab) -- 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):
|
|
15
15
|
- `u` / `U` -- update the selected package / update every outdated package, one combined confirmation and one reload. `U` shows a spinner inline next to the package currently updating, settling into a real ✓ or ✗ plus a short tail of that update's own captured output once it finishes -- 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`.
|
|
16
16
|
- `x` -- remove the selected package.
|
|
@@ -31,19 +31,16 @@ export function dialogTheme(theme: Theme): DialogTheme {
|
|
|
31
31
|
* overlay, replacing what used to be four separately-opened screens. */
|
|
32
32
|
export function tabBarTheme(theme: Theme): TabBarTheme {
|
|
33
33
|
return {
|
|
34
|
-
// Real bug, reported live: theme.inverse() swaps whatever
|
|
35
|
-
// foreground/background happen to be ambient at that point in the
|
|
36
|
-
// ANSI stream, not a specific color -- and Envelope wraps a whole
|
|
37
|
-
// content line (including this tab bar) in one continuous border-color
|
|
38
|
-
// span with no reset until the line's end, so which tab is active
|
|
39
|
-
// changed what "ambient" meant (blue when Packages was first on the
|
|
40
|
-
// line and nothing had reset yet, white once an earlier inactive tab's
|
|
41
|
-
// own fg reset already cleared it). An explicit theme.bg() always
|
|
42
|
-
// overwrites rather than swaps, so it's immune to that -- matches
|
|
43
|
-
// Pi's own core session-selector.js, which highlights its selected
|
|
44
|
-
// line the same way.
|
|
45
34
|
tab: (s) => theme.fg("dim", s),
|
|
46
|
-
|
|
35
|
+
// Set the semantic text color before reversing it. Reverse video then
|
|
36
|
+
// makes dark themes use their bright text as the highlight background
|
|
37
|
+
// and light themes use their dark text, without inheriting Envelope's
|
|
38
|
+
// ambient border foreground. Every tab position gets the same default
|
|
39
|
+
// terminal background as its displayed foreground.
|
|
40
|
+
activeTab: (s) => {
|
|
41
|
+
const text = theme.fg("text", s);
|
|
42
|
+
return typeof theme.inverse === "function" ? theme.inverse(text) : text;
|
|
43
|
+
},
|
|
47
44
|
// Every tab's first letter is a jump mnemonic (p/f/c/s) -- a distinct
|
|
48
45
|
// warning-colored bold, so it reads as "press this letter" on both the
|
|
49
46
|
// active and inactive tabs alike, not just whichever style tab/activeTab
|
|
@@ -51,3 +48,11 @@ export function tabBarTheme(theme: Theme): TabBarTheme {
|
|
|
51
48
|
mnemonic: (s) => theme.bold(theme.fg("warning", s)),
|
|
52
49
|
};
|
|
53
50
|
}
|
|
51
|
+
|
|
52
|
+
/** Envelope calls style with assembled lines in released Malevich versions.
|
|
53
|
+
* Color only frame glyphs so styled content cannot reset the closing border
|
|
54
|
+
* or inherit the border foreground. This remains compatible with Malevich's
|
|
55
|
+
* segment-styled Envelope implementation. */
|
|
56
|
+
export function panelFrameStyle(theme: Theme): (text: string) => string {
|
|
57
|
+
return (text) => text.replace(/[╭─╮│╰╯]+/gu, (glyphs) => theme.fg("border", glyphs));
|
|
58
|
+
}
|
package/extension/src/tui.ts
CHANGED
|
@@ -59,7 +59,7 @@ import {
|
|
|
59
59
|
type TextMeasure,
|
|
60
60
|
} from "malevich-tui-components";
|
|
61
61
|
import { confirmReload } from "./approval/reload.js";
|
|
62
|
-
import { dialogTheme, menuTheme, tabBarTheme } from "./menu-theme.js";
|
|
62
|
+
import { dialogTheme, menuTheme, panelFrameStyle, tabBarTheme } from "./menu-theme.js";
|
|
63
63
|
import type { Row, ViewMode } from "./model.js";
|
|
64
64
|
import { filterRows, mergeRows, nextMode, visibleRows } from "./model.js";
|
|
65
65
|
import type { Natives, PackageResources } from "./packed.js";
|
|
@@ -849,7 +849,7 @@ function renderUnifiedPanel(
|
|
|
849
849
|
const envelope = new Envelope({
|
|
850
850
|
title: "packed",
|
|
851
851
|
borderStyle: "rounded",
|
|
852
|
-
style: (
|
|
852
|
+
style: panelFrameStyle(theme),
|
|
853
853
|
titleStyle: (s) => theme.bold(theme.fg("accent", s)),
|
|
854
854
|
measure,
|
|
855
855
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danypops/pi-packed",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.2",
|
|
4
4
|
"description": "Pi package lifecycle, validation, daemon, tools, profiles, and TUI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -31,14 +31,15 @@
|
|
|
31
31
|
"@danypops/packed": "^0.6.0",
|
|
32
32
|
"@danypops/pi-extension-harness": "^0.2.0",
|
|
33
33
|
"@danypops/vehicle-client": "^0.5.1",
|
|
34
|
-
"@danypops/vehicle-core": "^0.
|
|
35
|
-
"@danypops/vehicle-server": "^0.
|
|
34
|
+
"@danypops/vehicle-core": "^0.12.3",
|
|
35
|
+
"@danypops/vehicle-server": "^0.17.1",
|
|
36
36
|
"jiti": "2.6.1",
|
|
37
|
-
"malevich-tui-components": "^0.
|
|
37
|
+
"malevich-tui-components": "^0.20.4",
|
|
38
38
|
"publint": "0.3.22",
|
|
39
39
|
"semver": "^7.8.5"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
+
"@danypops/pi-tui-harness": "^0.0.1",
|
|
42
43
|
"@danypops/vehicle-client-pi": "^0.16.0",
|
|
43
44
|
"@types/semver": "^7.7.1"
|
|
44
45
|
},
|
|
@@ -251,7 +251,11 @@ export function createApp(deps: Deps): { fetch: (req: Request) => Promise<Respon
|
|
|
251
251
|
// Additive, real Vehicle protocol surface for this daemon's full operation set -- served
|
|
252
252
|
// at /vehicle/* alongside (not replacing) /api/v1/ops below. Every operation delegates to
|
|
253
253
|
// the exact same executeOperation() this route's own /api/v1/ops handler calls.
|
|
254
|
-
const vehicleRegistry = new VehicleRegistry({
|
|
254
|
+
const vehicleRegistry = new VehicleRegistry({
|
|
255
|
+
name: "packed",
|
|
256
|
+
packageJsonUrl: new URL("../../../package.json", import.meta.url),
|
|
257
|
+
description: "Pi package lifecycle daemon",
|
|
258
|
+
});
|
|
255
259
|
registerPackedVehicleOperations(vehicleRegistry, executeOperation);
|
|
256
260
|
const vehicleApp = createVehicleHttpApp({ registry: vehicleRegistry, token: deps.token });
|
|
257
261
|
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
import type { VehicleEffect, VehicleIdempotency } from "@danypops/vehicle-core";
|
|
40
|
-
import { bindVehicleOperation, defineVehicleOperation, passthroughVehicleSchema
|
|
40
|
+
import { bindVehicleOperation, defineErrorMapping, defineVehicleOperation, passthroughVehicleSchema } from "@danypops/vehicle-core";
|
|
41
41
|
import type { VehicleRegistry } from "@danypops/vehicle-server";
|
|
42
42
|
import { OPERATION_NAMES, type OperationInputs, type OperationName, type OperationOutputs } from "./service.ts";
|
|
43
43
|
|
|
@@ -50,28 +50,12 @@ function hasStatus(error: unknown): error is StatusCarryingError {
|
|
|
50
50
|
return error instanceof Error && typeof (error as { status?: unknown }).status === "number";
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const category =
|
|
60
|
-
error.status === 403
|
|
61
|
-
? "authorization"
|
|
62
|
-
: error.status === 404
|
|
63
|
-
? "not_found"
|
|
64
|
-
: error.status === 400
|
|
65
|
-
? "validation"
|
|
66
|
-
: error.status >= 500
|
|
67
|
-
? "unavailable"
|
|
68
|
-
: "validation";
|
|
69
|
-
throw new VehicleError("operation-rejected", error.message, { category, cause: error });
|
|
70
|
-
}
|
|
71
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
72
|
-
throw new VehicleError("operation-rejected", message, { category: "validation", cause: error });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
53
|
+
const withPackedErrorParity = defineErrorMapping([
|
|
54
|
+
{ matches: (error) => hasStatus(error) && error.status === 403, category: "authorization" },
|
|
55
|
+
{ matches: (error) => hasStatus(error) && error.status === 404, category: "not_found" },
|
|
56
|
+
{ matches: (error) => hasStatus(error) && error.status === 400, category: "validation" },
|
|
57
|
+
{ matches: (error) => hasStatus(error) && error.status >= 500, category: "unavailable" },
|
|
58
|
+
]);
|
|
75
59
|
|
|
76
60
|
const OWNER = "packed";
|
|
77
61
|
const LIMITS = { defaultTimeoutMs: 30_000, maxTimeoutMs: 120_000, maxRequestBytes: 65_536, maxResponseBytes: 4_194_304 };
|