@saifymatteo/pi-x-ide 0.0.0-stage → 1.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/CHANGELOG.md +328 -0
- package/CONTRIBUTING.md +119 -0
- package/CONTRIBUTING.zh-CN.md +106 -0
- package/LICENSE +201 -0
- package/README.md +33 -2
- package/README.zh-CN.md +34 -0
- package/RELEASE.md +213 -0
- package/config.example.json +9 -0
- package/dist/src/pi/chunks/chunk-4RHJAL4S.js +200 -0
- package/dist/src/pi/chunks/chunk-UUXVCTOS.js +819 -0
- package/dist/src/pi/chunks/config-ui-LO3DVQFC.js +8073 -0
- package/dist/src/pi/chunks/runtime-services-BVQBAAWT.js +19569 -0
- package/dist/src/pi/commands.js +126 -0
- package/dist/src/pi/config-ui.js +127 -0
- package/dist/src/pi/connection.js +237 -0
- package/dist/src/pi/context.js +30 -0
- package/dist/src/pi/diagnostic-renderer.js +326 -0
- package/dist/src/pi/diagnostic-types.js +1 -0
- package/dist/src/pi/diagnostics.js +104 -0
- package/dist/src/pi/discovery.js +135 -0
- package/dist/src/pi/effect-boundary.js +5 -0
- package/dist/src/pi/ide-host.js +68 -0
- package/dist/src/pi/index.js +636 -0
- package/dist/src/pi/install.js +308 -0
- package/dist/src/pi/reconnect.js +57 -0
- package/dist/src/pi/runtime-loader.js +38 -0
- package/dist/src/pi/runtime-services.js +356 -0
- package/dist/src/pi/safety.js +61 -0
- package/dist/src/pi/selection.js +18 -0
- package/dist/src/pi/state.js +13 -0
- package/dist/src/pi/ui.js +304 -0
- package/dist/src/pi/zed.js +409 -0
- package/dist/src/shared/config-options.js +45 -0
- package/dist/src/shared/config.js +183 -0
- package/dist/src/shared/display-width.js +99 -0
- package/dist/src/shared/effect-errors.js +36 -0
- package/dist/src/shared/effect-runtime.js +36 -0
- package/dist/src/shared/effect-schema.js +120 -0
- package/dist/src/shared/errors.js +48 -0
- package/dist/src/shared/format.js +92 -0
- package/dist/src/shared/ide-server.js +167 -0
- package/dist/src/shared/jsonrpc-guard.js +12 -0
- package/dist/src/shared/lock-file-heartbeat.js +58 -0
- package/dist/src/shared/lock-file.js +50 -0
- package/dist/src/shared/paths.js +79 -0
- package/dist/src/shared/platform.js +57 -0
- package/dist/src/shared/protocol.js +3 -0
- package/dist/src/shared/schema.js +37 -0
- package/dist/src/shared/version.js +1 -0
- package/dist/src/shared/ws.js +8 -0
- package/docs/explanation/architecture.md +123 -0
- package/docs/explanation/discovery.md +113 -0
- package/docs/explanation/effect.md +127 -0
- package/docs/how-to/install-jetbrains.md +58 -0
- package/docs/how-to/install-neovim.md +138 -0
- package/docs/how-to/install-vscode.md +75 -0
- package/docs/how-to/install-zed.md +70 -0
- package/docs/how-to/troubleshoot-connection.md +103 -0
- package/docs/how-to/use-wsl2.md +90 -0
- package/docs/reference/commands.md +59 -0
- package/docs/reference/configuration.md +101 -0
- package/docs/reference/feature-parity.md +26 -0
- package/docs/reference/protocol.md +71 -0
- package/docs/specs/ide-protocol.md +215 -0
- package/docs/tutorials/get-started.md +133 -0
- package/docs/zh-CN/explanation/architecture.md +93 -0
- package/docs/zh-CN/explanation/discovery.md +71 -0
- package/docs/zh-CN/explanation/effect.md +90 -0
- package/docs/zh-CN/how-to/install-jetbrains.md +49 -0
- package/docs/zh-CN/how-to/install-neovim.md +126 -0
- package/docs/zh-CN/how-to/install-vscode.md +67 -0
- package/docs/zh-CN/how-to/install-zed.md +56 -0
- package/docs/zh-CN/how-to/troubleshoot-connection.md +85 -0
- package/docs/zh-CN/how-to/use-wsl2.md +75 -0
- package/docs/zh-CN/reference/commands.md +54 -0
- package/docs/zh-CN/reference/configuration.md +94 -0
- package/docs/zh-CN/reference/feature-parity.md +21 -0
- package/docs/zh-CN/reference/protocol.md +54 -0
- package/docs/zh-CN/tutorials/get-started.md +101 -0
- package/package.json +86 -4
- package/schemas/config.json +77 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { isStaleContextError, logExtensionError } from "../shared/errors.js";
|
|
2
|
+
import { describeRanges } from "../shared/format.js";
|
|
3
|
+
import { basename } from "node:path";
|
|
4
|
+
import { EXT_CONFIG_NAME, readPiConfigStatusDisplay } from "../shared/config.js";
|
|
5
|
+
import { toRelativeDisplayPath } from "../shared/paths.js";
|
|
6
|
+
import { truncateToWidth, visibleWidth } from "../shared/display-width.js";
|
|
7
|
+
const IDE_ICON = "⧉";
|
|
8
|
+
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
9
|
+
const SPINNER_INTERVAL_MS = 90;
|
|
10
|
+
const PI_THEME_KEYS = [
|
|
11
|
+
Symbol.for("@earendil-works/pi-coding-agent:theme"),
|
|
12
|
+
Symbol.for("@mariozechner/pi-coding-agent:theme"),
|
|
13
|
+
];
|
|
14
|
+
const statuslineSpinner = { frame: 0 };
|
|
15
|
+
function getActiveUiContext(ctx) {
|
|
16
|
+
if (!ctx)
|
|
17
|
+
return undefined;
|
|
18
|
+
try {
|
|
19
|
+
if (!ctx.hasUI)
|
|
20
|
+
return undefined;
|
|
21
|
+
return { cwd: ctx.cwd, ui: ctx.ui };
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
if (!isStaleContextError(error))
|
|
25
|
+
logExtensionError("read active Pi UI context", error);
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function runReportingUiFailure(scope, action) {
|
|
30
|
+
try {
|
|
31
|
+
action();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
logExtensionError(scope, error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function updateIdeUi(runtime, ctx = runtime.ctx, statusDisplay) {
|
|
38
|
+
const uiContext = getActiveUiContext(ctx);
|
|
39
|
+
if (!uiContext)
|
|
40
|
+
return;
|
|
41
|
+
const display = statusDisplay ??
|
|
42
|
+
readPiConfigStatusDisplay({
|
|
43
|
+
projectDir: ctx?.cwd ?? runtime.cwd,
|
|
44
|
+
});
|
|
45
|
+
if (display === "statusline") {
|
|
46
|
+
clearWidget(uiContext);
|
|
47
|
+
updateStatusline(runtime, uiContext);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
stopStatuslineSpinner();
|
|
51
|
+
clearStatusline(uiContext);
|
|
52
|
+
updateWidget(runtime, uiContext);
|
|
53
|
+
}
|
|
54
|
+
export function clearIdeUi(runtime, ctx = runtime.ctx) {
|
|
55
|
+
const uiContext = getActiveUiContext(ctx);
|
|
56
|
+
stopStatuslineSpinner();
|
|
57
|
+
if (!uiContext)
|
|
58
|
+
return;
|
|
59
|
+
clearWidget(uiContext);
|
|
60
|
+
clearStatusline(uiContext);
|
|
61
|
+
}
|
|
62
|
+
function updateWidget(runtime, uiContext) {
|
|
63
|
+
runReportingUiFailure("update IDE UI widget", () => uiContext.ui.setWidget(EXT_CONFIG_NAME, (tui, theme) => {
|
|
64
|
+
let frame = 0;
|
|
65
|
+
let timer;
|
|
66
|
+
const animate = (active) => {
|
|
67
|
+
if (active && !timer) {
|
|
68
|
+
timer = setInterval(() => {
|
|
69
|
+
try {
|
|
70
|
+
frame = (frame + 1) % SPINNER_FRAMES.length;
|
|
71
|
+
tui.requestRender();
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
logExtensionError("animate IDE UI widget", error);
|
|
75
|
+
if (timer) {
|
|
76
|
+
clearInterval(timer);
|
|
77
|
+
timer = undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}, SPINNER_INTERVAL_MS);
|
|
81
|
+
timer.unref?.();
|
|
82
|
+
}
|
|
83
|
+
else if (!active && timer) {
|
|
84
|
+
clearInterval(timer);
|
|
85
|
+
timer = undefined;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
render(width) {
|
|
90
|
+
try {
|
|
91
|
+
const connecting = runtime.enabled && runtime.connectionStatus === "connecting";
|
|
92
|
+
animate(connecting);
|
|
93
|
+
const segments = buildStatusSegments(runtime, SPINNER_FRAMES[frame]);
|
|
94
|
+
if (segments.length === 0 || width <= 0)
|
|
95
|
+
return [];
|
|
96
|
+
const visible = truncateSegments(segments, width);
|
|
97
|
+
const plainWidth = visible.reduce((sum, segment) => sum + visibleWidth(segment.text), 0);
|
|
98
|
+
const pad = " ".repeat(Math.max(0, width - plainWidth));
|
|
99
|
+
return [pad + visible.map((segment) => theme.fg(segment.color, segment.text)).join("")];
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
logExtensionError("render IDE UI widget", error);
|
|
103
|
+
animate(false);
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
invalidate() { },
|
|
108
|
+
dispose() {
|
|
109
|
+
animate(false);
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}, { placement: "aboveEditor" }));
|
|
113
|
+
}
|
|
114
|
+
function updateStatusline(runtime, uiContext) {
|
|
115
|
+
statuslineSpinner.runtime = runtime;
|
|
116
|
+
statuslineSpinner.ui = uiContext;
|
|
117
|
+
const connecting = runtime.enabled && runtime.connectionStatus === "connecting";
|
|
118
|
+
if (connecting) {
|
|
119
|
+
startStatuslineSpinner();
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
stopStatuslineSpinner();
|
|
123
|
+
}
|
|
124
|
+
applyStatusline(runtime, uiContext, SPINNER_FRAMES[statuslineSpinner.frame]);
|
|
125
|
+
}
|
|
126
|
+
function applyStatusline(runtime, uiContext, spinnerFrame) {
|
|
127
|
+
runReportingUiFailure("update IDE statusline", () => {
|
|
128
|
+
const text = buildStatusText(runtime, spinnerFrame, getThemeColorizer());
|
|
129
|
+
uiContext.ui.setStatus(EXT_CONFIG_NAME, text);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function startStatuslineSpinner() {
|
|
133
|
+
if (statuslineSpinner.timer)
|
|
134
|
+
return;
|
|
135
|
+
statuslineSpinner.timer = setInterval(() => {
|
|
136
|
+
try {
|
|
137
|
+
statuslineSpinner.frame = (statuslineSpinner.frame + 1) % SPINNER_FRAMES.length;
|
|
138
|
+
const runtime = statuslineSpinner.runtime;
|
|
139
|
+
const ui = statuslineSpinner.ui;
|
|
140
|
+
if (!runtime || !ui)
|
|
141
|
+
return;
|
|
142
|
+
if (!(runtime.enabled && runtime.connectionStatus === "connecting")) {
|
|
143
|
+
stopStatuslineSpinner();
|
|
144
|
+
applyStatusline(runtime, ui, SPINNER_FRAMES[statuslineSpinner.frame]);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
applyStatusline(runtime, ui, SPINNER_FRAMES[statuslineSpinner.frame]);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
logExtensionError("animate IDE statusline", error);
|
|
151
|
+
stopStatuslineSpinner();
|
|
152
|
+
}
|
|
153
|
+
}, SPINNER_INTERVAL_MS);
|
|
154
|
+
statuslineSpinner.timer.unref?.();
|
|
155
|
+
}
|
|
156
|
+
function stopStatuslineSpinner() {
|
|
157
|
+
if (!statuslineSpinner.timer)
|
|
158
|
+
return;
|
|
159
|
+
clearInterval(statuslineSpinner.timer);
|
|
160
|
+
statuslineSpinner.timer = undefined;
|
|
161
|
+
}
|
|
162
|
+
function clearWidget(uiContext) {
|
|
163
|
+
runReportingUiFailure("clear IDE UI widget", () => uiContext.ui.setWidget(EXT_CONFIG_NAME, undefined));
|
|
164
|
+
}
|
|
165
|
+
function clearStatusline(uiContext) {
|
|
166
|
+
runReportingUiFailure("clear IDE statusline", () => uiContext.ui.setStatus(EXT_CONFIG_NAME, undefined));
|
|
167
|
+
}
|
|
168
|
+
function truncateSegments(segments, width) {
|
|
169
|
+
const total = segments.reduce((sum, segment) => sum + visibleWidth(segment.text), 0);
|
|
170
|
+
if (total <= width)
|
|
171
|
+
return segments;
|
|
172
|
+
const fallbackColor = segments[0]?.color ?? "dim";
|
|
173
|
+
if (width <= 3)
|
|
174
|
+
return [{ text: ".".repeat(width), color: fallbackColor }];
|
|
175
|
+
const shrinkIndex = segments.findIndex((segment) => segment.shrink);
|
|
176
|
+
if (shrinkIndex >= 0) {
|
|
177
|
+
const shrinkSegment = segments[shrinkIndex];
|
|
178
|
+
const shrinkWidth = visibleWidth(shrinkSegment.text);
|
|
179
|
+
const reservedWidth = total - shrinkWidth;
|
|
180
|
+
const room = width - reservedWidth;
|
|
181
|
+
if (room > 0) {
|
|
182
|
+
const preserved = segments.map((segment, index) => index === shrinkIndex ? { ...segment, text: truncateToWidth(segment.text, room) } : segment);
|
|
183
|
+
const preservedWidth = preserved.reduce((sum, segment) => sum + visibleWidth(segment.text), 0);
|
|
184
|
+
if (preservedWidth <= width)
|
|
185
|
+
return preserved;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const ellipsis = "...";
|
|
189
|
+
const limit = width - visibleWidth(ellipsis);
|
|
190
|
+
const visible = [];
|
|
191
|
+
let used = 0;
|
|
192
|
+
for (const segment of segments) {
|
|
193
|
+
if (used >= limit)
|
|
194
|
+
break;
|
|
195
|
+
const room = limit - used;
|
|
196
|
+
const segmentWidth = visibleWidth(segment.text);
|
|
197
|
+
if (segmentWidth <= room) {
|
|
198
|
+
visible.push(segment);
|
|
199
|
+
used += segmentWidth;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
const text = truncateToWidth(segment.text, room, "");
|
|
203
|
+
if (text)
|
|
204
|
+
visible.push({ text, color: segment.color });
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
visible.push({ text: ellipsis, color: visible[visible.length - 1]?.color ?? fallbackColor });
|
|
209
|
+
return visible;
|
|
210
|
+
}
|
|
211
|
+
export function buildStatusSegments(runtime, spinnerFrame = SPINNER_FRAMES[0]) {
|
|
212
|
+
if (!runtime.enabled)
|
|
213
|
+
return [];
|
|
214
|
+
switch (runtime.connectionStatus) {
|
|
215
|
+
case "connecting":
|
|
216
|
+
return [{ text: `${IDE_ICON} ${spinnerFrame}`, color: "dim" }];
|
|
217
|
+
case "error":
|
|
218
|
+
return [
|
|
219
|
+
{ text: `${IDE_ICON} `, color: "dim" },
|
|
220
|
+
{ text: "✕", color: "error" },
|
|
221
|
+
];
|
|
222
|
+
case "connected":
|
|
223
|
+
return buildConnectedSegments(runtime);
|
|
224
|
+
default:
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
export function buildStatusText(runtime, spinnerFrame = SPINNER_FRAMES[0], colorize) {
|
|
229
|
+
const segments = buildStatusSegments(runtime, spinnerFrame);
|
|
230
|
+
if (segments.length === 0)
|
|
231
|
+
return undefined;
|
|
232
|
+
return formatStatusSegments(segments, colorize);
|
|
233
|
+
}
|
|
234
|
+
export function formatStatusSegments(segments, colorize) {
|
|
235
|
+
if (!colorize)
|
|
236
|
+
return segments.map((segment) => segment.text).join("");
|
|
237
|
+
return segments.map((segment) => colorize(segment.color, segment.text)).join("");
|
|
238
|
+
}
|
|
239
|
+
function getThemeColorizer() {
|
|
240
|
+
const activeTheme = getActivePiTheme();
|
|
241
|
+
if (!activeTheme)
|
|
242
|
+
return undefined;
|
|
243
|
+
return (color, text) => activeTheme.fg(color, text);
|
|
244
|
+
}
|
|
245
|
+
function getActivePiTheme() {
|
|
246
|
+
const globalStore = globalThis;
|
|
247
|
+
for (const key of PI_THEME_KEYS) {
|
|
248
|
+
const candidate = globalStore[key];
|
|
249
|
+
if (!isThemeLike(candidate))
|
|
250
|
+
continue;
|
|
251
|
+
return candidate;
|
|
252
|
+
}
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
function isThemeLike(value) {
|
|
256
|
+
if (!value || typeof value !== "object")
|
|
257
|
+
return false;
|
|
258
|
+
return typeof value.fg === "function";
|
|
259
|
+
}
|
|
260
|
+
function buildConnectedSegments(runtime) {
|
|
261
|
+
const selection = runtime.latestSelection;
|
|
262
|
+
if (!selection)
|
|
263
|
+
return [{ text: `${IDE_ICON} ✓`, color: "dim" }];
|
|
264
|
+
const rel = basename(selection.filePath);
|
|
265
|
+
const range = describeRanges(selection.ranges);
|
|
266
|
+
const pending = runtime.attachState === "pending";
|
|
267
|
+
const marker = pending ? "⇡" : "✓";
|
|
268
|
+
const color = pending ? "success" : "dim";
|
|
269
|
+
const prefix = `${IDE_ICON} ${marker} `;
|
|
270
|
+
if (range === "open file")
|
|
271
|
+
return [{ text: `${prefix}${rel}`, color, shrink: true }];
|
|
272
|
+
return [
|
|
273
|
+
{ text: prefix, color },
|
|
274
|
+
{ text: rel, color, shrink: true },
|
|
275
|
+
{ text: range, color },
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
export function buildWidget(runtime, cwd) {
|
|
279
|
+
if (!runtime.enabled || runtime.connectionStatus === "disabled")
|
|
280
|
+
return undefined;
|
|
281
|
+
if (runtime.connectionStatus !== "connected" &&
|
|
282
|
+
runtime.connectionStatus !== "connecting" &&
|
|
283
|
+
runtime.connectionStatus !== "error") {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
const lines = [];
|
|
287
|
+
const ide = runtime.connectedServer?.name ?? runtime.currentCandidate?.lock.name ?? "IDE";
|
|
288
|
+
lines.push(`IDE: ${ide} (${runtime.connectionStatus})`);
|
|
289
|
+
if (runtime.currentCandidate) {
|
|
290
|
+
lines.push(`Workspace: ${runtime.currentCandidate.workspaceFolder}`);
|
|
291
|
+
}
|
|
292
|
+
if (runtime.latestSelection) {
|
|
293
|
+
const selection = runtime.latestSelection;
|
|
294
|
+
lines.push(`File: ${toRelativeDisplayPath(selection.filePath, selection.workspaceFolder, cwd)}`);
|
|
295
|
+
lines.push(`Range: ${describeRanges(selection.ranges)}`);
|
|
296
|
+
lines.push(`Attach: ${runtime.attachState}`);
|
|
297
|
+
if (selection.receivedAt)
|
|
298
|
+
lines.push(`Updated: ${new Date(selection.receivedAt).toLocaleTimeString()}`);
|
|
299
|
+
}
|
|
300
|
+
else if (runtime.connectionMessage) {
|
|
301
|
+
lines.push(runtime.connectionMessage);
|
|
302
|
+
}
|
|
303
|
+
return lines;
|
|
304
|
+
}
|