pi-ui-extend 0.1.15 → 0.1.17
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.d.ts +2 -0
- package/dist/app/app.js +21 -6
- package/dist/app/commands/command-controller.js +1 -0
- package/dist/app/commands/command-host.d.ts +2 -0
- package/dist/app/commands/command-navigation-actions.d.ts +9 -0
- package/dist/app/commands/command-navigation-actions.js +62 -3
- package/dist/app/commands/command-registry.d.ts +1 -0
- package/dist/app/commands/command-registry.js +8 -0
- package/dist/app/constants.d.ts +0 -1
- package/dist/app/constants.js +0 -1
- 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 +5 -4
- package/dist/app/popup/menu-items-controller.d.ts +2 -0
- package/dist/app/popup/menu-items-controller.js +37 -14
- package/dist/app/popup/popup-menu-controller.js +30 -5
- package/dist/app/rendering/editor-panels.js +20 -9
- package/dist/app/rendering/popup-menu-renderer.d.ts +12 -0
- package/dist/app/rendering/popup-menu-renderer.js +151 -53
- package/dist/app/rendering/render-controller.js +25 -15
- package/dist/app/rendering/render-text.js +5 -2
- package/dist/app/rendering/status-line-renderer.d.ts +7 -0
- package/dist/app/rendering/status-line-renderer.js +191 -94
- package/dist/app/rendering/toast-controller.d.ts +1 -0
- package/dist/app/rendering/toast-controller.js +17 -0
- package/dist/app/screen/mouse-controller.js +4 -4
- package/dist/app/screen/scroll-controller.d.ts +1 -0
- package/dist/app/screen/scroll-controller.js +6 -0
- package/dist/app/screen/status-controller.js +2 -1
- package/dist/app/session/request-history.d.ts +4 -0
- package/dist/app/session/request-history.js +11 -0
- package/dist/app/session/session-search.js +10 -0
- package/dist/app/session/tabs-controller.d.ts +4 -4
- package/dist/app/session/tabs-controller.js +64 -6
- package/dist/app/todo/todo-model.d.ts +2 -2
- package/dist/app/todo/todo-model.js +15 -17
- package/dist/app/types.d.ts +12 -4
- package/dist/config.d.ts +1 -0
- package/dist/config.js +10 -1
- package/dist/default-pix-config.js +2 -0
- package/dist/fuzzy.d.ts +2 -0
- package/dist/fuzzy.js +27 -7
- package/dist/input-editor.d.ts +9 -0
- package/dist/input-editor.js +52 -0
- package/dist/schemas/pix-schema.d.ts +1 -0
- package/dist/schemas/pix-schema.js +1 -0
- package/dist/theme.js +6 -6
- package/dist/ui.d.ts +8 -0
- package/external/pi-tools-suite/README.md +2 -2
- package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +40 -5
- package/external/pi-tools-suite/src/antigravity-auth/commands.ts +1 -37
- package/external/pi-tools-suite/src/antigravity-auth/constants.ts +0 -2
- package/external/pi-tools-suite/src/antigravity-auth/index.ts +3 -16
- package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +33 -17
- package/external/pi-tools-suite/src/antigravity-auth/status.ts +1 -1
- package/external/pi-tools-suite/src/antigravity-auth/stream.ts +4 -12
- package/external/pi-tools-suite/src/antigravity-auth/types.ts +21 -0
- package/external/pi-tools-suite/src/todo/index.ts +3 -64
- package/external/pi-tools-suite/src/todo/state/persistence.ts +0 -1
- package/external/pi-tools-suite/src/todo/state/state-reducer.ts +7 -37
- package/external/pi-tools-suite/src/todo/todo.ts +2 -18
- package/external/pi-tools-suite/src/todo/tool/response-envelope.ts +2 -11
- package/external/pi-tools-suite/src/todo/tool/types.ts +0 -29
- package/external/pi-tools-suite/src/todo/view/format.ts +1 -3
- package/external/pi-tools-suite/src/tool-descriptions.ts +5 -4
- package/external/pi-tools-suite/src/usage/lib/google.ts +50 -30
- package/external/pi-tools-suite/src/usage/lib/types.ts +12 -2
- package/package.json +1 -1
- package/schemas/pix.json +5 -0
package/dist/app/app.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare class PiUiExtendApp {
|
|
|
44
44
|
private readonly extensionShutdownHandler;
|
|
45
45
|
private runtime;
|
|
46
46
|
private readonly inputEditor;
|
|
47
|
+
private lastInputEditorContentVersion;
|
|
47
48
|
private readonly requestHistory;
|
|
48
49
|
/** Shortcut: get/set the editor text as a plain string. */
|
|
49
50
|
private get input();
|
|
@@ -101,6 +102,7 @@ export declare class PiUiExtendApp {
|
|
|
101
102
|
private clearToastTimers;
|
|
102
103
|
private render;
|
|
103
104
|
private scheduleRender;
|
|
105
|
+
private syncScrollAfterInputEditorChange;
|
|
104
106
|
private renderStatusLine;
|
|
105
107
|
private terminalColumns;
|
|
106
108
|
private terminalRows;
|
package/dist/app/app.js
CHANGED
|
@@ -110,6 +110,7 @@ export class PiUiExtendApp {
|
|
|
110
110
|
extensionShutdownHandler = () => { };
|
|
111
111
|
runtime;
|
|
112
112
|
inputEditor = new InputEditor();
|
|
113
|
+
lastInputEditorContentVersion = this.inputEditor.contentVersion;
|
|
113
114
|
requestHistory;
|
|
114
115
|
/** Shortcut: get/set the editor text as a plain string. */
|
|
115
116
|
get input() { return this.inputEditor.text; }
|
|
@@ -126,6 +127,8 @@ export class PiUiExtendApp {
|
|
|
126
127
|
constructor(options) {
|
|
127
128
|
this.options = options;
|
|
128
129
|
this.theme = THEMES[options.themeName];
|
|
130
|
+
this.pixConfig = loadPixConfig(this.options.cwd);
|
|
131
|
+
setAppIconTheme(this.pixConfig.iconTheme.name);
|
|
129
132
|
const app = this;
|
|
130
133
|
this.blinkController = new AppBlinkController({
|
|
131
134
|
render: () => this.render(),
|
|
@@ -157,6 +160,7 @@ export class PiUiExtendApp {
|
|
|
157
160
|
});
|
|
158
161
|
this.tabsController = new AppTabsController({
|
|
159
162
|
options: this.options,
|
|
163
|
+
maxProjectSessions: this.pixConfig.maxProjectSessions,
|
|
160
164
|
blinkController: this.blinkController,
|
|
161
165
|
runtime: () => this.runtime,
|
|
162
166
|
createRuntimeForNewSession: () => this.createRuntime(newTabRuntimeOptions(this.options)),
|
|
@@ -175,8 +179,8 @@ export class PiUiExtendApp {
|
|
|
175
179
|
loadSessionHistory: () => this.loadSessionHistory(),
|
|
176
180
|
loadSessionHistoryAsync: (options) => this.loadSessionHistoryAsync(options),
|
|
177
181
|
syncUserSessionEntryMetadata: () => this.workspaceActions.syncUserSessionEntryMetadata(),
|
|
178
|
-
captureInputState: () =>
|
|
179
|
-
restoreInputState: (state) => this.restoreTabInputState(state
|
|
182
|
+
captureInputState: () => this.inputEditor.draftState,
|
|
183
|
+
restoreInputState: (state) => this.restoreTabInputState(state),
|
|
180
184
|
closeMenusForTabSwitch: () => this.popupMenus.closeMenusForTabSwitch(),
|
|
181
185
|
captureDeferredUserMessages: () => this.queuedMessages.captureDeferredUserMessages(),
|
|
182
186
|
restoreDeferredUserMessages: (messages) => this.queuedMessages.restoreDeferredUserMessages(messages),
|
|
@@ -184,8 +188,6 @@ export class PiUiExtendApp {
|
|
|
184
188
|
showToast: (message, kind) => this.showToast(message, kind),
|
|
185
189
|
render: () => this.render(),
|
|
186
190
|
});
|
|
187
|
-
this.pixConfig = loadPixConfig(this.options.cwd);
|
|
188
|
-
setAppIconTheme(this.pixConfig.iconTheme.name);
|
|
189
191
|
this.terminalBellSoundController = new TerminalBellSoundController();
|
|
190
192
|
this.promptEnhancer = new AppPromptEnhancerController({
|
|
191
193
|
runtime: () => this.runtime,
|
|
@@ -224,10 +226,12 @@ export class PiUiExtendApp {
|
|
|
224
226
|
const popupMenuRenderer = new PopupMenuRenderer({
|
|
225
227
|
theme: this.theme,
|
|
226
228
|
screenStyler: this.screenStyler,
|
|
229
|
+
modelColors: this.pixConfig.modelColors,
|
|
227
230
|
get entries() { return app.entries; },
|
|
228
231
|
get session() { return app.runtime?.session; },
|
|
229
232
|
get resumeLoading() { return app.resumeLoading; },
|
|
230
233
|
get resumeSessionCount() { return app.resumeSessions.length; },
|
|
234
|
+
get userMessageJumpLoading() { return app.menuItems.isUserMessageJumpLoading(); },
|
|
231
235
|
});
|
|
232
236
|
this.popupMenus = new AppPopupMenuController({
|
|
233
237
|
get entries() { return app.entries; },
|
|
@@ -430,6 +434,7 @@ export class PiUiExtendApp {
|
|
|
430
434
|
this.commandController = new AppCommandController({
|
|
431
435
|
options: this.options,
|
|
432
436
|
runtime: () => this.runtime,
|
|
437
|
+
requestHistory: () => this.requestHistory,
|
|
433
438
|
getInput: () => this.input,
|
|
434
439
|
setInput: (value) => this.setInput(value),
|
|
435
440
|
promptEnhancerModelRef: () => this.pixConfig.promptEnhancer.modelRef,
|
|
@@ -614,6 +619,7 @@ export class PiUiExtendApp {
|
|
|
614
619
|
addEntry: (entry) => this.addEntry(entry),
|
|
615
620
|
addSessionAbortedEntry: () => this.sessionEvents.addSessionAbortedEntry(),
|
|
616
621
|
showToast: (message, kind) => this.showToast(message, kind),
|
|
622
|
+
dismissActiveDialog: () => this.toastController.dismissActiveDialog(),
|
|
617
623
|
stopVoiceInput: () => this.voiceController.stopRecording(),
|
|
618
624
|
isShellCommandRunning: () => this.shellController.isRunning(),
|
|
619
625
|
runChatShellCommand: (command) => this.shellController.run(command),
|
|
@@ -814,10 +820,10 @@ export class PiUiExtendApp {
|
|
|
814
820
|
this.popupMenus.resetInputMenuDismissals();
|
|
815
821
|
this.autocompleteController.dispose();
|
|
816
822
|
}
|
|
817
|
-
restoreTabInputState(
|
|
823
|
+
restoreTabInputState(state) {
|
|
818
824
|
this.requestHistory.resetNavigation();
|
|
819
825
|
this.popupMenus.resetInputMenuDismissals();
|
|
820
|
-
this.inputEditor.
|
|
826
|
+
this.inputEditor.setDraftState(state);
|
|
821
827
|
this.autocompleteController.dispose();
|
|
822
828
|
}
|
|
823
829
|
async clearPersistedInputDraft() {
|
|
@@ -1007,6 +1013,7 @@ export class PiUiExtendApp {
|
|
|
1007
1013
|
this.scheduledRenderTimer = undefined;
|
|
1008
1014
|
}
|
|
1009
1015
|
this.autocompleteController.observeInput();
|
|
1016
|
+
this.syncScrollAfterInputEditorChange();
|
|
1010
1017
|
this.renderController.render();
|
|
1011
1018
|
}
|
|
1012
1019
|
scheduleRender() {
|
|
@@ -1014,10 +1021,18 @@ export class PiUiExtendApp {
|
|
|
1014
1021
|
return;
|
|
1015
1022
|
this.scheduledRenderTimer = setTimeout(() => {
|
|
1016
1023
|
this.scheduledRenderTimer = undefined;
|
|
1024
|
+
this.syncScrollAfterInputEditorChange();
|
|
1017
1025
|
this.renderController.render();
|
|
1018
1026
|
}, COALESCED_RENDER_DELAY_MS);
|
|
1019
1027
|
this.scheduledRenderTimer.unref?.();
|
|
1020
1028
|
}
|
|
1029
|
+
syncScrollAfterInputEditorChange() {
|
|
1030
|
+
const contentVersion = this.inputEditor.contentVersion;
|
|
1031
|
+
if (contentVersion === this.lastInputEditorContentVersion)
|
|
1032
|
+
return;
|
|
1033
|
+
this.lastInputEditorContentVersion = contentVersion;
|
|
1034
|
+
this.scrollController.scrollToBottom();
|
|
1035
|
+
}
|
|
1021
1036
|
renderStatusLine() {
|
|
1022
1037
|
this.renderController.renderStatusLine();
|
|
1023
1038
|
}
|
|
@@ -54,6 +54,7 @@ export class AppCommandController {
|
|
|
54
54
|
runCloneCommand: () => this.navigationActions.runCloneCommand(),
|
|
55
55
|
runTreeCommand: (argumentsText) => this.navigationActions.runTreeCommand(argumentsText),
|
|
56
56
|
runJumpCommand: (argumentsText) => this.navigationActions.runJumpCommand(argumentsText),
|
|
57
|
+
runHistoryCommand: (argumentsText) => this.navigationActions.runHistoryCommand(argumentsText),
|
|
57
58
|
runSearchCommand: (argumentsText) => this.navigationActions.runSearchCommand(argumentsText),
|
|
58
59
|
runUnsupportedBuiltinCommand: (commandName, message) => this.navigationActions.runUnsupportedBuiltinCommand(commandName, message),
|
|
59
60
|
runResumePathCommand: (sessionPath) => this.navigationActions.runResumePathCommand(sessionPath),
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { AgentSession, AgentSessionRuntime, SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { SessionSearchResult } from "../session/session-search.js";
|
|
3
|
+
import type { AppRequestHistory } from "../session/request-history.js";
|
|
3
4
|
import type { ActivePopupMenu, AppOptions, Entry, ModelMenuValue, PixMenuItem, PixMenuOptions, PopupMenuPlacement, ScopedSessionModel, SessionModel, ThinkingMenuValue } from "../types.js";
|
|
4
5
|
import type { ToastNotifier } from "../../ui.js";
|
|
5
6
|
export type DirectPopupMenu = Exclude<ActivePopupMenu, "slash">;
|
|
6
7
|
export type CommandControllerHost = {
|
|
7
8
|
readonly options: AppOptions;
|
|
8
9
|
runtime(): AgentSessionRuntime | undefined;
|
|
10
|
+
requestHistory(): AppRequestHistory;
|
|
9
11
|
getInput(): string;
|
|
10
12
|
setInput(value: string): void;
|
|
11
13
|
promptEnhancerModelRef(): string;
|
|
@@ -2,6 +2,14 @@ import type { SessionInfo } from "@earendil-works/pi-coding-agent";
|
|
|
2
2
|
import type { CommandControllerHost } from "./command-host.js";
|
|
3
3
|
import { type ResumeSessionLoaderOptions } from "../session/resume-session-loader.js";
|
|
4
4
|
import type { PopupMenuPlacement } from "../types.js";
|
|
5
|
+
export declare function formatHistoryMenuLabel(text: string): string;
|
|
6
|
+
export declare function historyHighlightRanges(ranges: readonly {
|
|
7
|
+
start: number;
|
|
8
|
+
end: number;
|
|
9
|
+
}[], text: string): {
|
|
10
|
+
start: number;
|
|
11
|
+
end: number;
|
|
12
|
+
}[];
|
|
5
13
|
export declare class NavigationCommandActions {
|
|
6
14
|
private readonly host;
|
|
7
15
|
private readonly resumeSessionLoader;
|
|
@@ -11,6 +19,7 @@ export declare class NavigationCommandActions {
|
|
|
11
19
|
runCloneCommand(): Promise<void>;
|
|
12
20
|
runTreeCommand(argumentsText: string): Promise<void>;
|
|
13
21
|
runJumpCommand(argumentsText: string): Promise<void>;
|
|
22
|
+
runHistoryCommand(argumentsText: string): Promise<void>;
|
|
14
23
|
runSearchCommand(argumentsText: string): Promise<void>;
|
|
15
24
|
runUnsupportedBuiltinCommand(commandName: string, message: string): Promise<void>;
|
|
16
25
|
runResumePathCommand(sessionPath: string): Promise<void>;
|
|
@@ -11,6 +11,24 @@ function nextTick() {
|
|
|
11
11
|
setImmediate(resolve);
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
|
+
export function formatHistoryMenuLabel(text) {
|
|
15
|
+
return sanitizeText(text).replace(/\n/g, " ↵ ");
|
|
16
|
+
}
|
|
17
|
+
export function historyHighlightRanges(ranges, text) {
|
|
18
|
+
return ranges.map((range) => ({
|
|
19
|
+
start: historyLabelIndex(range.start, text),
|
|
20
|
+
end: historyLabelIndex(range.end, text),
|
|
21
|
+
})).filter((range) => range.end > range.start);
|
|
22
|
+
}
|
|
23
|
+
function historyLabelIndex(index, text) {
|
|
24
|
+
const before = text.slice(0, Math.max(0, Math.min(index, text.length)));
|
|
25
|
+
const newlineCount = before.split("\n").length - 1;
|
|
26
|
+
return before.length + newlineCount * 2;
|
|
27
|
+
}
|
|
28
|
+
function formatHistoryMenuDescription(text) {
|
|
29
|
+
const lines = sanitizeText(text).split("\n");
|
|
30
|
+
return lines.length > 1 ? `${lines.length} lines` : undefined;
|
|
31
|
+
}
|
|
14
32
|
export class NavigationCommandActions {
|
|
15
33
|
host;
|
|
16
34
|
resumeSessionLoader;
|
|
@@ -98,12 +116,53 @@ export class NavigationCommandActions {
|
|
|
98
116
|
const runtime = getRuntime(this.host, "jump");
|
|
99
117
|
if (!runtime)
|
|
100
118
|
return;
|
|
101
|
-
this.host.setStatus("scanning session messages…");
|
|
102
|
-
this.host.render();
|
|
103
|
-
await this.host.refreshUserMessageJumpMenuItems();
|
|
104
119
|
this.host.openDirectPopupMenu("user-message-jump", { preserveStatus: true });
|
|
105
120
|
this.host.setDirectPopupMenuQuery(argumentsText.trim());
|
|
106
121
|
this.host.render();
|
|
122
|
+
try {
|
|
123
|
+
await this.host.refreshUserMessageJumpMenuItems();
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
this.host.toast.error(`Could not load jump messages: ${error instanceof Error ? error.message : String(error)}`);
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
this.host.render();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
async runHistoryCommand(argumentsText) {
|
|
133
|
+
const query = argumentsText.trim();
|
|
134
|
+
const matches = this.host.requestHistory().searchMatches(query, 100);
|
|
135
|
+
if (matches.length === 0) {
|
|
136
|
+
this.host.addEntry({ id: createId("system"), kind: "system", text: query ? `No command history found for: ${query}` : "Command history is empty." });
|
|
137
|
+
this.host.toast.info(query ? "No matching command history" : "Command history is empty");
|
|
138
|
+
this.host.setSessionStatus(this.host.runtime()?.session);
|
|
139
|
+
this.host.render();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const selected = await this.host.showMenu(matches.map((match) => {
|
|
143
|
+
const description = formatHistoryMenuDescription(match.value);
|
|
144
|
+
return {
|
|
145
|
+
value: match.value,
|
|
146
|
+
label: formatHistoryMenuLabel(match.value),
|
|
147
|
+
labelHighlightRanges: match.matchedText === match.label ? historyHighlightRanges(match.matchedRanges, match.value) : [],
|
|
148
|
+
...(description === undefined ? {} : { description }),
|
|
149
|
+
};
|
|
150
|
+
}), {
|
|
151
|
+
title: query ? `Search command history: ${query}` : "Command history",
|
|
152
|
+
placeholder: "Filter history",
|
|
153
|
+
emptyText: "No matching command history",
|
|
154
|
+
searchable: true,
|
|
155
|
+
minScorePerCharacter: 8,
|
|
156
|
+
preferKeyboardLayoutMatches: true,
|
|
157
|
+
});
|
|
158
|
+
if (!selected) {
|
|
159
|
+
this.host.setSessionStatus(this.host.runtime()?.session);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
this.host.setInput(selected);
|
|
163
|
+
this.host.toast.info("Restored command from history");
|
|
164
|
+
this.host.setSessionStatus(this.host.runtime()?.session);
|
|
165
|
+
this.host.render();
|
|
107
166
|
}
|
|
108
167
|
async runSearchCommand(argumentsText) {
|
|
109
168
|
const runtime = getIdleRuntime(this.host, "search");
|
|
@@ -25,6 +25,7 @@ export type CommandRegistryActions = {
|
|
|
25
25
|
runCloneCommand(): Promise<void>;
|
|
26
26
|
runTreeCommand(argumentsText: string): Promise<void>;
|
|
27
27
|
runJumpCommand(argumentsText: string): Promise<void>;
|
|
28
|
+
runHistoryCommand(argumentsText: string): Promise<void>;
|
|
28
29
|
runSearchCommand(argumentsText: string): Promise<void>;
|
|
29
30
|
runUnsupportedBuiltinCommand(commandName: string, message: string): Promise<void>;
|
|
30
31
|
runReloadCommand(): Promise<void>;
|
|
@@ -185,6 +185,14 @@ export function createSlashCommands(actions, host) {
|
|
|
185
185
|
allowArguments: true,
|
|
186
186
|
run: (argumentsText) => actions.runJumpCommand(argumentsText),
|
|
187
187
|
},
|
|
188
|
+
{
|
|
189
|
+
name: "history",
|
|
190
|
+
description: "Search command history and restore a match",
|
|
191
|
+
kind: "builtin",
|
|
192
|
+
keywords: ["command", "request", "prompt", "find", "recent"],
|
|
193
|
+
allowArguments: true,
|
|
194
|
+
run: (argumentsText) => actions.runHistoryCommand(argumentsText),
|
|
195
|
+
},
|
|
188
196
|
{
|
|
189
197
|
name: "search",
|
|
190
198
|
description: "Search sessions and open a match in a new tab",
|
package/dist/app/constants.d.ts
CHANGED
|
@@ -42,7 +42,6 @@ export declare const GIT_BRANCH_CACHE_MS = 30000;
|
|
|
42
42
|
export declare const TODO_TOOL_NAME = "todo";
|
|
43
43
|
export declare const TODO_ACTIONS: readonly ["create", "update", "batch_create", "batch_update", "list", "get", "delete", "clear", "export", "import"];
|
|
44
44
|
export declare const TODO_STATUSES: readonly ["pending", "in_progress", "deferred", "completed", "deleted"];
|
|
45
|
-
export declare const TODO_PRIORITIES: readonly ["low", "medium", "high", "urgent"];
|
|
46
45
|
export declare const SUBAGENT_STATUSES: readonly ["planned", "running", "retrying", "done", "failed", "stopped"];
|
|
47
46
|
export declare const SUBAGENT_ACTIVE_STATUSES: readonly ["planned", "running", "retrying"];
|
|
48
47
|
export declare const SUBAGENT_TERMINAL_STATUSES: readonly ["done", "failed", "stopped"];
|
package/dist/app/constants.js
CHANGED
|
@@ -90,7 +90,6 @@ export const TODO_ACTIONS = [
|
|
|
90
90
|
"import",
|
|
91
91
|
];
|
|
92
92
|
export const TODO_STATUSES = ["pending", "in_progress", "deferred", "completed", "deleted"];
|
|
93
|
-
export const TODO_PRIORITIES = ["low", "medium", "high", "urgent"];
|
|
94
93
|
export const SUBAGENT_STATUSES = ["planned", "running", "retrying", "done", "failed", "stopped"];
|
|
95
94
|
export const SUBAGENT_ACTIVE_STATUSES = ["planned", "running", "retrying"];
|
|
96
95
|
export const SUBAGENT_TERMINAL_STATUSES = ["done", "failed", "stopped"];
|
package/dist/app/icons.d.ts
CHANGED
package/dist/app/icons.js
CHANGED
|
@@ -18,6 +18,7 @@ const NERD_FONT_ICONS = {
|
|
|
18
18
|
deleted: "\u{f0159}",
|
|
19
19
|
deferred: "\u{f0377}",
|
|
20
20
|
info: "\u{f02fc}",
|
|
21
|
+
lightbulb: "\u{f0335}",
|
|
21
22
|
microphone: "\u{f036c}",
|
|
22
23
|
plus: "\u{f0415}",
|
|
23
24
|
pause: "\u{f03e4}",
|
|
@@ -44,6 +45,7 @@ const FALLBACK_ICONS = {
|
|
|
44
45
|
deleted: "×",
|
|
45
46
|
deferred: "↷",
|
|
46
47
|
info: "i",
|
|
48
|
+
lightbulb: "💡",
|
|
47
49
|
microphone: "m",
|
|
48
50
|
plus: "+",
|
|
49
51
|
pause: "⏸",
|
|
@@ -19,6 +19,7 @@ export type AppInputActionControllerHost = {
|
|
|
19
19
|
addEntry(entry: Entry): void;
|
|
20
20
|
addSessionAbortedEntry(): void;
|
|
21
21
|
showToast(message: string, kind: "success" | "error" | "warning" | "info"): void;
|
|
22
|
+
dismissActiveDialog?(): boolean;
|
|
22
23
|
stopVoiceInput(): Promise<void>;
|
|
23
24
|
isShellCommandRunning(): boolean;
|
|
24
25
|
runChatShellCommand(command: string): Promise<InteractiveShellCommandResult>;
|
|
@@ -80,10 +80,11 @@ export class AppInputActionController {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
closeActiveGlobalUi() {
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
83
|
+
if (this.popupMenus.syncActivePopupMenu()) {
|
|
84
|
+
this.popupMenus.cancelActivePopupMenu();
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return this.host.dismissActiveDialog?.() ?? false;
|
|
87
88
|
}
|
|
88
89
|
async abortStreamingSession(runtime, options) {
|
|
89
90
|
const session = runtime.session;
|
|
@@ -11,6 +11,7 @@ export declare class AppMenuItemsController {
|
|
|
11
11
|
private readonly host;
|
|
12
12
|
private resumeMenuLoaderCache;
|
|
13
13
|
private userMessageJumpItems;
|
|
14
|
+
private userMessageJumpLoading;
|
|
14
15
|
constructor(host: AppMenuItemsControllerHost);
|
|
15
16
|
parseSlashInput(text: string): import("../types.js").ParsedSlashInput | undefined;
|
|
16
17
|
getResourceSlashCommands(): SlashCommand[];
|
|
@@ -22,6 +23,7 @@ export declare class AppMenuItemsController {
|
|
|
22
23
|
getThinkingMenuItems(query: string): PopupMenuItem<ThinkingMenuValue>[];
|
|
23
24
|
getUserMessageMenuItems(): PopupMenuItem<UserMessageMenuValue>[];
|
|
24
25
|
getUserMessageJumpMenuItems(query: string): PopupMenuItem<UserMessageJumpMenuValue>[];
|
|
26
|
+
isUserMessageJumpLoading(): boolean;
|
|
25
27
|
refreshUserMessageJumpMenuItems(): Promise<void>;
|
|
26
28
|
getQueueMessageMenuItems(): PopupMenuItem<QueueMessageMenuValue>[];
|
|
27
29
|
getResumeMenuItems(query: string, limit?: number): PopupMenuItem<ResumeMenuValue>[];
|
|
@@ -12,6 +12,7 @@ export class AppMenuItemsController {
|
|
|
12
12
|
host;
|
|
13
13
|
resumeMenuLoaderCache;
|
|
14
14
|
userMessageJumpItems;
|
|
15
|
+
userMessageJumpLoading = false;
|
|
15
16
|
constructor(host) {
|
|
16
17
|
this.host = host;
|
|
17
18
|
}
|
|
@@ -29,6 +30,9 @@ export class AppMenuItemsController {
|
|
|
29
30
|
value: match.value,
|
|
30
31
|
label: `/${match.value.name}`,
|
|
31
32
|
description: match.value.description,
|
|
33
|
+
labelHighlightRanges: match.matchedText === match.label
|
|
34
|
+
? match.matchedRanges.map((range) => ({ start: range.start + 1, end: range.end + 1 }))
|
|
35
|
+
: [],
|
|
32
36
|
}));
|
|
33
37
|
}
|
|
34
38
|
modelRef(model) {
|
|
@@ -64,6 +68,7 @@ export class AppMenuItemsController {
|
|
|
64
68
|
value: match.value,
|
|
65
69
|
label: `${match.value.ref}${match.value.current ? ` ${APP_ICONS.check}` : ""}`,
|
|
66
70
|
description: match.value.model.name,
|
|
71
|
+
labelHighlightRanges: labelHighlightRangesFromMatch(match.matchedText, match.matchedRanges, match.label),
|
|
67
72
|
}));
|
|
68
73
|
}
|
|
69
74
|
getThinkingMenuItems(query) {
|
|
@@ -96,26 +101,36 @@ export class AppMenuItemsController {
|
|
|
96
101
|
getUserMessageJumpMenuItems(query) {
|
|
97
102
|
return filterUserMessageJumpItems(this.userMessageJumpItems ?? buildUserMessageJumpItems(this.host.getEntries()), query);
|
|
98
103
|
}
|
|
104
|
+
isUserMessageJumpLoading() {
|
|
105
|
+
return this.userMessageJumpLoading;
|
|
106
|
+
}
|
|
99
107
|
async refreshUserMessageJumpMenuItems() {
|
|
100
108
|
const runtime = this.host.runtime();
|
|
101
109
|
if (!runtime) {
|
|
102
110
|
this.userMessageJumpItems = undefined;
|
|
111
|
+
this.userMessageJumpLoading = false;
|
|
103
112
|
return;
|
|
104
113
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
this.userMessageJumpLoading = true;
|
|
115
|
+
try {
|
|
116
|
+
const entries = await sessionHistoryFullBranchEntries(runtime.session);
|
|
117
|
+
const loadedBySessionEntryId = new Map(this.host.getEntries()
|
|
118
|
+
.filter((entry) => entry.kind === "user" && typeof entry.sessionEntryId === "string")
|
|
119
|
+
.map((entry) => [entry.sessionEntryId, entry]));
|
|
120
|
+
const sources = entries.flatMap((entry) => {
|
|
121
|
+
if (entry.type !== "message" || !isRecord(entry.message) || entry.message.role !== "user")
|
|
122
|
+
return [];
|
|
123
|
+
const text = renderUserMessageContent(entry.message.content);
|
|
124
|
+
if (!text)
|
|
125
|
+
return [];
|
|
126
|
+
const loaded = loadedBySessionEntryId.get(entry.id);
|
|
127
|
+
return [{ text, ...(loaded ? { entryId: loaded.id } : {}), sessionEntryId: entry.id }];
|
|
128
|
+
});
|
|
129
|
+
this.userMessageJumpItems = buildUserMessageJumpItems(sources);
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
this.userMessageJumpLoading = false;
|
|
133
|
+
}
|
|
119
134
|
}
|
|
120
135
|
getQueueMessageMenuItems() {
|
|
121
136
|
return [
|
|
@@ -205,6 +220,14 @@ export class AppMenuItemsController {
|
|
|
205
220
|
}
|
|
206
221
|
}
|
|
207
222
|
}
|
|
223
|
+
function labelHighlightRangesFromMatch(matchedText, matchedRanges, label) {
|
|
224
|
+
if (matchedText === label)
|
|
225
|
+
return matchedRanges;
|
|
226
|
+
const offset = label.toLocaleLowerCase().indexOf(matchedText.toLocaleLowerCase());
|
|
227
|
+
if (offset < 0)
|
|
228
|
+
return [];
|
|
229
|
+
return matchedRanges.map((range) => ({ start: offset + range.start, end: offset + range.end }));
|
|
230
|
+
}
|
|
208
231
|
function normalizeAvailableThinkingLevels(levels) {
|
|
209
232
|
const seen = new Set();
|
|
210
233
|
const normalized = [];
|
|
@@ -666,10 +666,18 @@ export class AppPopupMenuController {
|
|
|
666
666
|
value: item,
|
|
667
667
|
label: item.label,
|
|
668
668
|
...(item.keywords === undefined ? {} : { keywords: item.keywords }),
|
|
669
|
-
})), query
|
|
669
|
+
})), query, {
|
|
670
|
+
...(request.options.minScorePerCharacter === undefined ? {} : { minScorePerCharacter: request.options.minScorePerCharacter }),
|
|
671
|
+
preferKeyboardLayoutMatches: request.options.preferKeyboardLayoutMatches ?? false,
|
|
672
|
+
}).map((match) => ({
|
|
673
|
+
...match.value,
|
|
674
|
+
labelHighlightRanges: match.matchedText === match.label ? match.matchedRanges : [],
|
|
675
|
+
}));
|
|
670
676
|
return this.withoutCloseMenuItems(items.map((item) => ({
|
|
671
677
|
value: item,
|
|
672
678
|
label: item.label,
|
|
679
|
+
...(item.labelHighlightRanges === undefined ? {} : { labelHighlightRanges: item.labelHighlightRanges }),
|
|
680
|
+
...(item.descriptionHighlightRanges === undefined ? {} : { descriptionHighlightRanges: item.descriptionHighlightRanges }),
|
|
673
681
|
...(item.description === undefined ? {} : { description: item.description }),
|
|
674
682
|
})));
|
|
675
683
|
}
|
|
@@ -733,7 +741,8 @@ function formatSessionMenuDateTime(dateTime) {
|
|
|
733
741
|
time: dateTime.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit", hourCycle: "h23" }),
|
|
734
742
|
};
|
|
735
743
|
}
|
|
736
|
-
function formatSessionInfoMenuItem(
|
|
744
|
+
function formatSessionInfoMenuItem(source) {
|
|
745
|
+
const { session, labelPrefix } = source;
|
|
737
746
|
const { date, time } = formatSessionMenuDateTime(session.modified);
|
|
738
747
|
const messages = `${session.messageCount} msg${session.messageCount !== 1 ? "s" : ""}`;
|
|
739
748
|
const label = session.name ?? session.firstMessage.slice(0, 50);
|
|
@@ -741,6 +750,7 @@ function formatSessionInfoMenuItem(session, labelPrefix = "") {
|
|
|
741
750
|
value: session,
|
|
742
751
|
label: `${labelPrefix}${label}`,
|
|
743
752
|
description: `${date} ${time} · ${messages} · ${session.id.slice(0, 8)}`,
|
|
753
|
+
...(source.labelHighlightRanges === undefined ? {} : { labelHighlightRanges: source.labelHighlightRanges }),
|
|
744
754
|
};
|
|
745
755
|
}
|
|
746
756
|
function buildSessionInfoMenuSource(sessions, currentSessionFile, query) {
|
|
@@ -761,7 +771,11 @@ function buildSessionInfoMenuSource(sessions, currentSessionFile, query) {
|
|
|
761
771
|
session.id,
|
|
762
772
|
],
|
|
763
773
|
}));
|
|
764
|
-
return fuzzySearch(items, query).map((match) => ({
|
|
774
|
+
return fuzzySearch(items, query).map((match) => ({
|
|
775
|
+
session: match.value,
|
|
776
|
+
labelPrefix: "",
|
|
777
|
+
labelHighlightRanges: match.matchedText === match.label ? match.matchedRanges : [],
|
|
778
|
+
}));
|
|
765
779
|
}
|
|
766
780
|
export function createSessionInfoMenuItemsLoader(sessions, currentSessionFile, query) {
|
|
767
781
|
const source = buildSessionInfoMenuSource(sessions, currentSessionFile, query);
|
|
@@ -775,7 +789,7 @@ export function createSessionInfoMenuItemsLoader(sessions, currentSessionFile, q
|
|
|
775
789
|
const cached = cachedItems.get(effectiveLimit);
|
|
776
790
|
if (cached)
|
|
777
791
|
return cached;
|
|
778
|
-
const result = source.slice(0, effectiveLimit).map((item) => formatSessionInfoMenuItem(item
|
|
792
|
+
const result = source.slice(0, effectiveLimit).map((item) => formatSessionInfoMenuItem(item));
|
|
779
793
|
cachedItems.set(effectiveLimit, result);
|
|
780
794
|
return result;
|
|
781
795
|
},
|
|
@@ -812,5 +826,16 @@ export function filterUserMessageJumpItems(items, query) {
|
|
|
812
826
|
...(item.aliases === undefined ? {} : { aliases: item.aliases }),
|
|
813
827
|
...(item.keywords === undefined ? {} : { keywords: item.keywords }),
|
|
814
828
|
}));
|
|
815
|
-
return fuzzySearch(searchableItems, query).map((match) =>
|
|
829
|
+
return fuzzySearch(searchableItems, query).map((match) => ({
|
|
830
|
+
...match.value,
|
|
831
|
+
labelHighlightRanges: labelHighlightRangesFromMatch(match.matchedText, match.matchedRanges, match.label),
|
|
832
|
+
}));
|
|
833
|
+
}
|
|
834
|
+
function labelHighlightRangesFromMatch(matchedText, matchedRanges, label) {
|
|
835
|
+
if (matchedText === label)
|
|
836
|
+
return matchedRanges;
|
|
837
|
+
const offset = label.toLocaleLowerCase().indexOf(matchedText.toLocaleLowerCase());
|
|
838
|
+
if (offset < 0)
|
|
839
|
+
return [];
|
|
840
|
+
return matchedRanges.map((range) => ({ start: offset + range.start, end: offset + range.end }));
|
|
816
841
|
}
|
|
@@ -20,35 +20,46 @@ export function renderTodoPanel(details, expanded, width, colors) {
|
|
|
20
20
|
const todoPanelColor = colors.warning;
|
|
21
21
|
const todoMetaColor = colors.muted;
|
|
22
22
|
const todoThinkingColor = (level) => thinkingLevelThemeColor(level, colors);
|
|
23
|
+
const todoStatusThemeColor = (status) => {
|
|
24
|
+
switch (status) {
|
|
25
|
+
case "pending": return colors.muted;
|
|
26
|
+
case "in_progress": return colors.warning;
|
|
27
|
+
case "deferred": return colors.muted;
|
|
28
|
+
case "completed": return colors.success;
|
|
29
|
+
case "deleted": return colors.error;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
23
32
|
if (!expanded) {
|
|
24
33
|
const prefix = `${headerText} — current: `;
|
|
25
34
|
const current = activeTask ? formatTodoTaskLine(activeTask) : "no active todo";
|
|
26
35
|
const collapsedText = `${prefix}${current}`;
|
|
27
|
-
const segments =
|
|
28
|
-
|
|
36
|
+
const segments = [
|
|
37
|
+
{ start: 0, end: headerText.length, foreground: todoPanelColor },
|
|
38
|
+
{ start: headerText.length, end: prefix.length, foreground: todoMetaColor },
|
|
39
|
+
];
|
|
40
|
+
if (activeTask) {
|
|
41
|
+
const activeSegments = todoTaskLineSegments(activeTask, todoMetaColor, { thinkingColor: todoThinkingColor, statusColor: todoStatusThemeColor }).map((segment) => ({
|
|
29
42
|
...segment,
|
|
30
43
|
start: segment.start + prefix.length,
|
|
31
44
|
end: segment.end + prefix.length,
|
|
32
|
-
}))
|
|
33
|
-
|
|
45
|
+
}));
|
|
46
|
+
segments.push(...activeSegments);
|
|
47
|
+
}
|
|
34
48
|
const line = {
|
|
35
49
|
text: padOrTrimPlain(ellipsizeDisplay(collapsedText, contentWidth), width),
|
|
36
|
-
|
|
50
|
+
segments,
|
|
37
51
|
target,
|
|
38
52
|
};
|
|
39
|
-
if (segments)
|
|
40
|
-
line.segments = segments;
|
|
41
53
|
return [line];
|
|
42
54
|
}
|
|
43
55
|
const lines = [];
|
|
44
56
|
for (const { task, depth } of visibleTodoTaskRows(details)) {
|
|
45
57
|
const text = formatTodoTaskLine(task, { depth });
|
|
46
|
-
const segments = todoTaskLineSegments(task, todoMetaColor, { depth, thinkingColor: todoThinkingColor });
|
|
58
|
+
const segments = todoTaskLineSegments(task, todoMetaColor, { depth, thinkingColor: todoThinkingColor, statusColor: todoStatusThemeColor });
|
|
47
59
|
let start = 0;
|
|
48
60
|
for (const wrapped of wrapLine(text, contentWidth)) {
|
|
49
61
|
lines.push({
|
|
50
62
|
text: padOrTrimPlain(wrapped, width),
|
|
51
|
-
colorOverride: todoPanelColor,
|
|
52
63
|
segments: shiftSegmentsToSlice(segments, start, wrapped.length),
|
|
53
64
|
target,
|
|
54
65
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Theme } from "../../theme.js";
|
|
2
|
+
import { type ModelColorsConfig } from "../../config.js";
|
|
2
3
|
import type { PopupMenu } from "../../ui.js";
|
|
3
4
|
import type { ScreenStyler } from "../screen/screen-styler.js";
|
|
4
5
|
import type { Entry, ModelMenuValue, PixMenuItem, PixMenuOptions, QueueMessageMenuValue, RenderedLine, ResumeMenuValue, SlashCommand, ThinkingMenuValue, UserMessageJumpMenuValue, UserMessageMenuValue } from "../types.js";
|
|
@@ -8,8 +9,10 @@ export type PopupMenuRendererHost = {
|
|
|
8
9
|
readonly screenStyler: ScreenStyler;
|
|
9
10
|
readonly entries: readonly Entry[];
|
|
10
11
|
readonly session: AgentSession | undefined;
|
|
12
|
+
readonly modelColors?: ModelColorsConfig;
|
|
11
13
|
readonly resumeLoading: boolean;
|
|
12
14
|
readonly resumeSessionCount: number;
|
|
15
|
+
readonly userMessageJumpLoading: boolean;
|
|
13
16
|
};
|
|
14
17
|
export declare class PopupMenuRenderer {
|
|
15
18
|
private readonly host;
|
|
@@ -38,10 +41,19 @@ export declare class PopupMenuRenderer {
|
|
|
38
41
|
options: PixMenuOptions;
|
|
39
42
|
} | undefined, directQuery: string): RenderedLine[];
|
|
40
43
|
private hasPopupActionItems;
|
|
44
|
+
private labelDescriptionText;
|
|
41
45
|
private userMessageActionForeground;
|
|
42
46
|
private selectableItemVariant;
|
|
47
|
+
private thinkingMenuItemSegments;
|
|
48
|
+
private modelMenuItemSegments;
|
|
49
|
+
private modelMenuItemColor;
|
|
50
|
+
private availableThinkingLevels;
|
|
43
51
|
private queueMessageItemVariant;
|
|
44
52
|
private sdkItemVariant;
|
|
53
|
+
private sdkMenuItemSegments;
|
|
54
|
+
private itemHighlightSegments;
|
|
55
|
+
private highlightSegments;
|
|
56
|
+
private descriptionHighlightSegments;
|
|
45
57
|
private resumeMenuItemSegments;
|
|
46
58
|
private popupMenuHeader;
|
|
47
59
|
private popupLineForeground;
|