pi-ui-extend 0.1.11 → 0.1.15
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 +1 -1
- package/dist/app/app.d.ts +5 -0
- package/dist/app/app.js +88 -16
- package/dist/app/cli/install.d.ts +14 -0
- package/dist/app/cli/install.js +19 -7
- package/dist/app/cli/startup-info.js +5 -2
- package/dist/app/cli/update.d.ts +7 -0
- package/dist/app/cli/update.js +11 -3
- package/dist/app/commands/command-controller.js +1 -0
- package/dist/app/commands/command-host.d.ts +3 -0
- package/dist/app/commands/command-model-actions.d.ts +2 -0
- package/dist/app/commands/command-model-actions.js +40 -4
- package/dist/app/commands/command-navigation-actions.js +3 -0
- package/dist/app/commands/command-registry.d.ts +1 -0
- package/dist/app/commands/command-registry.js +8 -0
- package/dist/app/commands/shell-command.d.ts +7 -0
- package/dist/app/commands/shell-command.js +12 -4
- package/dist/app/extensions/extension-ui-controller.d.ts +16 -5
- package/dist/app/extensions/extension-ui-controller.js +99 -61
- package/dist/app/icons.d.ts +1 -0
- package/dist/app/icons.js +2 -0
- package/dist/app/input/input-action-controller.d.ts +1 -0
- package/dist/app/input/input-action-controller.js +8 -2
- package/dist/app/input/prompt-enhancer-controller.d.ts +7 -1
- package/dist/app/input/prompt-enhancer-controller.js +12 -3
- package/dist/app/input/voice-controller.d.ts +49 -1
- package/dist/app/input/voice-controller.js +16 -5
- package/dist/app/logger.d.ts +25 -0
- package/dist/app/logger.js +90 -0
- package/dist/app/model/model-usage-status.js +30 -15
- package/dist/app/popup/menu-items-controller.d.ts +2 -0
- package/dist/app/popup/menu-items-controller.js +45 -6
- package/dist/app/popup/popup-action-controller.d.ts +2 -1
- package/dist/app/popup/popup-action-controller.js +7 -4
- package/dist/app/popup/popup-menu-controller.d.ts +36 -23
- package/dist/app/popup/popup-menu-controller.js +68 -322
- package/dist/app/rendering/conversation-entry-renderer.js +4 -13
- package/dist/app/rendering/conversation-viewport.d.ts +10 -2
- package/dist/app/rendering/conversation-viewport.js +157 -16
- package/dist/app/rendering/editor-panels.js +4 -2
- package/dist/app/rendering/popup-menu-renderer.d.ts +50 -0
- package/dist/app/rendering/popup-menu-renderer.js +307 -0
- package/dist/app/rendering/render-controller.js +5 -13
- package/dist/app/rendering/status-line-renderer.d.ts +1 -1
- package/dist/app/rendering/status-line-renderer.js +30 -35
- package/dist/app/rendering/toast-controller.d.ts +11 -3
- package/dist/app/rendering/toast-controller.js +53 -12
- package/dist/app/rendering/toast-renderer.js +10 -13
- package/dist/app/rendering/tool-block-renderer.d.ts +1 -0
- package/dist/app/rendering/tool-block-renderer.js +3 -2
- package/dist/app/runtime.d.ts +2 -1
- package/dist/app/runtime.js +20 -10
- package/dist/app/screen/clipboard.d.ts +9 -0
- package/dist/app/screen/clipboard.js +19 -6
- package/dist/app/screen/file-link-opener.d.ts +8 -0
- package/dist/app/screen/file-link-opener.js +11 -3
- package/dist/app/screen/file-links.js +3 -3
- package/dist/app/screen/image-opener.d.ts +12 -0
- package/dist/app/screen/image-opener.js +13 -5
- package/dist/app/screen/mouse-controller.d.ts +2 -2
- package/dist/app/screen/mouse-controller.js +27 -48
- package/dist/app/screen/screen-styler.d.ts +1 -1
- package/dist/app/screen/screen-styler.js +9 -7
- package/dist/app/screen/scroll-controller.d.ts +11 -9
- package/dist/app/screen/scroll-controller.js +50 -45
- package/dist/app/session/lazy-session-manager.d.ts +11 -0
- package/dist/app/session/lazy-session-manager.js +539 -0
- package/dist/app/session/pix-system-message.d.ts +16 -0
- package/dist/app/session/pix-system-message.js +64 -0
- package/dist/app/session/queued-message-controller.js +5 -1
- package/dist/app/session/session-event-controller.d.ts +11 -0
- package/dist/app/session/session-event-controller.js +58 -2
- package/dist/app/session/session-history.d.ts +18 -0
- package/dist/app/session/session-history.js +72 -3
- package/dist/app/session/session-lifecycle-controller.d.ts +6 -2
- package/dist/app/session/session-lifecycle-controller.js +7 -2
- package/dist/app/session/tabs-controller.d.ts +13 -1
- package/dist/app/session/tabs-controller.js +248 -27
- package/dist/app/terminal/nerd-font-controller.d.ts +16 -0
- package/dist/app/terminal/nerd-font-controller.js +20 -12
- package/dist/app/todo/todo-model.d.ts +3 -1
- package/dist/app/todo/todo-model.js +14 -2
- package/dist/app/types.d.ts +5 -2
- package/dist/app/workspace/workspace-actions-controller.d.ts +2 -0
- package/dist/app/workspace/workspace-actions-controller.js +12 -0
- package/dist/config.d.ts +5 -1
- package/dist/config.js +73 -25
- package/dist/default-pix-config.js +9 -6
- package/dist/schemas/index.d.ts +5 -0
- package/dist/schemas/index.js +5 -0
- package/dist/schemas/pi-tools-suite-schema.d.ts +178 -0
- package/dist/schemas/pi-tools-suite-schema.js +219 -0
- package/dist/schemas/pix-schema.d.ts +66 -0
- package/dist/schemas/pix-schema.js +92 -0
- package/dist/terminal-width.d.ts +2 -0
- package/dist/terminal-width.js +134 -56
- package/external/pi-tools-suite/README.md +1 -0
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +12 -3
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +2 -4
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +2 -2
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +8 -2
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +102 -50
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +81 -2
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +29 -8
- package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +3 -0
- package/external/pi-tools-suite/src/config.ts +8 -0
- package/external/pi-tools-suite/src/dcp/index.ts +16 -1
- package/external/pi-tools-suite/src/dcp/state.ts +35 -0
- package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +4 -0
- package/external/pi-tools-suite/src/todo/index.ts +185 -13
- package/external/pi-tools-suite/src/todo/state/selectors.ts +4 -0
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +23 -10
- package/external/pi-tools-suite/src/todo/todo.ts +12 -11
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +33 -6
- package/external/pi-tools-suite/src/todo/tool/types.ts +9 -1
- package/external/pi-tools-suite/src/todo/view/format.ts +2 -1
- package/external/pi-tools-suite/src/tool-descriptions.ts +2 -1
- package/external/pi-tools-suite/src/usage/index.ts +5 -2
- package/external/pi-tools-suite/src/usage/lib/google.ts +6 -13
- package/package.json +12 -3
- package/schemas/pi-tools-suite.json +885 -0
- package/schemas/pix.json +302 -0
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
type ShellCommandDeps = {
|
|
3
|
+
spawn: typeof spawn;
|
|
4
|
+
waitForReturnToPix: () => Promise<void>;
|
|
5
|
+
};
|
|
6
|
+
export declare function setShellCommandTestDeps(overrides: Partial<ShellCommandDeps>): () => void;
|
|
1
7
|
export type InteractiveShellCommandResult = {
|
|
2
8
|
exitCode: number | null;
|
|
3
9
|
signal: NodeJS.Signals | null;
|
|
@@ -25,3 +31,4 @@ export declare function shellCommandFromBangInput(text: string): string | undefi
|
|
|
25
31
|
export declare function runChatShellCommand(command: string, cwd: string, handlers?: ChatShellCommandHandlers): RunningChatShellCommand;
|
|
26
32
|
export declare function runInteractiveShellCommand(command: string, cwd: string): Promise<InteractiveShellCommandResult>;
|
|
27
33
|
export declare function formatShellCommandEntry(command: string, result: InteractiveShellCommandResult, prefix?: string): string;
|
|
34
|
+
export {};
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
let deps = { spawn, waitForReturnToPix: waitForReturnToPixImpl };
|
|
3
|
+
export function setShellCommandTestDeps(overrides) {
|
|
4
|
+
const previous = deps;
|
|
5
|
+
deps = { ...deps, ...overrides };
|
|
6
|
+
return () => {
|
|
7
|
+
deps = previous;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
2
10
|
export function bangShellCommandFromInput(text) {
|
|
3
11
|
const trimmed = text.trimStart();
|
|
4
12
|
if (!trimmed.startsWith("!"))
|
|
@@ -12,7 +20,7 @@ export function shellCommandFromBangInput(text) {
|
|
|
12
20
|
export function runChatShellCommand(command, cwd, handlers = {}) {
|
|
13
21
|
let child;
|
|
14
22
|
try {
|
|
15
|
-
child = spawn(command, {
|
|
23
|
+
child = deps.spawn(command, {
|
|
16
24
|
cwd,
|
|
17
25
|
env: process.env,
|
|
18
26
|
shell: shellOption(),
|
|
@@ -79,7 +87,7 @@ export async function runInteractiveShellCommand(command, cwd) {
|
|
|
79
87
|
try {
|
|
80
88
|
const result = await spawnShellCommand(command, cwd);
|
|
81
89
|
process.stdout.write(`\n[pix] ${formatInteractiveShellResult(result)}\n`);
|
|
82
|
-
await waitForReturnToPix();
|
|
90
|
+
await deps.waitForReturnToPix();
|
|
83
91
|
return result;
|
|
84
92
|
}
|
|
85
93
|
finally {
|
|
@@ -93,7 +101,7 @@ export function formatShellCommandEntry(command, result, prefix = "!") {
|
|
|
93
101
|
}
|
|
94
102
|
async function spawnShellCommand(command, cwd) {
|
|
95
103
|
try {
|
|
96
|
-
const child = spawn(command, {
|
|
104
|
+
const child = deps.spawn(command, {
|
|
97
105
|
cwd,
|
|
98
106
|
env: process.env,
|
|
99
107
|
shell: shellOption(),
|
|
@@ -160,7 +168,7 @@ function formatInteractiveShellResult(result) {
|
|
|
160
168
|
return `terminated by ${result.signal}`;
|
|
161
169
|
return `exit ${result.exitCode ?? 0}`;
|
|
162
170
|
}
|
|
163
|
-
async function
|
|
171
|
+
async function waitForReturnToPixImpl() {
|
|
164
172
|
if (!process.stdin.isTTY || !process.stdin.readable)
|
|
165
173
|
return;
|
|
166
174
|
process.stdout.write("[pix] Press Enter to return to pix…");
|
|
@@ -7,10 +7,14 @@ export type ExtensionTerminalInputResult = {
|
|
|
7
7
|
};
|
|
8
8
|
export type ExtensionUiControllerHost = {
|
|
9
9
|
readonly theme: Theme;
|
|
10
|
+
activeExtensionUiScope?(): string | undefined;
|
|
10
11
|
isRunning(): boolean;
|
|
11
12
|
render(): void;
|
|
12
|
-
showToast(message: string, kind?: ToastKind
|
|
13
|
+
showToast(message: string, kind?: ToastKind, options?: {
|
|
14
|
+
scopeKey?: string;
|
|
15
|
+
}): void;
|
|
13
16
|
readonly toastNotifier: ToastNotifier;
|
|
17
|
+
toastNotifierForScope?(scopeKey: string | undefined): ToastNotifier;
|
|
14
18
|
readonly menuController: PixMenuController;
|
|
15
19
|
setStatus(status: string): void;
|
|
16
20
|
restoreSessionStatus(): void;
|
|
@@ -23,22 +27,24 @@ export declare class ExtensionUiController {
|
|
|
23
27
|
private readonly host;
|
|
24
28
|
private readonly extensionWidgets;
|
|
25
29
|
private readonly terminalInputHandlers;
|
|
26
|
-
private
|
|
27
|
-
private readonly aboveInputRenderer;
|
|
30
|
+
private readonly activeCustomUis;
|
|
28
31
|
constructor(host: ExtensionUiControllerHost);
|
|
29
32
|
get widgets(): ReadonlyMap<string, ExtensionWidgetRegistration>;
|
|
30
33
|
createExtensionTheme(): ExtensionWidgetTheme;
|
|
31
34
|
setWidget(key: string, content: unknown, options?: {
|
|
32
35
|
placement?: WidgetPlacement;
|
|
36
|
+
scopeKey?: string;
|
|
37
|
+
}): void;
|
|
38
|
+
clearWidgets(scopeKey?: string, options?: {
|
|
39
|
+
cancelCustomUi?: boolean;
|
|
33
40
|
}): void;
|
|
34
|
-
clearWidgets(): void;
|
|
35
41
|
suppressWidget(key: string): void;
|
|
36
42
|
handleTerminalInput(data: string): ExtensionTerminalInputResult;
|
|
37
43
|
renderActiveCustomUi(width: number): string[] | undefined;
|
|
38
44
|
activeCustomUiUsesEditor(): boolean;
|
|
39
45
|
handleCustomUiMouse(event: ExtensionInputMouseEvent): boolean;
|
|
40
46
|
widgetTuiHandle(): WidgetTuiHandle;
|
|
41
|
-
createExtensionUIContext(): PixExtensionUIContext;
|
|
47
|
+
createExtensionUIContext(scopeKey?: string): PixExtensionUIContext;
|
|
42
48
|
private setAboveInputWidget;
|
|
43
49
|
private clearAboveInputWidget;
|
|
44
50
|
private selectDialog;
|
|
@@ -53,5 +59,10 @@ export declare class ExtensionUiController {
|
|
|
53
59
|
private cancelActiveCustomUi;
|
|
54
60
|
private rejectActiveCustomUi;
|
|
55
61
|
private finishActiveCustomUi;
|
|
62
|
+
private activeCustomUiForActiveScope;
|
|
63
|
+
private activeScopeKey;
|
|
64
|
+
private normalizeScopeKey;
|
|
65
|
+
private scopedWidgetKey;
|
|
66
|
+
private unscopedWidgetKey;
|
|
56
67
|
private invalidateWidget;
|
|
57
68
|
}
|
|
@@ -6,20 +6,15 @@ export class ExtensionUiController {
|
|
|
6
6
|
host;
|
|
7
7
|
extensionWidgets = new Map();
|
|
8
8
|
terminalInputHandlers = new Set();
|
|
9
|
-
|
|
10
|
-
aboveInputRenderer = {
|
|
11
|
-
set: (key, content) => {
|
|
12
|
-
this.setAboveInputWidget(key, content);
|
|
13
|
-
},
|
|
14
|
-
clear: (key) => {
|
|
15
|
-
this.clearAboveInputWidget(key);
|
|
16
|
-
},
|
|
17
|
-
};
|
|
9
|
+
activeCustomUis = new Map();
|
|
18
10
|
constructor(host) {
|
|
19
11
|
this.host = host;
|
|
20
12
|
}
|
|
21
13
|
get widgets() {
|
|
22
|
-
|
|
14
|
+
const activeScopeKey = this.activeScopeKey();
|
|
15
|
+
return new Map([...this.extensionWidgets.entries()]
|
|
16
|
+
.filter(([, widget]) => widget.scopeKey === activeScopeKey)
|
|
17
|
+
.map(([scopedKey, widget]) => [this.unscopedWidgetKey(scopedKey, widget.scopeKey), widget]));
|
|
23
18
|
}
|
|
24
19
|
createExtensionTheme() {
|
|
25
20
|
const colors = this.host.theme.colors;
|
|
@@ -47,44 +42,54 @@ export class ExtensionUiController {
|
|
|
47
42
|
};
|
|
48
43
|
}
|
|
49
44
|
setWidget(key, content, options) {
|
|
50
|
-
const
|
|
45
|
+
const scopeKey = this.normalizeScopeKey(options?.scopeKey);
|
|
46
|
+
const scopedKey = this.scopedWidgetKey(scopeKey, key);
|
|
47
|
+
const existing = this.extensionWidgets.get(scopedKey);
|
|
51
48
|
if (existing)
|
|
52
49
|
this.invalidateWidget(existing);
|
|
53
50
|
if (content === undefined) {
|
|
54
|
-
this.extensionWidgets.delete(
|
|
51
|
+
this.extensionWidgets.delete(scopedKey);
|
|
55
52
|
if (this.host.isRunning())
|
|
56
53
|
this.host.render();
|
|
57
54
|
return;
|
|
58
55
|
}
|
|
59
56
|
if (!Array.isArray(content) && typeof content !== "function")
|
|
60
57
|
return;
|
|
61
|
-
this.extensionWidgets.set(
|
|
58
|
+
this.extensionWidgets.set(scopedKey, {
|
|
62
59
|
key,
|
|
60
|
+
scopeKey,
|
|
63
61
|
placement: options?.placement === "belowEditor" ? "belowEditor" : "aboveEditor",
|
|
64
62
|
content: content,
|
|
65
63
|
});
|
|
66
64
|
if (this.host.isRunning())
|
|
67
65
|
this.host.render();
|
|
68
66
|
}
|
|
69
|
-
clearWidgets() {
|
|
70
|
-
this.
|
|
71
|
-
|
|
67
|
+
clearWidgets(scopeKey, options = {}) {
|
|
68
|
+
const normalizedScopeKey = this.normalizeScopeKey(scopeKey);
|
|
69
|
+
if (options.cancelCustomUi !== false)
|
|
70
|
+
this.cancelActiveCustomUi(normalizedScopeKey);
|
|
71
|
+
for (const [key, widget] of this.extensionWidgets.entries()) {
|
|
72
|
+
if (widget.scopeKey !== normalizedScopeKey)
|
|
73
|
+
continue;
|
|
72
74
|
this.invalidateWidget(widget);
|
|
73
|
-
|
|
75
|
+
this.extensionWidgets.delete(key);
|
|
76
|
+
}
|
|
74
77
|
}
|
|
75
78
|
suppressWidget(key) {
|
|
76
|
-
const
|
|
79
|
+
const scopedKey = this.scopedWidgetKey(this.activeScopeKey(), key);
|
|
80
|
+
const widget = this.extensionWidgets.get(scopedKey);
|
|
77
81
|
if (!widget)
|
|
78
82
|
return;
|
|
79
83
|
this.invalidateWidget(widget);
|
|
80
|
-
this.extensionWidgets.delete(
|
|
84
|
+
this.extensionWidgets.delete(scopedKey);
|
|
81
85
|
}
|
|
82
86
|
handleTerminalInput(data) {
|
|
83
|
-
|
|
87
|
+
const active = this.activeCustomUiForActiveScope();
|
|
88
|
+
if (active) {
|
|
84
89
|
if (data === "\u0003")
|
|
85
90
|
return { consume: false };
|
|
86
91
|
try {
|
|
87
|
-
const result =
|
|
92
|
+
const result = active.component.handleInput?.(data);
|
|
88
93
|
if (result && typeof result === "object") {
|
|
89
94
|
return {
|
|
90
95
|
consume: result.consume !== false,
|
|
@@ -98,7 +103,10 @@ export class ExtensionUiController {
|
|
|
98
103
|
return { consume: true };
|
|
99
104
|
}
|
|
100
105
|
let current = data;
|
|
101
|
-
|
|
106
|
+
const activeScopeKey = this.activeScopeKey();
|
|
107
|
+
for (const { scopeKey, handler } of [...this.terminalInputHandlers]) {
|
|
108
|
+
if (scopeKey !== activeScopeKey)
|
|
109
|
+
continue;
|
|
102
110
|
const result = handler(current);
|
|
103
111
|
if (result?.data !== undefined)
|
|
104
112
|
current = result.data;
|
|
@@ -108,7 +116,7 @@ export class ExtensionUiController {
|
|
|
108
116
|
return current === data ? { consume: false } : { consume: false, data: current };
|
|
109
117
|
}
|
|
110
118
|
renderActiveCustomUi(width) {
|
|
111
|
-
const active = this.
|
|
119
|
+
const active = this.activeCustomUiForActiveScope();
|
|
112
120
|
if (!active)
|
|
113
121
|
return undefined;
|
|
114
122
|
try {
|
|
@@ -119,7 +127,7 @@ export class ExtensionUiController {
|
|
|
119
127
|
}
|
|
120
128
|
}
|
|
121
129
|
activeCustomUiUsesEditor() {
|
|
122
|
-
const active = this.
|
|
130
|
+
const active = this.activeCustomUiForActiveScope();
|
|
123
131
|
if (!active)
|
|
124
132
|
return false;
|
|
125
133
|
try {
|
|
@@ -130,7 +138,7 @@ export class ExtensionUiController {
|
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
handleCustomUiMouse(event) {
|
|
133
|
-
const active = this.
|
|
141
|
+
const active = this.activeCustomUiForActiveScope();
|
|
134
142
|
if (!active)
|
|
135
143
|
return false;
|
|
136
144
|
try {
|
|
@@ -142,13 +150,14 @@ export class ExtensionUiController {
|
|
|
142
150
|
}
|
|
143
151
|
}
|
|
144
152
|
widgetTuiHandle() {
|
|
153
|
+
const activeScopeToastNotifier = this.host.toastNotifierForScope?.(this.activeScopeKey()) ?? this.host.toastNotifier;
|
|
145
154
|
return {
|
|
146
155
|
requestRender: () => {
|
|
147
156
|
if (this.host.isRunning())
|
|
148
157
|
this.host.render();
|
|
149
158
|
},
|
|
150
|
-
showToast:
|
|
151
|
-
toast:
|
|
159
|
+
showToast: activeScopeToastNotifier.show,
|
|
160
|
+
toast: activeScopeToastNotifier,
|
|
152
161
|
showMenu: this.host.menuController.show,
|
|
153
162
|
menu: this.host.menuController,
|
|
154
163
|
pix: {
|
|
@@ -157,9 +166,11 @@ export class ExtensionUiController {
|
|
|
157
166
|
},
|
|
158
167
|
};
|
|
159
168
|
}
|
|
160
|
-
createExtensionUIContext() {
|
|
169
|
+
createExtensionUIContext(scopeKey) {
|
|
170
|
+
const contextScopeKey = this.normalizeScopeKey(scopeKey);
|
|
171
|
+
const scopedToastNotifier = this.host.toastNotifierForScope?.(contextScopeKey) ?? this.host.toastNotifier;
|
|
161
172
|
const notify = (message, type) => {
|
|
162
|
-
this.host.showToast(message, isToastKind(type) ? type : "info");
|
|
173
|
+
this.host.showToast(message, isToastKind(type) ? type : "info", { scopeKey: contextScopeKey });
|
|
163
174
|
};
|
|
164
175
|
const extensionTheme = this.createExtensionTheme();
|
|
165
176
|
const renderIfRunning = () => {
|
|
@@ -169,17 +180,24 @@ export class ExtensionUiController {
|
|
|
169
180
|
return {
|
|
170
181
|
select: async (title, options, opts) => await this.selectDialog(title, options, opts),
|
|
171
182
|
confirm: async (title, message, opts) => await this.confirmDialog(title, message, opts),
|
|
172
|
-
input: async (title, placeholder, opts) => await this.inputDialog(title, placeholder, opts),
|
|
183
|
+
input: async (title, placeholder, opts) => await this.inputDialog(title, placeholder, opts, contextScopeKey),
|
|
173
184
|
notify,
|
|
174
|
-
toast:
|
|
175
|
-
aboveInput:
|
|
185
|
+
toast: scopedToastNotifier,
|
|
186
|
+
aboveInput: {
|
|
187
|
+
set: (key, content) => {
|
|
188
|
+
this.setAboveInputWidget(key, content, contextScopeKey);
|
|
189
|
+
},
|
|
190
|
+
clear: (key) => {
|
|
191
|
+
this.clearAboveInputWidget(key, contextScopeKey);
|
|
192
|
+
},
|
|
193
|
+
},
|
|
176
194
|
renderAboveInput: (key, content) => {
|
|
177
|
-
this.setAboveInputWidget(key, content);
|
|
195
|
+
this.setAboveInputWidget(key, content, contextScopeKey);
|
|
178
196
|
},
|
|
179
197
|
showMenu: this.host.menuController.show,
|
|
180
198
|
menu: this.host.menuController,
|
|
181
199
|
onTerminalInput: (handler) => {
|
|
182
|
-
const terminalInputHandler = handler;
|
|
200
|
+
const terminalInputHandler = { scopeKey: contextScopeKey, handler: handler };
|
|
183
201
|
this.terminalInputHandlers.add(terminalInputHandler);
|
|
184
202
|
return () => {
|
|
185
203
|
this.terminalInputHandlers.delete(terminalInputHandler);
|
|
@@ -187,13 +205,13 @@ export class ExtensionUiController {
|
|
|
187
205
|
},
|
|
188
206
|
setStatus: (_key, text) => {
|
|
189
207
|
if (text)
|
|
190
|
-
this.host.showToast(text, "info");
|
|
208
|
+
this.host.showToast(text, "info", { scopeKey: contextScopeKey });
|
|
191
209
|
this.host.restoreSessionStatus();
|
|
192
210
|
renderIfRunning();
|
|
193
211
|
},
|
|
194
212
|
setWorkingMessage: (message) => {
|
|
195
213
|
if (message)
|
|
196
|
-
this.host.showToast(message, "info");
|
|
214
|
+
this.host.showToast(message, "info", { scopeKey: contextScopeKey });
|
|
197
215
|
this.host.restoreSessionStatus();
|
|
198
216
|
renderIfRunning();
|
|
199
217
|
},
|
|
@@ -201,7 +219,7 @@ export class ExtensionUiController {
|
|
|
201
219
|
setWorkingIndicator: () => undefined,
|
|
202
220
|
setHiddenThinkingLabel: () => undefined,
|
|
203
221
|
setWidget: ((key, content, options) => {
|
|
204
|
-
this.setWidget(key, content, options);
|
|
222
|
+
this.setWidget(key, content, { ...options, scopeKey: contextScopeKey });
|
|
205
223
|
}),
|
|
206
224
|
setFooter: () => undefined,
|
|
207
225
|
setHeader: () => undefined,
|
|
@@ -209,7 +227,7 @@ export class ExtensionUiController {
|
|
|
209
227
|
process.title = title;
|
|
210
228
|
renderIfRunning();
|
|
211
229
|
},
|
|
212
|
-
custom: (async (factory) => await this.showCustomUi(factory)),
|
|
230
|
+
custom: (async (factory) => await this.showCustomUi(factory, { scopeKey: contextScopeKey })),
|
|
213
231
|
pasteToEditor: (text) => {
|
|
214
232
|
this.host.setInput(text);
|
|
215
233
|
renderIfRunning();
|
|
@@ -219,7 +237,7 @@ export class ExtensionUiController {
|
|
|
219
237
|
renderIfRunning();
|
|
220
238
|
},
|
|
221
239
|
getEditorText: () => this.host.getInput(),
|
|
222
|
-
editor: async (title, prefill) => await this.editorDialog(title, prefill),
|
|
240
|
+
editor: async (title, prefill) => await this.editorDialog(title, prefill, contextScopeKey),
|
|
223
241
|
addAutocompleteProvider: () => undefined,
|
|
224
242
|
setEditorComponent: () => undefined,
|
|
225
243
|
getEditorComponent: () => undefined,
|
|
@@ -241,11 +259,11 @@ export class ExtensionUiController {
|
|
|
241
259
|
},
|
|
242
260
|
};
|
|
243
261
|
}
|
|
244
|
-
setAboveInputWidget(key, content) {
|
|
245
|
-
this.setWidget(key, content, { placement: "aboveEditor" });
|
|
262
|
+
setAboveInputWidget(key, content, scopeKey = this.activeScopeKey()) {
|
|
263
|
+
this.setWidget(key, content, { placement: "aboveEditor", scopeKey });
|
|
246
264
|
}
|
|
247
|
-
clearAboveInputWidget(key) {
|
|
248
|
-
this.setWidget(key, undefined, { placement: "aboveEditor" });
|
|
265
|
+
clearAboveInputWidget(key, scopeKey = this.activeScopeKey()) {
|
|
266
|
+
this.setWidget(key, undefined, { placement: "aboveEditor", scopeKey });
|
|
249
267
|
}
|
|
250
268
|
async selectDialog(title, options, opts) {
|
|
251
269
|
if (opts?.signal?.aborted)
|
|
@@ -265,7 +283,7 @@ export class ExtensionUiController {
|
|
|
265
283
|
});
|
|
266
284
|
return selected === true;
|
|
267
285
|
}
|
|
268
|
-
async inputDialog(title, placeholder, opts) {
|
|
286
|
+
async inputDialog(title, placeholder, opts, scopeKey = this.activeScopeKey()) {
|
|
269
287
|
if (opts?.signal?.aborted)
|
|
270
288
|
return undefined;
|
|
271
289
|
return await this.editorBackedDialog({
|
|
@@ -274,21 +292,21 @@ export class ExtensionUiController {
|
|
|
274
292
|
mode: "input",
|
|
275
293
|
...(placeholder === undefined ? {} : { placeholder }),
|
|
276
294
|
...(opts === undefined ? {} : { opts }),
|
|
277
|
-
});
|
|
295
|
+
}, scopeKey);
|
|
278
296
|
}
|
|
279
|
-
async editorDialog(title, prefill = "") {
|
|
297
|
+
async editorDialog(title, prefill = "", scopeKey = this.activeScopeKey()) {
|
|
280
298
|
return await this.editorBackedDialog({
|
|
281
299
|
title,
|
|
282
300
|
initialValue: prefill,
|
|
283
301
|
mode: "editor",
|
|
284
|
-
});
|
|
302
|
+
}, scopeKey);
|
|
285
303
|
}
|
|
286
|
-
async editorBackedDialog(options) {
|
|
304
|
+
async editorBackedDialog(options, scopeKey = this.activeScopeKey()) {
|
|
287
305
|
if (options.opts?.signal?.aborted)
|
|
288
306
|
return undefined;
|
|
289
307
|
if (!this.host.isRunning())
|
|
290
308
|
return undefined;
|
|
291
|
-
if (this.
|
|
309
|
+
if (this.activeCustomUis.has(scopeKey))
|
|
292
310
|
throw new Error("Another extension custom UI is already active.");
|
|
293
311
|
const savedInput = this.host.getInput();
|
|
294
312
|
this.host.setInput(options.initialValue);
|
|
@@ -315,9 +333,9 @@ export class ExtensionUiController {
|
|
|
315
333
|
return { consume: false, data };
|
|
316
334
|
},
|
|
317
335
|
};
|
|
318
|
-
}, { savedInput });
|
|
336
|
+
}, { savedInput, scopeKey });
|
|
319
337
|
return await this.withDialogAutoDismiss(promise, options.opts, () => {
|
|
320
|
-
this.cancelActiveCustomUi();
|
|
338
|
+
this.cancelActiveCustomUi(scopeKey);
|
|
321
339
|
});
|
|
322
340
|
}
|
|
323
341
|
renderEditorBackedDialog(options, width) {
|
|
@@ -356,7 +374,8 @@ export class ExtensionUiController {
|
|
|
356
374
|
async showCustomUi(factory, options = {}) {
|
|
357
375
|
if (!this.host.isRunning())
|
|
358
376
|
return undefined;
|
|
359
|
-
|
|
377
|
+
const scopeKey = this.normalizeScopeKey(options.scopeKey);
|
|
378
|
+
if (this.activeCustomUis.has(scopeKey))
|
|
360
379
|
throw new Error("Another extension custom UI is already active.");
|
|
361
380
|
const savedInput = options.savedInput ?? this.host.getInput();
|
|
362
381
|
return await new Promise((resolve, reject) => {
|
|
@@ -365,7 +384,7 @@ export class ExtensionUiController {
|
|
|
365
384
|
if (settled)
|
|
366
385
|
return;
|
|
367
386
|
settled = true;
|
|
368
|
-
this.finishActiveCustomUi(value, { resolve: true });
|
|
387
|
+
this.finishActiveCustomUi(scopeKey, value, { resolve: true });
|
|
369
388
|
resolve(value);
|
|
370
389
|
};
|
|
371
390
|
void (async () => {
|
|
@@ -375,8 +394,9 @@ export class ExtensionUiController {
|
|
|
375
394
|
component.dispose?.();
|
|
376
395
|
return;
|
|
377
396
|
}
|
|
378
|
-
this.
|
|
397
|
+
this.activeCustomUis.set(scopeKey, {
|
|
379
398
|
key: CUSTOM_UI_WIDGET_KEY,
|
|
399
|
+
scopeKey,
|
|
380
400
|
component,
|
|
381
401
|
savedInput,
|
|
382
402
|
resolve: (value) => {
|
|
@@ -391,7 +411,7 @@ export class ExtensionUiController {
|
|
|
391
411
|
settled = true;
|
|
392
412
|
reject(error);
|
|
393
413
|
},
|
|
394
|
-
};
|
|
414
|
+
});
|
|
395
415
|
if (this.host.isRunning())
|
|
396
416
|
this.host.render();
|
|
397
417
|
}
|
|
@@ -404,17 +424,20 @@ export class ExtensionUiController {
|
|
|
404
424
|
})();
|
|
405
425
|
});
|
|
406
426
|
}
|
|
407
|
-
cancelActiveCustomUi() {
|
|
408
|
-
this.finishActiveCustomUi(undefined, { resolve: true });
|
|
427
|
+
cancelActiveCustomUi(scopeKey = this.activeScopeKey()) {
|
|
428
|
+
this.finishActiveCustomUi(scopeKey, undefined, { resolve: true });
|
|
409
429
|
}
|
|
410
430
|
rejectActiveCustomUi(error) {
|
|
411
|
-
this.
|
|
431
|
+
const active = this.activeCustomUiForActiveScope();
|
|
432
|
+
if (!active)
|
|
433
|
+
return;
|
|
434
|
+
this.finishActiveCustomUi(active.scopeKey, error, { resolve: false });
|
|
412
435
|
}
|
|
413
|
-
finishActiveCustomUi(value, options) {
|
|
414
|
-
const active = this.
|
|
436
|
+
finishActiveCustomUi(scopeKey, value, options) {
|
|
437
|
+
const active = this.activeCustomUis.get(scopeKey);
|
|
415
438
|
if (!active)
|
|
416
439
|
return;
|
|
417
|
-
this.
|
|
440
|
+
this.activeCustomUis.delete(scopeKey);
|
|
418
441
|
if (this.host.getInput() !== active.savedInput)
|
|
419
442
|
this.host.setInput(active.savedInput);
|
|
420
443
|
try {
|
|
@@ -436,6 +459,21 @@ export class ExtensionUiController {
|
|
|
436
459
|
if (this.host.isRunning())
|
|
437
460
|
this.host.render();
|
|
438
461
|
}
|
|
462
|
+
activeCustomUiForActiveScope() {
|
|
463
|
+
return this.activeCustomUis.get(this.activeScopeKey());
|
|
464
|
+
}
|
|
465
|
+
activeScopeKey() {
|
|
466
|
+
return this.normalizeScopeKey(this.host.activeExtensionUiScope?.());
|
|
467
|
+
}
|
|
468
|
+
normalizeScopeKey(scopeKey) {
|
|
469
|
+
return scopeKey ?? "";
|
|
470
|
+
}
|
|
471
|
+
scopedWidgetKey(scopeKey, key) {
|
|
472
|
+
return `${scopeKey.length}:${scopeKey}:${key}`;
|
|
473
|
+
}
|
|
474
|
+
unscopedWidgetKey(scopedKey, scopeKey) {
|
|
475
|
+
return scopedKey.slice(`${scopeKey.length}:${scopeKey}:`.length);
|
|
476
|
+
}
|
|
439
477
|
invalidateWidget(widget) {
|
|
440
478
|
try {
|
|
441
479
|
widget.component?.dispose?.();
|
package/dist/app/icons.d.ts
CHANGED
package/dist/app/icons.js
CHANGED
|
@@ -20,6 +20,7 @@ const NERD_FONT_ICONS = {
|
|
|
20
20
|
info: "\u{f02fc}",
|
|
21
21
|
microphone: "\u{f036c}",
|
|
22
22
|
plus: "\u{f0415}",
|
|
23
|
+
pause: "\u{f03e4}",
|
|
23
24
|
record: "\u{f044a}",
|
|
24
25
|
refresh: "\u{f0450}",
|
|
25
26
|
volumeHigh: "\u{f057e}",
|
|
@@ -45,6 +46,7 @@ const FALLBACK_ICONS = {
|
|
|
45
46
|
info: "i",
|
|
46
47
|
microphone: "m",
|
|
47
48
|
plus: "+",
|
|
49
|
+
pause: "⏸",
|
|
48
50
|
record: "●",
|
|
49
51
|
refresh: "↻",
|
|
50
52
|
volumeHigh: "♪",
|
|
@@ -39,6 +39,7 @@ export declare class AppInputActionController {
|
|
|
39
39
|
queueInputFromEditor(): Promise<void>;
|
|
40
40
|
handleInterrupt(): Promise<void>;
|
|
41
41
|
handleEscape(): Promise<void>;
|
|
42
|
+
private closeActiveGlobalUi;
|
|
42
43
|
private abortStreamingSession;
|
|
43
44
|
private restoreSessionState;
|
|
44
45
|
private sessionActivity;
|
|
@@ -63,6 +63,8 @@ export class AppInputActionController {
|
|
|
63
63
|
await this.host.stop();
|
|
64
64
|
}
|
|
65
65
|
async handleEscape() {
|
|
66
|
+
if (this.closeActiveGlobalUi())
|
|
67
|
+
return;
|
|
66
68
|
const session = this.host.runtime()?.session;
|
|
67
69
|
if (session?.isCompacting) {
|
|
68
70
|
this.host.setStatus("aborting compaction");
|
|
@@ -76,8 +78,12 @@ export class AppInputActionController {
|
|
|
76
78
|
await this.abortStreamingSession(runtime, { stopIfAlreadyAborting: false });
|
|
77
79
|
return;
|
|
78
80
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
}
|
|
82
|
+
closeActiveGlobalUi() {
|
|
83
|
+
if (!this.popupMenus.syncActivePopupMenu())
|
|
84
|
+
return false;
|
|
85
|
+
this.popupMenus.cancelActivePopupMenu();
|
|
86
|
+
return true;
|
|
81
87
|
}
|
|
82
88
|
async abortStreamingSession(runtime, options) {
|
|
83
89
|
const session = runtime.session;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import { createAgentSessionFromServices, createAgentSessionServices, SessionManager, type AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { PromptEnhancerConfig } from "../../config.js";
|
|
3
3
|
import type { InputEditor } from "../../input-editor.js";
|
|
4
4
|
import type { ToastNotifier } from "../../ui.js";
|
|
@@ -19,6 +19,12 @@ export type AppPromptEnhancerControllerHost = {
|
|
|
19
19
|
render(): void;
|
|
20
20
|
};
|
|
21
21
|
type PromptEnhanceRunner = typeof enhancePromptWithPi;
|
|
22
|
+
type PromptEnhancerPiDeps = {
|
|
23
|
+
createAgentSessionServices: typeof createAgentSessionServices;
|
|
24
|
+
createAgentSessionFromServices: typeof createAgentSessionFromServices;
|
|
25
|
+
sessionManagerInMemory: typeof SessionManager.inMemory;
|
|
26
|
+
};
|
|
27
|
+
export declare function setPromptEnhancerPiTestDeps(overrides?: Partial<PromptEnhancerPiDeps>): void;
|
|
22
28
|
type AppPromptEnhancerControllerOptions = {
|
|
23
29
|
enhancePromptWithPi?: PromptEnhanceRunner;
|
|
24
30
|
};
|
|
@@ -11,6 +11,15 @@ Do not add unsupported assumptions.
|
|
|
11
11
|
Add useful constraints, acceptance criteria, and context requests when helpful.
|
|
12
12
|
Output only the improved prompt. No commentary, no markdown fences.`;
|
|
13
13
|
const PROMPT_ENHANCER_MIN_TEXT_LENGTH = 3;
|
|
14
|
+
const defaultPromptEnhancerPiDeps = {
|
|
15
|
+
createAgentSessionServices,
|
|
16
|
+
createAgentSessionFromServices,
|
|
17
|
+
sessionManagerInMemory: SessionManager.inMemory,
|
|
18
|
+
};
|
|
19
|
+
let promptEnhancerPiDeps = defaultPromptEnhancerPiDeps;
|
|
20
|
+
export function setPromptEnhancerPiTestDeps(overrides) {
|
|
21
|
+
promptEnhancerPiDeps = overrides ? { ...defaultPromptEnhancerPiDeps, ...overrides } : defaultPromptEnhancerPiDeps;
|
|
22
|
+
}
|
|
14
23
|
export class AppPromptEnhancerController {
|
|
15
24
|
host;
|
|
16
25
|
enhancing = false;
|
|
@@ -115,7 +124,7 @@ export function promptEnhancerTextIsSufficient(text) {
|
|
|
115
124
|
}
|
|
116
125
|
async function enhancePromptWithPi(runtime, draft, config) {
|
|
117
126
|
const parsedModel = parseModelRef(config.modelRef);
|
|
118
|
-
const services = await createAgentSessionServices({
|
|
127
|
+
const services = await promptEnhancerPiDeps.createAgentSessionServices({
|
|
119
128
|
cwd: runtime.cwd,
|
|
120
129
|
agentDir: runtime.services.agentDir,
|
|
121
130
|
authStorage: runtime.services.authStorage,
|
|
@@ -135,9 +144,9 @@ async function enhancePromptWithPi(runtime, draft, config) {
|
|
|
135
144
|
if (!model) {
|
|
136
145
|
throw new Error(modelNotFoundMessage(parsedModel.provider, parsedModel.modelId, services.modelRegistry.getAll()));
|
|
137
146
|
}
|
|
138
|
-
const { session } = await createAgentSessionFromServices({
|
|
147
|
+
const { session } = await promptEnhancerPiDeps.createAgentSessionFromServices({
|
|
139
148
|
services,
|
|
140
|
-
sessionManager:
|
|
149
|
+
sessionManager: promptEnhancerPiDeps.sessionManagerInMemory(runtime.cwd),
|
|
141
150
|
model,
|
|
142
151
|
thinkingLevel: parsedModel.thinkingLevel ?? "minimal",
|
|
143
152
|
noTools: "all",
|