pi-x-ide 1.11.1 → 1.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/dist/package.json +1 -1
- package/dist/src/pi/ui.d.ts +9 -2
- package/dist/src/pi/ui.js +87 -32
- package/dist/src/pi/ui.js.map +1 -1
- package/package.json +1 -1
- package/src/pi/ui.ts +98 -28
package/dist/package.json
CHANGED
package/dist/src/pi/ui.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@earendil-works/pi-coding-agent" with {
|
|
1
|
+
import type { ExtensionContext, ThemeColor } from "@earendil-works/pi-coding-agent" with {
|
|
2
|
+
"resolution-mode": "import"
|
|
3
|
+
};
|
|
2
4
|
import type { PiIdeRuntime } from "./state";
|
|
5
|
+
interface StatusSegment {
|
|
6
|
+
text: string;
|
|
7
|
+
color: ThemeColor;
|
|
8
|
+
}
|
|
3
9
|
export declare function updateIdeUi(runtime: PiIdeRuntime, ctx?: ExtensionContext | undefined): void;
|
|
4
10
|
export declare function clearIdeUi(runtime: PiIdeRuntime, ctx?: ExtensionContext | undefined): void;
|
|
5
|
-
export declare function
|
|
11
|
+
export declare function buildStatusSegments(runtime: PiIdeRuntime, cwd?: string, spinnerFrame?: string): StatusSegment[];
|
|
6
12
|
export declare function buildWidget(runtime: PiIdeRuntime, cwd?: string): string[] | undefined;
|
|
13
|
+
export {};
|
package/dist/src/pi/ui.js
CHANGED
|
@@ -2,57 +2,112 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.updateIdeUi = updateIdeUi;
|
|
4
4
|
exports.clearIdeUi = clearIdeUi;
|
|
5
|
-
exports.
|
|
5
|
+
exports.buildStatusSegments = buildStatusSegments;
|
|
6
6
|
exports.buildWidget = buildWidget;
|
|
7
7
|
const format_1 = require("../shared/format");
|
|
8
8
|
const paths_1 = require("../shared/paths");
|
|
9
|
+
const IDE_ICON = "⧉";
|
|
10
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
11
|
+
const SPINNER_INTERVAL_MS = 90;
|
|
9
12
|
function updateIdeUi(runtime, ctx = runtime.ctx) {
|
|
10
13
|
if (!ctx?.hasUI)
|
|
11
14
|
return;
|
|
12
|
-
ctx.ui.setWidget("pi-x-ide", (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
ctx.ui.setWidget("pi-x-ide", (tui, theme) => {
|
|
16
|
+
let frame = 0;
|
|
17
|
+
let timer;
|
|
18
|
+
// Drive the connecting spinner: tick a frame and request a redraw only while connecting.
|
|
19
|
+
const animate = (active) => {
|
|
20
|
+
if (active && !timer) {
|
|
21
|
+
timer = setInterval(() => {
|
|
22
|
+
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
23
|
+
tui.requestRender();
|
|
24
|
+
}, SPINNER_INTERVAL_MS);
|
|
25
|
+
timer.unref?.();
|
|
26
|
+
}
|
|
27
|
+
else if (!active && timer) {
|
|
28
|
+
clearInterval(timer);
|
|
29
|
+
timer = undefined;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
render(width) {
|
|
34
|
+
const connecting = runtime.enabled && runtime.connectionStatus === "connecting";
|
|
35
|
+
animate(connecting);
|
|
36
|
+
const segments = buildStatusSegments(runtime, ctx.cwd, SPINNER_FRAMES[frame]);
|
|
37
|
+
if (segments.length === 0 || width <= 0)
|
|
38
|
+
return [];
|
|
39
|
+
const visible = truncateSegments(segments, width);
|
|
40
|
+
const plainLength = visible.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
41
|
+
const pad = " ".repeat(Math.max(0, width - plainLength));
|
|
42
|
+
return [pad + visible.map((segment) => theme.fg(segment.color, segment.text)).join("")];
|
|
43
|
+
},
|
|
44
|
+
invalidate() { },
|
|
45
|
+
dispose() {
|
|
46
|
+
animate(false);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}, { placement: "aboveEditor" });
|
|
22
50
|
}
|
|
23
51
|
function clearIdeUi(runtime, ctx = runtime.ctx) {
|
|
24
52
|
if (!ctx?.hasUI)
|
|
25
53
|
return;
|
|
26
54
|
ctx.ui.setWidget("pi-x-ide", undefined);
|
|
27
55
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
return
|
|
56
|
+
// Truncates colored segments to the given width, appending an ellipsis when content overflows.
|
|
57
|
+
function truncateSegments(segments, width) {
|
|
58
|
+
const total = segments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
59
|
+
if (total <= width)
|
|
60
|
+
return segments;
|
|
61
|
+
const fallbackColor = segments[0]?.color ?? "dim";
|
|
33
62
|
if (width <= 3)
|
|
34
|
-
return ".".repeat(width);
|
|
35
|
-
|
|
63
|
+
return [{ text: ".".repeat(width), color: fallbackColor }];
|
|
64
|
+
const limit = width - 3;
|
|
65
|
+
const visible = [];
|
|
66
|
+
let used = 0;
|
|
67
|
+
for (const segment of segments) {
|
|
68
|
+
if (used >= limit)
|
|
69
|
+
break;
|
|
70
|
+
const room = limit - used;
|
|
71
|
+
if (segment.text.length <= room) {
|
|
72
|
+
visible.push(segment);
|
|
73
|
+
used += segment.text.length;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
visible.push({ text: segment.text.slice(0, room), color: segment.color });
|
|
77
|
+
used = limit;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
visible.push({ text: "...", color: visible[visible.length - 1]?.color ?? fallbackColor });
|
|
81
|
+
return visible;
|
|
36
82
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
83
|
+
function buildStatusSegments(runtime, cwd, spinnerFrame = SPINNER_FRAMES[0]) {
|
|
84
|
+
if (!runtime.enabled)
|
|
85
|
+
return [];
|
|
86
|
+
switch (runtime.connectionStatus) {
|
|
87
|
+
case "connecting":
|
|
88
|
+
return [{ text: `${IDE_ICON} ${spinnerFrame}`, color: "dim" }];
|
|
89
|
+
case "error":
|
|
90
|
+
return [
|
|
91
|
+
{ text: `${IDE_ICON} `, color: "dim" },
|
|
92
|
+
{ text: "✕", color: "error" },
|
|
93
|
+
];
|
|
94
|
+
case "connected":
|
|
95
|
+
return buildConnectedSegments(runtime, cwd);
|
|
96
|
+
default:
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
39
99
|
}
|
|
40
|
-
function
|
|
41
|
-
if (!runtime.enabled || runtime.connectionStatus === "disabled")
|
|
42
|
-
return "Pi x IDE: off";
|
|
43
|
-
if (runtime.connectionStatus === "connecting")
|
|
44
|
-
return "Pi x IDE: connecting";
|
|
45
|
-
if (runtime.connectionStatus === "error")
|
|
46
|
-
return `Pi x IDE: error${runtime.connectionMessage ? ` ${runtime.connectionMessage}` : ""}`;
|
|
47
|
-
if (runtime.connectionStatus !== "connected")
|
|
48
|
-
return "Pi x IDE: disconnected";
|
|
49
|
-
const ide = runtime.connectedServer?.ide ?? runtime.currentCandidate?.lock.ide ?? "ide";
|
|
100
|
+
function buildConnectedSegments(runtime, cwd) {
|
|
50
101
|
const selection = runtime.latestSelection;
|
|
51
102
|
if (!selection)
|
|
52
|
-
return `${
|
|
103
|
+
return [{ text: `${IDE_ICON} ✓`, color: "dim" }];
|
|
53
104
|
const rel = (0, paths_1.toRelativeDisplayPath)(selection.filePath, selection.workspaceFolder, cwd);
|
|
54
105
|
const range = (0, format_1.describeRanges)(selection.ranges);
|
|
55
|
-
|
|
106
|
+
// Pending selections are still queued for the next prompt (⇡, success); sent ones are attached (✓, dim).
|
|
107
|
+
const pending = runtime.attachState === "pending";
|
|
108
|
+
const marker = pending ? "⇡" : "✓";
|
|
109
|
+
const color = pending ? "success" : "dim";
|
|
110
|
+
return [{ text: `${IDE_ICON} ${marker} ${rel}${range === "open file" ? "" : range}`, color }];
|
|
56
111
|
}
|
|
57
112
|
function buildWidget(runtime, cwd) {
|
|
58
113
|
if (!runtime.enabled || runtime.connectionStatus === "disabled")
|
package/dist/src/pi/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/pi/ui.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../src/pi/ui.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/pi/ui.ts
CHANGED
|
@@ -1,23 +1,59 @@
|
|
|
1
|
-
import type { ExtensionContext } from "@earendil-works/pi-coding-agent" with {
|
|
1
|
+
import type { ExtensionContext, ThemeColor } from "@earendil-works/pi-coding-agent" with {
|
|
2
|
+
"resolution-mode": "import",
|
|
3
|
+
};
|
|
2
4
|
import { describeRanges } from "../shared/format";
|
|
3
5
|
import { toRelativeDisplayPath } from "../shared/paths";
|
|
4
6
|
import type { PiIdeRuntime } from "./state";
|
|
5
7
|
|
|
8
|
+
const IDE_ICON = "⧉";
|
|
9
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
10
|
+
const SPINNER_INTERVAL_MS = 90;
|
|
11
|
+
|
|
12
|
+
interface StatusSegment {
|
|
13
|
+
text: string;
|
|
14
|
+
color: ThemeColor;
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
export function updateIdeUi(runtime: PiIdeRuntime, ctx: ExtensionContext | undefined = runtime.ctx): void {
|
|
7
18
|
if (!ctx?.hasUI) return;
|
|
8
19
|
|
|
9
20
|
ctx.ui.setWidget(
|
|
10
21
|
"pi-x-ide",
|
|
11
|
-
(
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
(tui, theme) => {
|
|
23
|
+
let frame = 0;
|
|
24
|
+
let timer: ReturnType<typeof setInterval> | undefined;
|
|
25
|
+
|
|
26
|
+
// Drive the connecting spinner: tick a frame and request a redraw only while connecting.
|
|
27
|
+
const animate = (active: boolean): void => {
|
|
28
|
+
if (active && !timer) {
|
|
29
|
+
timer = setInterval(() => {
|
|
30
|
+
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
31
|
+
tui.requestRender();
|
|
32
|
+
}, SPINNER_INTERVAL_MS);
|
|
33
|
+
timer.unref?.();
|
|
34
|
+
} else if (!active && timer) {
|
|
35
|
+
clearInterval(timer);
|
|
36
|
+
timer = undefined;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
render(width: number): string[] {
|
|
42
|
+
const connecting = runtime.enabled && runtime.connectionStatus === "connecting";
|
|
43
|
+
animate(connecting);
|
|
44
|
+
const segments = buildStatusSegments(runtime, ctx.cwd, SPINNER_FRAMES[frame]);
|
|
45
|
+
if (segments.length === 0 || width <= 0) return [];
|
|
46
|
+
const visible = truncateSegments(segments, width);
|
|
47
|
+
const plainLength = visible.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
48
|
+
const pad = " ".repeat(Math.max(0, width - plainLength));
|
|
49
|
+
return [pad + visible.map((segment) => theme.fg(segment.color, segment.text)).join("")];
|
|
50
|
+
},
|
|
51
|
+
invalidate() {},
|
|
52
|
+
dispose() {
|
|
53
|
+
animate(false);
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
},
|
|
21
57
|
{ placement: "aboveEditor" },
|
|
22
58
|
);
|
|
23
59
|
}
|
|
@@ -27,30 +63,64 @@ export function clearIdeUi(runtime: PiIdeRuntime, ctx: ExtensionContext | undefi
|
|
|
27
63
|
ctx.ui.setWidget("pi-x-ide", undefined);
|
|
28
64
|
}
|
|
29
65
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
}
|
|
66
|
+
// Truncates colored segments to the given width, appending an ellipsis when content overflows.
|
|
67
|
+
function truncateSegments(segments: StatusSegment[], width: number): StatusSegment[] {
|
|
68
|
+
const total = segments.reduce((sum, segment) => sum + segment.text.length, 0);
|
|
69
|
+
if (total <= width) return segments;
|
|
70
|
+
const fallbackColor = segments[0]?.color ?? "dim";
|
|
71
|
+
if (width <= 3) return [{ text: ".".repeat(width), color: fallbackColor }];
|
|
36
72
|
|
|
37
|
-
|
|
38
|
-
|
|
73
|
+
const limit = width - 3;
|
|
74
|
+
const visible: StatusSegment[] = [];
|
|
75
|
+
let used = 0;
|
|
76
|
+
for (const segment of segments) {
|
|
77
|
+
if (used >= limit) break;
|
|
78
|
+
const room = limit - used;
|
|
79
|
+
if (segment.text.length <= room) {
|
|
80
|
+
visible.push(segment);
|
|
81
|
+
used += segment.text.length;
|
|
82
|
+
} else {
|
|
83
|
+
visible.push({ text: segment.text.slice(0, room), color: segment.color });
|
|
84
|
+
used = limit;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
visible.push({ text: "...", color: visible[visible.length - 1]?.color ?? fallbackColor });
|
|
88
|
+
return visible;
|
|
39
89
|
}
|
|
40
90
|
|
|
41
|
-
export function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (runtime.
|
|
91
|
+
export function buildStatusSegments(
|
|
92
|
+
runtime: PiIdeRuntime,
|
|
93
|
+
cwd?: string,
|
|
94
|
+
spinnerFrame = SPINNER_FRAMES[0],
|
|
95
|
+
): StatusSegment[] {
|
|
96
|
+
if (!runtime.enabled) return [];
|
|
47
97
|
|
|
48
|
-
|
|
98
|
+
switch (runtime.connectionStatus) {
|
|
99
|
+
case "connecting":
|
|
100
|
+
return [{ text: `${IDE_ICON} ${spinnerFrame}`, color: "dim" }];
|
|
101
|
+
case "error":
|
|
102
|
+
return [
|
|
103
|
+
{ text: `${IDE_ICON} `, color: "dim" },
|
|
104
|
+
{ text: "✕", color: "error" },
|
|
105
|
+
];
|
|
106
|
+
case "connected":
|
|
107
|
+
return buildConnectedSegments(runtime, cwd);
|
|
108
|
+
default:
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function buildConnectedSegments(runtime: PiIdeRuntime, cwd?: string): StatusSegment[] {
|
|
49
114
|
const selection = runtime.latestSelection;
|
|
50
|
-
if (!selection) return `${
|
|
115
|
+
if (!selection) return [{ text: `${IDE_ICON} ✓`, color: "dim" }];
|
|
116
|
+
|
|
51
117
|
const rel = toRelativeDisplayPath(selection.filePath, selection.workspaceFolder, cwd);
|
|
52
118
|
const range = describeRanges(selection.ranges);
|
|
53
|
-
|
|
119
|
+
// Pending selections are still queued for the next prompt (⇡, success); sent ones are attached (✓, dim).
|
|
120
|
+
const pending = runtime.attachState === "pending";
|
|
121
|
+
const marker = pending ? "⇡" : "✓";
|
|
122
|
+
const color: ThemeColor = pending ? "success" : "dim";
|
|
123
|
+
return [{ text: `${IDE_ICON} ${marker} ${rel}${range === "open file" ? "" : range}`, color }];
|
|
54
124
|
}
|
|
55
125
|
|
|
56
126
|
export function buildWidget(runtime: PiIdeRuntime, cwd?: string): string[] | undefined {
|