pi-ui-extend 0.1.17 → 0.1.19
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/app/app.js +8 -6
- package/dist/app/constants.d.ts +1 -0
- package/dist/app/constants.js +1 -0
- package/dist/app/input/input-controller.d.ts +1 -0
- package/dist/app/input/input-controller.js +29 -0
- package/dist/app/input/input-paste-handler.d.ts +1 -1
- package/dist/app/input/input-paste-handler.js +6 -5
- package/dist/app/input/voice-controller.js +16 -12
- package/dist/app/model/model-usage-status.js +4 -27
- package/dist/app/popup/popup-menu-controller.d.ts +1 -5
- package/dist/app/popup/popup-menu-controller.js +7 -8
- package/dist/app/process.js +7 -0
- package/dist/app/rendering/conversation-entry-renderer.js +17 -16
- package/dist/app/rendering/conversation-viewport.js +4 -35
- package/dist/app/rendering/editor-layout-renderer.d.ts +5 -1
- package/dist/app/rendering/editor-layout-renderer.js +25 -16
- package/dist/app/rendering/popup-menu-renderer.d.ts +1 -5
- package/dist/app/rendering/popup-menu-renderer.js +24 -34
- package/dist/app/rendering/render-controller.d.ts +2 -0
- package/dist/app/rendering/render-controller.js +38 -33
- package/dist/app/rendering/render-text.js +2 -2
- package/dist/app/rendering/status-line-renderer.js +1 -1
- package/dist/app/rendering/tab-line-renderer.js +3 -3
- package/dist/app/runtime.js +29 -3
- package/dist/app/screen/file-link-opener.d.ts +2 -0
- package/dist/app/screen/file-link-opener.js +84 -17
- package/dist/app/screen/mouse-controller.d.ts +1 -2
- package/dist/app/screen/mouse-controller.js +19 -28
- package/dist/app/screen/screen-styler.js +1 -1
- package/dist/app/session/lazy-session-manager.d.ts +1 -1
- package/dist/app/session/lazy-session-manager.js +64 -52
- package/dist/app/session/queued-message-controller.d.ts +6 -0
- package/dist/app/session/queued-message-controller.js +9 -1
- package/dist/app/session/queued-message-entries.d.ts +8 -0
- package/dist/app/session/queued-message-entries.js +41 -0
- package/dist/app/session/session-lifecycle-controller.d.ts +9 -1
- package/dist/app/session/session-lifecycle-controller.js +45 -11
- package/dist/app/session/tabs-controller.d.ts +11 -1
- package/dist/app/session/tabs-controller.js +197 -30
- package/dist/app/terminal/terminal-controller.d.ts +2 -0
- package/dist/app/terminal/terminal-controller.js +7 -5
- package/dist/schemas/pi-tools-suite-schema.d.ts +3 -0
- package/dist/schemas/pi-tools-suite-schema.js +3 -0
- package/dist/theme.d.ts +3 -0
- package/dist/theme.js +8 -2
- package/extensions/session-title/config.ts +3 -3
- package/extensions/session-title/index.ts +60 -5
- package/external/pi-tools-suite/README.md +3 -2
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +1 -0
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +1 -0
- package/external/pi-tools-suite/src/async-subagents/core/config.ts +0 -3
- package/external/pi-tools-suite/src/async-subagents/core/notifications.ts +64 -0
- package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +1 -0
- package/external/pi-tools-suite/src/async-subagents/index.ts +54 -8
- package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +4 -4
- package/external/pi-tools-suite/src/config.ts +56 -0
- package/external/pi-tools-suite/src/dcp/commands.ts +1 -1
- package/external/pi-tools-suite/src/dcp/index.ts +21 -1
- package/external/pi-tools-suite/src/dcp/state.ts +234 -7
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +10 -1
- package/external/pi-tools-suite/src/glm-coding-discipline/index.ts +580 -0
- package/external/pi-tools-suite/src/index.ts +2 -0
- package/external/pi-tools-suite/src/lib/lsp.ts +2 -5
- package/external/pi-tools-suite/src/lsp/_shared/config.ts +2 -0
- package/external/pi-tools-suite/src/lsp/_shared/types.ts +2 -0
- package/external/pi-tools-suite/src/lsp/manager.ts +15 -9
- package/external/pi-tools-suite/src/telegram-mirror/README.md +168 -0
- package/external/pi-tools-suite/src/telegram-mirror/bot.ts +228 -0
- package/external/pi-tools-suite/src/telegram-mirror/events.ts +94 -0
- package/external/pi-tools-suite/src/telegram-mirror/format.ts +120 -0
- package/external/pi-tools-suite/src/telegram-mirror/index.ts +424 -0
- package/external/pi-tools-suite/src/telegram-mirror/ipc.ts +420 -0
- package/external/pi-tools-suite/src/telegram-mirror/multiplexer.ts +408 -0
- package/external/pi-tools-suite/src/telegram-mirror/renderer.ts +214 -0
- package/external/pi-tools-suite/src/todo/index.ts +81 -4
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +5 -0
- package/external/pi-tools-suite/src/tool-descriptions.ts +4 -4
- package/package.json +1 -1
- package/schemas/pi-tools-suite.json +19 -0
|
@@ -49,41 +49,31 @@ export class PopupMenuRenderer {
|
|
|
49
49
|
const rightMargin = Math.max(0, width - margin - menuWidth);
|
|
50
50
|
return `${" ".repeat(margin)}${padOrTrimPlain(line.text, menuWidth)}${" ".repeat(rightMargin)}`;
|
|
51
51
|
}
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
headerLine.target = { kind: "popup-menu-close" };
|
|
55
|
-
headerLine.segments = [{
|
|
56
|
-
start: options.userContentLeft,
|
|
57
|
-
end: options.userContentLeft + options.userContentWidth,
|
|
58
|
-
foreground: this.host.theme.colors.accent,
|
|
59
|
-
background: this.host.theme.colors.popupHeaderBackground,
|
|
60
|
-
bold: true,
|
|
61
|
-
}];
|
|
62
|
-
const lines = [headerLine];
|
|
52
|
+
renderUserMessageMenu(width, menu) {
|
|
53
|
+
const lines = [this.popupMenuHeader("Message actions", width)];
|
|
63
54
|
for (const item of menu.visibleItems()) {
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
line
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
];
|
|
55
|
+
const marker = item.selected ? "▶ " : " ";
|
|
56
|
+
const text = `${marker}${this.labelDescriptionText(item.label, item.description, width - 2, 18)}`;
|
|
57
|
+
const labelStart = 2;
|
|
58
|
+
const labelEnd = Math.min(text.length, labelStart + item.label.length);
|
|
59
|
+
const description = item.description ? sanitizeText(item.description).replace(/\s+/gu, " ") : "";
|
|
60
|
+
const descriptionStart = description ? text.indexOf(description, labelEnd) : -1;
|
|
61
|
+
const line = {
|
|
62
|
+
text,
|
|
63
|
+
target: { kind: "popup-menu", index: item.index },
|
|
64
|
+
segments: [
|
|
65
|
+
...(item.selected ? [{ start: 0, end: 1, foreground: this.host.theme.colors.accent, bold: true }] : []),
|
|
66
|
+
{
|
|
67
|
+
start: labelStart,
|
|
68
|
+
end: labelEnd,
|
|
69
|
+
foreground: this.userMessageActionForeground(item.value),
|
|
70
|
+
bold: item.selected,
|
|
71
|
+
},
|
|
72
|
+
...(descriptionStart >= 0
|
|
73
|
+
? [{ start: descriptionStart, end: text.length, foreground: this.host.theme.colors.muted }]
|
|
74
|
+
: []),
|
|
75
|
+
],
|
|
76
|
+
};
|
|
87
77
|
lines.push(line);
|
|
88
78
|
}
|
|
89
79
|
return lines;
|
|
@@ -24,6 +24,7 @@ export type AppRenderControllerDeps = {
|
|
|
24
24
|
tabLineRenderer: TabLineRenderer;
|
|
25
25
|
toastController: AppToastController;
|
|
26
26
|
outputBuffer?: TerminalOutputBuffer;
|
|
27
|
+
loadingConversationOverlayText?: () => string | undefined;
|
|
27
28
|
voiceProgressOverlayText(): string | undefined;
|
|
28
29
|
};
|
|
29
30
|
export declare class AppRenderController {
|
|
@@ -38,4 +39,5 @@ export declare class AppRenderController {
|
|
|
38
39
|
private renderClickFlashOverlay;
|
|
39
40
|
private updateStatusMouseState;
|
|
40
41
|
private renderVoiceProgressOverlay;
|
|
42
|
+
private renderConversationLoadingOverlay;
|
|
41
43
|
}
|
|
@@ -6,10 +6,6 @@ import { ANSI_RESET, colorLine, colorize } from "../../theme.js";
|
|
|
6
6
|
import { stringDisplayWidth } from "../../terminal-width.js";
|
|
7
7
|
import { padOrTrimPlain } from "./render-text.js";
|
|
8
8
|
const INPUT_FRAME = {
|
|
9
|
-
topLeft: "╭",
|
|
10
|
-
topRight: "╮",
|
|
11
|
-
bottomLeft: "╰",
|
|
12
|
-
bottomRight: "╯",
|
|
13
9
|
horizontal: "─",
|
|
14
10
|
};
|
|
15
11
|
export class AppRenderController {
|
|
@@ -129,6 +125,11 @@ export class AppRenderController {
|
|
|
129
125
|
setRenderedBackground(row, rendered?.backgroundOverride);
|
|
130
126
|
appendFrameOutput("conversation", row, this.renderFrameRow(row, this.deps.screenStyler.styleBaseLine(row, rendered, conversationColumns)));
|
|
131
127
|
}
|
|
128
|
+
const loadingConversationOverlay = this.renderConversationLoadingOverlay(this.deps.loadingConversationOverlayText?.(), conversationColumns, topReservedRows, bodyHeight);
|
|
129
|
+
if (loadingConversationOverlay) {
|
|
130
|
+
this.deps.mouseController.renderedRowTexts.set(loadingConversationOverlay.row, loadingConversationOverlay.text);
|
|
131
|
+
appendFrameOutput("conversation", loadingConversationOverlay.row, this.renderFrameRow(loadingConversationOverlay.row, loadingConversationOverlay.output));
|
|
132
|
+
}
|
|
132
133
|
const aboveEditorStartRow = inputSeparatorRow + 1;
|
|
133
134
|
for (let index = 0; index < aboveEditorLines.length; index += 1) {
|
|
134
135
|
const rendered = frameRenderedLine(aboveEditorLines[index], columns, this.deps.theme, this.deps.screenStyler);
|
|
@@ -160,7 +161,7 @@ export class AppRenderController {
|
|
|
160
161
|
const row = toScreenRow(inputStartRow + index);
|
|
161
162
|
this.deps.mouseController.renderedRowTexts.set(row, inputLine);
|
|
162
163
|
const tagColor = this.deps.theme.colors.accent;
|
|
163
|
-
const styledLine = this.deps.screenStyler.styleInputLine(row, inputLine, tagSpans, suggestionSpans, columns, tagColor, this.deps.theme.colors.muted
|
|
164
|
+
const styledLine = this.deps.screenStyler.styleInputLine(row, inputLine, tagSpans, suggestionSpans, columns, tagColor, this.deps.theme.colors.muted);
|
|
164
165
|
appendFrameOutput("inputStatus", row, this.renderFrameRow(row, styledLine));
|
|
165
166
|
}
|
|
166
167
|
if (renderedInput.scrollBar && columns > 0) {
|
|
@@ -293,15 +294,19 @@ export class AppRenderController {
|
|
|
293
294
|
this.deps.mouseController.statusThinkingTarget = this.deps.statusLineRenderer.thinkingTarget(statusLayout.text, statusRow);
|
|
294
295
|
this.deps.mouseController.statusContextTarget = this.deps.statusLineRenderer.contextTarget(statusLayout.text, statusRow, statusLayout);
|
|
295
296
|
this.deps.mouseController.statusModelUsageTarget = this.deps.statusLineRenderer.modelUsageTarget(statusLayout.text, statusRow, statusLayout);
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
297
|
+
if (widgetLayout) {
|
|
298
|
+
this.deps.mouseController.statusDraftQueueTarget = this.deps.statusLineRenderer.draftQueueTarget?.(widgetLayout, widgetRow);
|
|
299
|
+
this.deps.mouseController.statusUserJumpTarget = this.deps.statusLineRenderer.userJumpTarget?.(widgetLayout, widgetRow);
|
|
300
|
+
this.deps.mouseController.statusThinkingExpandTarget = this.deps.statusLineRenderer.thinkingExpandTarget?.(widgetLayout, widgetRow);
|
|
301
|
+
this.deps.mouseController.statusCompactToolsTarget = this.deps.statusLineRenderer.compactToolsTarget?.(widgetLayout, widgetRow);
|
|
302
|
+
this.deps.mouseController.statusTerminalBellSoundTarget = this.deps.statusLineRenderer.terminalBellSoundTarget?.(widgetLayout, widgetRow);
|
|
303
|
+
}
|
|
301
304
|
this.deps.mouseController.statusSessionTarget = this.deps.statusLineRenderer.sessionTarget(statusLayout.text, statusRow, statusLayout.sessionLabel, statusLayout.workspaceLabel);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
+
if (widgetLayout) {
|
|
306
|
+
this.deps.mouseController.statusPromptEnhancerTarget = this.deps.statusLineRenderer.promptEnhancerTarget(widgetLayout, widgetRow);
|
|
307
|
+
this.deps.mouseController.statusVoiceMicTarget = this.deps.statusLineRenderer.voiceMicTarget(widgetLayout, widgetRow);
|
|
308
|
+
this.deps.mouseController.statusVoiceLanguageTarget = this.deps.statusLineRenderer.voiceLanguageTarget(widgetLayout, widgetRow);
|
|
309
|
+
}
|
|
305
310
|
this.deps.mouseController.renderedRowTexts.set(statusRow, statusLayout.text);
|
|
306
311
|
}
|
|
307
312
|
renderVoiceProgressOverlay(message, width, rows) {
|
|
@@ -323,40 +328,40 @@ export class AppRenderController {
|
|
|
323
328
|
].join("");
|
|
324
329
|
return { row: Math.min(2, rows - 1), text, output };
|
|
325
330
|
}
|
|
331
|
+
renderConversationLoadingOverlay(message, width, topReservedRows, bodyHeight) {
|
|
332
|
+
if (!message || width <= 0 || bodyHeight <= 0)
|
|
333
|
+
return undefined;
|
|
334
|
+
const overlayWidth = Math.min(stringDisplayWidth(message), width);
|
|
335
|
+
const leftWidth = Math.max(0, Math.floor((width - overlayWidth) / 2));
|
|
336
|
+
const rightWidth = Math.max(0, width - leftWidth - overlayWidth);
|
|
337
|
+
const text = `${" ".repeat(leftWidth)}${padOrTrimPlain(message, overlayWidth)}${" ".repeat(rightWidth)}`;
|
|
338
|
+
const row = topReservedRows + Math.floor((bodyHeight + 1) / 2);
|
|
339
|
+
const output = this.deps.screenStyler.styleLine(row, text, width, {
|
|
340
|
+
foreground: this.deps.theme.colors.muted,
|
|
341
|
+
});
|
|
342
|
+
return { row, text, output };
|
|
343
|
+
}
|
|
326
344
|
}
|
|
327
345
|
function visibleToastStates(toastController) {
|
|
328
346
|
const candidate = toastController;
|
|
329
347
|
return typeof candidate.visibleStates === "function" ? candidate.visibleStates() : candidate.toast?.visibleStates ?? [];
|
|
330
348
|
}
|
|
331
349
|
function inputFrameLine(width, edge) {
|
|
350
|
+
void edge;
|
|
332
351
|
if (width <= 0)
|
|
333
352
|
return "";
|
|
334
|
-
|
|
335
|
-
return edge === "top" ? INPUT_FRAME.topLeft : INPUT_FRAME.bottomLeft;
|
|
336
|
-
const left = edge === "top" ? INPUT_FRAME.topLeft : INPUT_FRAME.bottomLeft;
|
|
337
|
-
const right = edge === "top" ? INPUT_FRAME.topRight : INPUT_FRAME.bottomRight;
|
|
338
|
-
return `${left}${INPUT_FRAME.horizontal.repeat(Math.max(0, width - 2))}${right}`;
|
|
353
|
+
return INPUT_FRAME.horizontal.repeat(width);
|
|
339
354
|
}
|
|
340
355
|
function frameRenderedLine(line, width, theme, screenStyler) {
|
|
341
356
|
if (width <= 0)
|
|
342
357
|
return { line, text: "", output: () => "" };
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
});
|
|
347
|
-
return { line, text: "│", output: () => border };
|
|
348
|
-
}
|
|
349
|
-
const innerWidth = Math.max(0, width - 2);
|
|
350
|
-
const innerText = padOrTrimPlain(line?.text ?? "", innerWidth);
|
|
351
|
-
const innerLine = line ? frameInnerRenderedLine(line, innerText, innerWidth) : undefined;
|
|
352
|
-
const leftBorder = colorize("│", {
|
|
353
|
-
foreground: theme.colors.inputBorder,
|
|
354
|
-
});
|
|
355
|
-
const rightBorder = leftBorder;
|
|
358
|
+
void theme;
|
|
359
|
+
const text = padOrTrimPlain(line?.text ?? "", width);
|
|
360
|
+
const outputLine = line ? frameInnerRenderedLine(line, text, width) : undefined;
|
|
356
361
|
return {
|
|
357
362
|
line,
|
|
358
|
-
text
|
|
359
|
-
output: (row) =>
|
|
363
|
+
text,
|
|
364
|
+
output: (row) => screenStyler.styleBaseLine(row, outputLine, width),
|
|
360
365
|
};
|
|
361
366
|
}
|
|
362
367
|
function frameInnerRenderedLine(line, text, width) {
|
|
@@ -98,8 +98,8 @@ export function padOrTrimPlain(text, width) {
|
|
|
98
98
|
}
|
|
99
99
|
export function horizontalPaddingLayout(width) {
|
|
100
100
|
const safeWidth = Math.max(1, width);
|
|
101
|
-
const left =
|
|
102
|
-
const right =
|
|
101
|
+
const left = 0;
|
|
102
|
+
const right = 0;
|
|
103
103
|
return { left, right, contentWidth: Math.max(1, safeWidth - left - right) };
|
|
104
104
|
}
|
|
105
105
|
export function padHorizontalText(text, width) {
|
|
@@ -110,7 +110,7 @@ export class StatusLineRenderer {
|
|
|
110
110
|
}
|
|
111
111
|
inputBorderWidgetSegments(layout, text) {
|
|
112
112
|
const colors = this.host.theme.colors;
|
|
113
|
-
const background = colors.
|
|
113
|
+
const background = colors.inputBorderWidgetBackground;
|
|
114
114
|
const segments = [];
|
|
115
115
|
const pushWidgetSegment = (widget, foreground) => {
|
|
116
116
|
if (!widget)
|
|
@@ -50,7 +50,7 @@ export class TabLineRenderer {
|
|
|
50
50
|
segments.push({
|
|
51
51
|
start: separatorOffset + 1,
|
|
52
52
|
end: separatorOffset + 2,
|
|
53
|
-
foreground: this.host.theme.colors.
|
|
53
|
+
foreground: this.host.theme.colors.tabBorder,
|
|
54
54
|
});
|
|
55
55
|
displayColumn += separatorWidth;
|
|
56
56
|
}
|
|
@@ -89,7 +89,7 @@ export class TabLineRenderer {
|
|
|
89
89
|
segments.push({
|
|
90
90
|
start: newTabDividerOffset,
|
|
91
91
|
end: newTabDividerOffset + 1,
|
|
92
|
-
foreground: this.host.theme.colors.
|
|
92
|
+
foreground: this.host.theme.colors.tabBorder,
|
|
93
93
|
});
|
|
94
94
|
segments.push({
|
|
95
95
|
start: lineText.length - APP_ICONS.plus.length,
|
|
@@ -119,7 +119,7 @@ export class TabLineRenderer {
|
|
|
119
119
|
}
|
|
120
120
|
renderBottom(row, layout, width) {
|
|
121
121
|
return this.host.screenStyler.styleLine(row, this.bottomText(layout, width), width, {
|
|
122
|
-
foreground: this.host.theme.colors.
|
|
122
|
+
foreground: this.host.theme.colors.tabBorder,
|
|
123
123
|
});
|
|
124
124
|
}
|
|
125
125
|
bottomText(layout, width) {
|
package/dist/app/runtime.js
CHANGED
|
@@ -133,6 +133,32 @@ function isBundledQuestionConflict(error, bundledExtensionPaths) {
|
|
|
133
133
|
}
|
|
134
134
|
return false;
|
|
135
135
|
}
|
|
136
|
+
const bundledSkillsInstallPromises = new Map();
|
|
137
|
+
const piToolsSuiteInstallPromises = new Map();
|
|
138
|
+
async function ensureBundledSkillsInstalledOnce(options = {}) {
|
|
139
|
+
const targetPath = resolve(options.targetPath ?? bundledSkillsInstallPath(options.homeDir));
|
|
140
|
+
const existing = bundledSkillsInstallPromises.get(targetPath);
|
|
141
|
+
if (existing)
|
|
142
|
+
return await existing;
|
|
143
|
+
const pending = ensureBundledSkillsInstalled(options).catch((error) => {
|
|
144
|
+
bundledSkillsInstallPromises.delete(targetPath);
|
|
145
|
+
throw error;
|
|
146
|
+
});
|
|
147
|
+
bundledSkillsInstallPromises.set(targetPath, pending);
|
|
148
|
+
return await pending;
|
|
149
|
+
}
|
|
150
|
+
async function ensurePiToolsSuiteExtensionInstalledOnce(options = {}) {
|
|
151
|
+
const targetPath = resolve(options.targetPath ?? piToolsSuiteExtensionInstallPath(options.agentDir));
|
|
152
|
+
const existing = piToolsSuiteInstallPromises.get(targetPath);
|
|
153
|
+
if (existing)
|
|
154
|
+
return await existing;
|
|
155
|
+
const pending = ensurePiToolsSuiteExtensionInstalled(options).catch((error) => {
|
|
156
|
+
piToolsSuiteInstallPromises.delete(targetPath);
|
|
157
|
+
throw error;
|
|
158
|
+
});
|
|
159
|
+
piToolsSuiteInstallPromises.set(targetPath, pending);
|
|
160
|
+
return await pending;
|
|
161
|
+
}
|
|
136
162
|
export function resolvePixRuntimeModelRef(options, sessionManager, config = loadPixConfig()) {
|
|
137
163
|
if (options.modelRef)
|
|
138
164
|
return options.modelRef;
|
|
@@ -176,8 +202,8 @@ export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
|
176
202
|
const effectiveModelRef = resolvePixRuntimeModelRef(options, sessionManager, config);
|
|
177
203
|
const parsedModel = effectiveModelRef ? parseModelRef(effectiveModelRef) : undefined;
|
|
178
204
|
const initialThinkingLevel = resolvePixRuntimeInitialThinkingLevel(options, sessionManager, config);
|
|
179
|
-
await
|
|
180
|
-
await
|
|
205
|
+
await ensureBundledSkillsInstalledOnce();
|
|
206
|
+
await ensurePiToolsSuiteExtensionInstalledOnce({ agentDir });
|
|
181
207
|
const bundledExtensionPaths = getBundledExtensionPaths();
|
|
182
208
|
const services = await createAgentSessionServices({
|
|
183
209
|
cwd,
|
|
@@ -232,7 +258,7 @@ export async function createPixRuntime(options, runtimeOptions = {}) {
|
|
|
232
258
|
sessionManager: options.noSession
|
|
233
259
|
? SessionManager.inMemory(options.cwd)
|
|
234
260
|
: options.sessionPath
|
|
235
|
-
? openLazySessionManager(options.sessionPath, { cwdOverride: options.cwd })
|
|
261
|
+
? await openLazySessionManager(options.sessionPath, { cwdOverride: options.cwd })
|
|
236
262
|
: SessionManager.create(options.cwd),
|
|
237
263
|
});
|
|
238
264
|
}
|
|
@@ -3,6 +3,8 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import type { RenderedLink } from "./file-links.js";
|
|
4
4
|
type FileLinkOpenerDeps = {
|
|
5
5
|
existsSync: typeof existsSync;
|
|
6
|
+
env: NodeJS.ProcessEnv;
|
|
7
|
+
platform: NodeJS.Platform;
|
|
6
8
|
spawn: typeof spawn;
|
|
7
9
|
};
|
|
8
10
|
export declare function setFileLinkOpenerTestDeps(overrides: Partial<FileLinkOpenerDeps>): () => void;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import {
|
|
3
|
+
import { isAbsolute, posix, win32 } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
let deps = { existsSync, spawn };
|
|
5
|
+
let deps = { existsSync, env: process.env, platform: process.platform, spawn };
|
|
6
6
|
export function setFileLinkOpenerTestDeps(overrides) {
|
|
7
7
|
const previous = deps;
|
|
8
8
|
deps = { ...deps, ...overrides };
|
|
@@ -14,13 +14,10 @@ export function openFileLink(link) {
|
|
|
14
14
|
const filePath = link.filePath ?? filePathFromUrl(link.url);
|
|
15
15
|
if (!filePath)
|
|
16
16
|
return false;
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
if (trySpawnCandidates(candidates, [target]))
|
|
17
|
+
const editorLaunch = preferredEditorLaunch(filePath, link.line, link.column);
|
|
18
|
+
if (editorLaunch && trySpawnCandidates(editorLaunch.candidates, editorLaunch.args))
|
|
20
19
|
return true;
|
|
21
|
-
|
|
22
|
-
return spawnDetached("open", ["-a", "Zed", filePath]);
|
|
23
|
-
return false;
|
|
20
|
+
return openPathWithSystemViewer(filePath);
|
|
24
21
|
}
|
|
25
22
|
function filePathFromUrl(url) {
|
|
26
23
|
if (!url.startsWith("file://"))
|
|
@@ -37,29 +34,99 @@ function zedTarget(filePath, line, column) {
|
|
|
37
34
|
return filePath;
|
|
38
35
|
return column === undefined ? `${filePath}:${line}` : `${filePath}:${line}:${column}`;
|
|
39
36
|
}
|
|
37
|
+
function gotoTarget(filePath, line, column) {
|
|
38
|
+
if (line === undefined)
|
|
39
|
+
return filePath;
|
|
40
|
+
return column === undefined ? `${filePath}:${line}` : `${filePath}:${line}:${column}`;
|
|
41
|
+
}
|
|
42
|
+
function preferredEditorLaunch(filePath, line, column) {
|
|
43
|
+
switch (detectEditor(deps.env)) {
|
|
44
|
+
case "cursor":
|
|
45
|
+
return { args: ["--goto", gotoTarget(filePath, line, column)], candidates: commandCandidates(deps.env.CURSOR_CLI, "cursor") };
|
|
46
|
+
case "jetbrains":
|
|
47
|
+
return {
|
|
48
|
+
args: jetbrainsTargetArgs(filePath, line),
|
|
49
|
+
candidates: commandCandidates(deps.env.JETBRAINS_IDE_CLI, "idea", "idea64", "webstorm", "webstorm64", "pycharm", "pycharm64", "goland", "goland64", "clion", "clion64", "phpstorm", "phpstorm64", "rubymine", "rubymine64", "rider", "rider64"),
|
|
50
|
+
};
|
|
51
|
+
case "vscode":
|
|
52
|
+
return { args: ["--goto", gotoTarget(filePath, line, column)], candidates: commandCandidates(deps.env.VSCODE_CLI, "code", "code-insiders") };
|
|
53
|
+
case "windsurf":
|
|
54
|
+
return { args: ["--goto", gotoTarget(filePath, line, column)], candidates: commandCandidates(deps.env.WINDSURF_CLI, "windsurf") };
|
|
55
|
+
case "zed":
|
|
56
|
+
return { args: [zedTarget(filePath, line, column)], candidates: zedCommandCandidates() };
|
|
57
|
+
default:
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function detectEditor(env) {
|
|
62
|
+
const termProgram = env.TERM_PROGRAM?.trim().toLowerCase();
|
|
63
|
+
const terminalEmulator = env.TERMINAL_EMULATOR?.trim().toLowerCase();
|
|
64
|
+
const terminalProvider = env.TERMINAL_PROVIDER?.trim().toLowerCase();
|
|
65
|
+
if (termProgram === "cursor" || env.CURSOR_TRACE_ID || env.CURSOR_TRACE)
|
|
66
|
+
return "cursor";
|
|
67
|
+
if (termProgram === "windsurf")
|
|
68
|
+
return "windsurf";
|
|
69
|
+
if (termProgram === "zed" || env.ZED_CLI)
|
|
70
|
+
return "zed";
|
|
71
|
+
if (termProgram === "vscode" || env.VSCODE_IPC_HOOK_CLI || env.VSCODE_GIT_IPC_HANDLE)
|
|
72
|
+
return "vscode";
|
|
73
|
+
if (terminalEmulator?.includes("jetbrains") || terminalProvider === "jetbrains")
|
|
74
|
+
return "jetbrains";
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
40
77
|
function zedCommandCandidates() {
|
|
41
|
-
const candidates = [
|
|
42
|
-
if (
|
|
78
|
+
const candidates = [deps.env.ZED_CLI, "zed", "zeditor"];
|
|
79
|
+
if (deps.platform === "darwin")
|
|
43
80
|
candidates.push("/opt/homebrew/bin/zed", "/usr/local/bin/zed");
|
|
44
81
|
return candidates.filter((candidate) => Boolean(candidate));
|
|
45
82
|
}
|
|
83
|
+
function commandCandidates(primary, ...rest) {
|
|
84
|
+
return [primary, ...rest].filter((candidate) => Boolean(candidate));
|
|
85
|
+
}
|
|
86
|
+
function jetbrainsTargetArgs(filePath, line) {
|
|
87
|
+
if (line === undefined)
|
|
88
|
+
return [filePath];
|
|
89
|
+
return ["--line", `${line}`, filePath];
|
|
90
|
+
}
|
|
46
91
|
function trySpawnCandidates(candidates, args) {
|
|
47
92
|
for (const command of candidates) {
|
|
48
|
-
if (
|
|
49
|
-
continue;
|
|
50
|
-
if (!command.includes("/") && !commandOnPath(command))
|
|
93
|
+
if (!canRunCommand(command))
|
|
51
94
|
continue;
|
|
52
95
|
if (spawnDetached(command, args))
|
|
53
96
|
return true;
|
|
54
97
|
}
|
|
55
98
|
return false;
|
|
56
99
|
}
|
|
100
|
+
function canRunCommand(command) {
|
|
101
|
+
if (hasPathSeparator(command) || isAbsolute(command))
|
|
102
|
+
return deps.existsSync(command);
|
|
103
|
+
return commandOnPath(command);
|
|
104
|
+
}
|
|
105
|
+
function hasPathSeparator(command) {
|
|
106
|
+
return command.includes("/") || command.includes("\\");
|
|
107
|
+
}
|
|
57
108
|
function commandOnPath(command) {
|
|
58
|
-
const pathEntries =
|
|
59
|
-
const extensions =
|
|
60
|
-
? (
|
|
109
|
+
const pathEntries = deps.env.PATH?.split(pathDelimiter()) ?? [];
|
|
110
|
+
const extensions = deps.platform === "win32"
|
|
111
|
+
? (deps.env.PATHEXT?.split(";") ?? [".EXE", ".CMD", ".BAT", ".COM"])
|
|
61
112
|
: [""];
|
|
62
|
-
return pathEntries.some((entry) => extensions.some((
|
|
113
|
+
return pathEntries.some((entry) => pathCommandCandidates(entry, command, extensions).some((candidate) => deps.existsSync(candidate)));
|
|
114
|
+
}
|
|
115
|
+
function pathDelimiter() {
|
|
116
|
+
return deps.platform === "win32" ? ";" : ":";
|
|
117
|
+
}
|
|
118
|
+
function pathCommandCandidates(entry, command, extensions) {
|
|
119
|
+
const pathApi = deps.platform === "win32" ? win32 : posix;
|
|
120
|
+
if (deps.platform !== "win32" || pathApi.extname(command))
|
|
121
|
+
return [pathApi.join(entry, command)];
|
|
122
|
+
return [pathApi.join(entry, command), ...extensions.map((extension) => pathApi.join(entry, `${command}${extension}`))];
|
|
123
|
+
}
|
|
124
|
+
function openPathWithSystemViewer(filePath) {
|
|
125
|
+
if (deps.platform === "darwin")
|
|
126
|
+
return spawnDetached("open", [filePath]);
|
|
127
|
+
if (deps.platform === "win32")
|
|
128
|
+
return spawnDetached("cmd", ["/c", "start", "", filePath]);
|
|
129
|
+
return spawnDetached("xdg-open", [filePath]);
|
|
63
130
|
}
|
|
64
131
|
function spawnDetached(command, args) {
|
|
65
132
|
try {
|
|
@@ -21,8 +21,6 @@ export type InputFrameCopyRows = {
|
|
|
21
21
|
inputEndRow: number;
|
|
22
22
|
inputSeparatorRow: number;
|
|
23
23
|
inputBottomSeparatorRow: number;
|
|
24
|
-
contentStartColumn: number;
|
|
25
|
-
contentEndColumn: number;
|
|
26
24
|
};
|
|
27
25
|
export type AppMouseControllerHost = {
|
|
28
26
|
terminalColumns(): number;
|
|
@@ -154,6 +152,7 @@ export declare class AppMouseController {
|
|
|
154
152
|
private handleStatusContextClick;
|
|
155
153
|
private handleStatusModelUsageClick;
|
|
156
154
|
private handleStatusUserJumpClick;
|
|
155
|
+
private handleInputBorderStatusClick;
|
|
157
156
|
private openStatusUserJumpMenu;
|
|
158
157
|
private handleStatusDraftQueueClick;
|
|
159
158
|
private handleStatusThinkingExpandClick;
|
|
@@ -57,6 +57,8 @@ export class AppMouseController {
|
|
|
57
57
|
if (this.handleInputScrollBar(event))
|
|
58
58
|
return;
|
|
59
59
|
this.showClickFlashOnPress(event);
|
|
60
|
+
if (event.button === 0 && !event.released && this.handleInputBorderStatusClick(event))
|
|
61
|
+
return;
|
|
60
62
|
if (this.handleMouseSelection(event))
|
|
61
63
|
return;
|
|
62
64
|
if (this.withClickFlash(event, () => this.handleImageClick(event)))
|
|
@@ -73,24 +75,8 @@ export class AppMouseController {
|
|
|
73
75
|
return;
|
|
74
76
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusModelUsageClick(event)))
|
|
75
77
|
return;
|
|
76
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusDraftQueueClick(event)))
|
|
77
|
-
return;
|
|
78
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusUserJumpClick(event)))
|
|
79
|
-
return;
|
|
80
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusThinkingExpandClick(event)))
|
|
81
|
-
return;
|
|
82
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusCompactToolsClick(event)))
|
|
83
|
-
return;
|
|
84
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusTerminalBellSoundClick(event)))
|
|
85
|
-
return;
|
|
86
78
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusSessionClick(event)))
|
|
87
79
|
return;
|
|
88
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusPromptEnhancerClick(event)))
|
|
89
|
-
return;
|
|
90
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusVoiceMicClick(event)))
|
|
91
|
-
return;
|
|
92
|
-
if (event.button === 0 && this.withClickFlash(event, () => this.handleStatusVoiceLanguageClick(event)))
|
|
93
|
-
return;
|
|
94
80
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleExtensionInputClick(event)))
|
|
95
81
|
return;
|
|
96
82
|
if (event.button === 0 && this.withClickFlash(event, () => this.handleInputClick(event)))
|
|
@@ -290,6 +276,7 @@ export class AppMouseController {
|
|
|
290
276
|
this.statusUserJumpTarget,
|
|
291
277
|
this.statusThinkingExpandTarget,
|
|
292
278
|
this.statusCompactToolsTarget,
|
|
279
|
+
this.statusTerminalBellSoundTarget,
|
|
293
280
|
this.statusSessionTarget,
|
|
294
281
|
this.statusPromptEnhancerTarget,
|
|
295
282
|
this.statusVoiceMicTarget,
|
|
@@ -333,7 +320,7 @@ export class AppMouseController {
|
|
|
333
320
|
return false;
|
|
334
321
|
const opened = this.host.openFileLink?.(link) ?? openDetectedFileLink(link);
|
|
335
322
|
if (!opened)
|
|
336
|
-
this.host.showToast("Could not open file link
|
|
323
|
+
this.host.showToast("Could not open file link in the detected editor or system viewer.", "warning");
|
|
337
324
|
return true;
|
|
338
325
|
}
|
|
339
326
|
handleInputScrollBar(event) {
|
|
@@ -476,6 +463,16 @@ export class AppMouseController {
|
|
|
476
463
|
void this.openStatusUserJumpMenu();
|
|
477
464
|
return true;
|
|
478
465
|
}
|
|
466
|
+
handleInputBorderStatusClick(event) {
|
|
467
|
+
return this.handleStatusDraftQueueClick(event)
|
|
468
|
+
|| this.handleStatusUserJumpClick(event)
|
|
469
|
+
|| this.handleStatusThinkingExpandClick(event)
|
|
470
|
+
|| this.handleStatusCompactToolsClick(event)
|
|
471
|
+
|| this.handleStatusTerminalBellSoundClick(event)
|
|
472
|
+
|| this.handleStatusPromptEnhancerClick(event)
|
|
473
|
+
|| this.handleStatusVoiceMicClick(event)
|
|
474
|
+
|| this.handleStatusVoiceLanguageClick(event);
|
|
475
|
+
}
|
|
479
476
|
async openStatusUserJumpMenu() {
|
|
480
477
|
try {
|
|
481
478
|
const refreshPromise = this.host.refreshUserMessageJumpMenuItems?.();
|
|
@@ -792,8 +789,6 @@ export class AppMouseController {
|
|
|
792
789
|
inputEndRow: toScreenRowExclusive(layout.inputStartRow + layout.renderedInput.lines.length),
|
|
793
790
|
inputSeparatorRow: toScreenRow(layout.inputSeparatorRow),
|
|
794
791
|
inputBottomSeparatorRow: toScreenRow(layout.inputBottomSeparatorRow),
|
|
795
|
-
contentStartColumn: 2,
|
|
796
|
-
contentEndColumn: columns,
|
|
797
792
|
};
|
|
798
793
|
}
|
|
799
794
|
getSelectedConversationText(anchor, current) {
|
|
@@ -803,11 +798,13 @@ export class AppMouseController {
|
|
|
803
798
|
const renderedLines = this.host.conversationViewport().slice(width, range.start.line, count);
|
|
804
799
|
const lines = [];
|
|
805
800
|
for (let index = 0; index < count; index += 1) {
|
|
806
|
-
const
|
|
801
|
+
const rendered = renderedLines[index];
|
|
802
|
+
const text = rendered?.text ?? "";
|
|
807
803
|
const line = range.start.line + index;
|
|
808
804
|
const startColumn = line === range.start.line ? range.start.x : 1;
|
|
809
805
|
const endColumn = line === range.end.line ? range.end.x : text.length + 1;
|
|
810
|
-
|
|
806
|
+
const lineText = sliceByDisplayColumns(text, startColumn, endColumn);
|
|
807
|
+
lines.push(lineText.trimEnd());
|
|
811
808
|
}
|
|
812
809
|
return lines.join("\n").replace(/\s+$/u, "");
|
|
813
810
|
}
|
|
@@ -941,13 +938,7 @@ export function screenSelectionLineText(row, text, startColumn, endColumn, input
|
|
|
941
938
|
if (inputFrame && (row === inputFrame.inputSeparatorRow || row === inputFrame.inputBottomSeparatorRow)) {
|
|
942
939
|
return undefined;
|
|
943
940
|
}
|
|
944
|
-
|
|
945
|
-
let copyEndColumn = endColumn;
|
|
946
|
-
if (inputFrame && row >= inputFrame.inputStartRow && row < inputFrame.inputEndRow) {
|
|
947
|
-
copyStartColumn = Math.max(copyStartColumn, inputFrame.contentStartColumn);
|
|
948
|
-
copyEndColumn = Math.min(copyEndColumn, inputFrame.contentEndColumn);
|
|
949
|
-
}
|
|
950
|
-
return sliceByDisplayColumns(text, copyStartColumn, copyEndColumn);
|
|
941
|
+
return sliceByDisplayColumns(text, startColumn, endColumn);
|
|
951
942
|
}
|
|
952
943
|
function sameConversationPoint(left, right) {
|
|
953
944
|
return !!left && left.line === right.line && left.x === right.x;
|
|
@@ -75,7 +75,7 @@ export class ScreenStyler {
|
|
|
75
75
|
}
|
|
76
76
|
styleInputLine(row, text, tagSpans, suggestionSpans, width, tagColor, suggestionColor, frameColor) {
|
|
77
77
|
const colors = this.host.theme.colors;
|
|
78
|
-
const baseOptions = { foreground: colors.
|
|
78
|
+
const baseOptions = { foreground: colors.warning };
|
|
79
79
|
if (this.selectionRangeForRow(row, width, text))
|
|
80
80
|
return this.styleLine(row, text, width, baseOptions);
|
|
81
81
|
const plain = padOrTrimPlain(text, width);
|
|
@@ -8,4 +8,4 @@ export type LazySessionHistoryReader = {
|
|
|
8
8
|
hasOlder(): boolean;
|
|
9
9
|
readOlder(limit: number): Promise<SessionEntry[]>;
|
|
10
10
|
};
|
|
11
|
-
export declare function openLazySessionManager(sessionPath: string, options?: LazySessionManagerOptions): SessionManager
|
|
11
|
+
export declare function openLazySessionManager(sessionPath: string, options?: LazySessionManagerOptions): Promise<SessionManager>;
|