@oh-my-pi/pi-coding-agent 15.7.2 → 15.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +114 -6
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/extension-flags.d.ts +36 -0
- package/dist/types/config/config-file.d.ts +4 -0
- package/dist/types/config/file-lock.d.ts +23 -0
- package/dist/types/config/keybindings.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +69 -65
- package/dist/types/edit/hashline/diff.d.ts +3 -3
- package/dist/types/eval/__tests__/agent-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/budget-bridge.test.d.ts +1 -0
- package/dist/types/eval/__tests__/idle-timeout.test.d.ts +1 -0
- package/dist/types/eval/agent-bridge.d.ts +25 -0
- package/dist/types/eval/backend.d.ts +17 -2
- package/dist/types/eval/budget-bridge.d.ts +29 -0
- package/dist/types/eval/idle-timeout.d.ts +28 -0
- package/dist/types/eval/js/executor.d.ts +8 -0
- package/dist/types/eval/js/tool-bridge.d.ts +2 -1
- package/dist/types/eval/py/executor.d.ts +13 -0
- package/dist/types/exec/bash-executor.d.ts +1 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/plugins/git-url.d.ts +11 -1
- package/dist/types/extensibility/plugins/manager.d.ts +12 -1
- package/dist/types/extensibility/shared-events.d.ts +2 -2
- package/dist/types/internal-urls/local-protocol.d.ts +19 -9
- package/dist/types/internal-urls/types.d.ts +14 -0
- package/dist/types/lsp/client.d.ts +3 -0
- package/dist/types/mcp/manager.d.ts +14 -5
- package/dist/types/memory-backend/index.d.ts +1 -1
- package/dist/types/memory-backend/resolve.d.ts +1 -1
- package/dist/types/memory-backend/types.d.ts +3 -3
- package/dist/types/mnemopi/backend.d.ts +4 -0
- package/dist/types/mnemopi/config.d.ts +29 -0
- package/dist/types/mnemopi/state.d.ts +72 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -2
- package/dist/types/modes/components/omfg-panel.d.ts +19 -0
- package/dist/types/modes/controllers/command-controller.d.ts +6 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -3
- package/dist/types/modes/controllers/omfg-controller.d.ts +10 -0
- package/dist/types/modes/controllers/omfg-rule.d.ts +26 -0
- package/dist/types/modes/gradient-highlight.d.ts +5 -1
- package/dist/types/modes/interactive-mode.d.ts +7 -3
- package/dist/types/modes/magic-keywords.d.ts +14 -0
- package/dist/types/modes/markdown-prose.d.ts +27 -0
- package/dist/types/modes/orchestrate.d.ts +7 -2
- package/dist/types/modes/shared.d.ts +1 -1
- package/dist/types/modes/turn-budget.d.ts +18 -0
- package/dist/types/modes/types.d.ts +7 -3
- package/dist/types/modes/ultrathink.d.ts +7 -2
- package/dist/types/modes/workflow.d.ts +15 -0
- package/dist/types/sdk.d.ts +13 -3
- package/dist/types/session/agent-session.d.ts +36 -17
- package/dist/types/session/session-manager.d.ts +18 -0
- package/dist/types/session/session-storage.d.ts +6 -0
- package/dist/types/session/shake-types.d.ts +24 -0
- package/dist/types/task/executor.d.ts +2 -2
- package/dist/types/task/repair-args.d.ts +52 -0
- package/dist/types/tiny/models.d.ts +1 -1
- package/dist/types/tiny/title-client.d.ts +28 -2
- package/dist/types/tiny/title-protocol.d.ts +8 -5
- package/dist/types/tools/find.d.ts +1 -1
- package/dist/types/tools/index.d.ts +19 -3
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/dist/types/tools/path-utils.d.ts +7 -0
- package/dist/types/tui/output-block.d.ts +7 -7
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/package.json +10 -10
- package/scripts/build-binary.ts +0 -1
- package/src/autoresearch/tools/run-experiment.ts +45 -113
- package/src/cli/args.ts +39 -16
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/plugin-cli.ts +11 -2
- package/src/cli.ts +59 -0
- package/src/config/config-file.ts +98 -13
- package/src/config/file-lock.ts +60 -17
- package/src/config/keybindings.ts +78 -27
- package/src/config/model-registry.ts +7 -1
- package/src/config/settings-schema.ts +73 -67
- package/src/config/settings.ts +22 -0
- package/src/edit/hashline/diff.ts +81 -24
- package/src/edit/renderer.ts +16 -12
- package/src/eval/__tests__/agent-bridge.test.ts +433 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/idle-timeout.test.ts +66 -0
- package/src/eval/__tests__/shared-executors.test.ts +21 -0
- package/src/eval/agent-bridge.ts +295 -0
- package/src/eval/backend.ts +17 -2
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/idle-timeout.ts +80 -0
- package/src/eval/js/executor.ts +35 -7
- package/src/eval/js/index.ts +2 -1
- package/src/eval/js/shared/prelude.txt +85 -1
- package/src/eval/js/tool-bridge.ts +9 -0
- package/src/eval/py/executor.ts +41 -14
- package/src/eval/py/index.ts +2 -1
- package/src/eval/py/prelude.py +132 -1
- package/src/exec/bash-executor.ts +2 -3
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/runner.ts +12 -2
- package/src/extensibility/plugins/git-url.ts +90 -4
- package/src/extensibility/plugins/manager.ts +103 -7
- package/src/extensibility/shared-events.ts +2 -2
- package/src/internal-urls/docs-index.generated.ts +88 -88
- package/src/internal-urls/local-protocol.ts +23 -11
- package/src/internal-urls/types.ts +15 -0
- package/src/lsp/client.ts +28 -5
- package/src/main.ts +44 -55
- package/src/mcp/manager.ts +87 -4
- package/src/memory-backend/index.ts +1 -1
- package/src/memory-backend/resolve.ts +3 -3
- package/src/memory-backend/types.ts +3 -3
- package/src/{mnemosyne → mnemopi}/backend.ts +70 -70
- package/src/{mnemosyne → mnemopi}/config.ts +36 -36
- package/src/{mnemosyne → mnemopi}/state.ts +67 -67
- package/src/modes/components/agent-dashboard.ts +6 -6
- package/src/modes/components/custom-editor.ts +4 -11
- package/src/modes/components/extensions/state-manager.ts +3 -4
- package/src/modes/components/footer.ts +8 -9
- package/src/modes/components/hook-selector.ts +86 -20
- package/src/modes/components/oauth-selector.ts +93 -21
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/settings-defs.ts +2 -2
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/tool-execution.ts +38 -19
- package/src/modes/components/tree-selector.ts +4 -3
- package/src/modes/components/user-message-selector.ts +94 -19
- package/src/modes/components/user-message.ts +8 -1
- package/src/modes/controllers/command-controller.ts +25 -0
- package/src/modes/controllers/event-controller.ts +68 -3
- package/src/modes/controllers/input-controller.ts +14 -11
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +1 -0
- package/src/modes/gradient-highlight.ts +23 -6
- package/src/modes/interactive-mode.ts +41 -7
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/orchestrate.ts +17 -11
- package/src/modes/shared.ts +3 -11
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/ultrathink.ts +16 -10
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/workflow.ts +42 -0
- package/src/prompts/system/omfg-user.md +51 -0
- package/src/prompts/system/project-prompt.md +3 -2
- package/src/prompts/system/subagent-system-prompt.md +12 -8
- package/src/prompts/system/system-prompt.md +9 -6
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/eval.md +13 -1
- package/src/prompts/tools/memory-edit.md +1 -1
- package/src/sdk.ts +63 -33
- package/src/session/agent-session.ts +290 -55
- package/src/session/session-manager.ts +32 -0
- package/src/session/session-storage.ts +68 -8
- package/src/session/shake-types.ts +43 -0
- package/src/slash-commands/builtin-registry.ts +39 -16
- package/src/task/executor.ts +17 -7
- package/src/task/index.ts +9 -8
- package/src/task/repair-args.ts +117 -0
- package/src/tiny/models.ts +2 -2
- package/src/tiny/title-client.ts +133 -43
- package/src/tiny/title-protocol.ts +10 -5
- package/src/tiny/worker.ts +3 -46
- package/src/tools/ast-edit.ts +3 -0
- package/src/tools/ast-grep.ts +3 -0
- package/src/tools/eval.ts +202 -26
- package/src/tools/find.ts +20 -6
- package/src/tools/gh.ts +1 -0
- package/src/tools/grouped-file-output.ts +9 -2
- package/src/tools/index.ts +17 -5
- package/src/tools/memory-edit.ts +4 -4
- package/src/tools/memory-recall.ts +5 -5
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +4 -4
- package/src/tools/path-utils.ts +13 -2
- package/src/tools/read.ts +1 -0
- package/src/tools/render-utils.ts +2 -1
- package/src/tools/search.ts +491 -76
- package/src/tui/output-block.ts +37 -75
- package/src/utils/git.ts +9 -3
- package/dist/types/mnemosyne/backend.d.ts +0 -4
- package/dist/types/mnemosyne/config.d.ts +0 -29
- package/dist/types/mnemosyne/state.d.ts +0 -72
- /package/dist/types/{mnemosyne → mnemopi}/index.d.ts +0 -0
- /package/src/{mnemosyne → mnemopi}/index.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getOAuthProviders } from "@oh-my-pi/pi-ai/utils/oauth";
|
|
2
2
|
import type { OAuthProviderInfo } from "@oh-my-pi/pi-ai/utils/oauth/types";
|
|
3
|
-
import { Container, matchesKey, Spacer, TruncatedText } from "@oh-my-pi/pi-tui";
|
|
3
|
+
import { Container, extractPrintableText, fuzzyFilter, matchesKey, Spacer, TruncatedText } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { theme } from "../../modes/theme/theme";
|
|
5
5
|
import { matchesSelectCancel, matchesSelectDown, matchesSelectUp } from "../../modes/utils/keybinding-matchers";
|
|
6
6
|
import type { AuthStorage } from "../../session/auth-storage";
|
|
@@ -13,6 +13,8 @@ const OAUTH_SELECTOR_MAX_VISIBLE = 10;
|
|
|
13
13
|
export class OAuthSelectorComponent extends Container {
|
|
14
14
|
#listContainer: Container;
|
|
15
15
|
#allProviders: OAuthProviderInfo[] = [];
|
|
16
|
+
#filteredProviders: OAuthProviderInfo[] = [];
|
|
17
|
+
#searchQuery = "";
|
|
16
18
|
#selectedIndex: number = 0;
|
|
17
19
|
#mode: "login" | "logout";
|
|
18
20
|
#authStorage: AuthStorage;
|
|
@@ -67,6 +69,7 @@ export class OAuthSelectorComponent extends Container {
|
|
|
67
69
|
}
|
|
68
70
|
#loadProviders(): void {
|
|
69
71
|
this.#allProviders = getOAuthProviders();
|
|
72
|
+
this.#filteredProviders = this.#allProviders;
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
#startValidation(): void {
|
|
@@ -144,10 +147,68 @@ export class OAuthSelectorComponent extends Container {
|
|
|
144
147
|
}
|
|
145
148
|
return this.#authStorage.hasAuth(providerId) ? theme.fg("success", ` ${theme.status.success} logged in`) : "";
|
|
146
149
|
}
|
|
150
|
+
#isSearchEnabled(): boolean {
|
|
151
|
+
return this.#allProviders.length > OAUTH_SELECTOR_MAX_VISIBLE;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
#shouldRenderSearchStatus(): boolean {
|
|
155
|
+
return this.#isSearchEnabled() || this.#searchQuery.length > 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#renderStatusLine(total: number): string {
|
|
159
|
+
const selectedCount = total === 0 ? 0 : this.#selectedIndex + 1;
|
|
160
|
+
const count =
|
|
161
|
+
this.#searchQuery.trim() && total !== this.#allProviders.length
|
|
162
|
+
? `${selectedCount}/${total} of ${this.#allProviders.length}`
|
|
163
|
+
: `${selectedCount}/${total}`;
|
|
164
|
+
const suffix = this.#searchQuery.trim() ? ` Search: ${this.#searchQuery}` : " Type to search";
|
|
165
|
+
return theme.fg("muted", ` (${count})${suffix}`);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#getProviderSearchText(provider: OAuthProviderInfo): string {
|
|
169
|
+
let text = `${provider.name} ${provider.id}`;
|
|
170
|
+
if (this.#authStorage.hasAuth(provider.id)) {
|
|
171
|
+
text += " logged in authenticated";
|
|
172
|
+
}
|
|
173
|
+
if (!provider.available) {
|
|
174
|
+
text += " unavailable";
|
|
175
|
+
}
|
|
176
|
+
return text;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
#setSearchQuery(query: string): void {
|
|
180
|
+
this.#searchQuery = query;
|
|
181
|
+
this.#filteredProviders = query.trim()
|
|
182
|
+
? fuzzyFilter(this.#allProviders, query, provider => this.#getProviderSearchText(provider))
|
|
183
|
+
: this.#allProviders;
|
|
184
|
+
this.#selectedIndex = 0;
|
|
185
|
+
this.#statusMessage = undefined;
|
|
186
|
+
this.#updateList();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#handleSearchInput(keyData: string): boolean {
|
|
190
|
+
if (!this.#isSearchEnabled()) return false;
|
|
191
|
+
|
|
192
|
+
if (matchesKey(keyData, "backspace")) {
|
|
193
|
+
if (this.#searchQuery.length === 0) return false;
|
|
194
|
+
const chars = [...this.#searchQuery];
|
|
195
|
+
chars.pop();
|
|
196
|
+
this.#setSearchQuery(chars.join(""));
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const printableText = extractPrintableText(keyData);
|
|
201
|
+
if (printableText === undefined) return false;
|
|
202
|
+
if (this.#searchQuery.length === 0 && printableText.trim().length === 0) return false;
|
|
203
|
+
|
|
204
|
+
this.#setSearchQuery(this.#searchQuery + printableText);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
|
|
147
208
|
#updateList(): void {
|
|
148
209
|
this.#listContainer.clear();
|
|
149
210
|
|
|
150
|
-
const total = this.#
|
|
211
|
+
const total = this.#filteredProviders.length;
|
|
151
212
|
const maxVisible = OAUTH_SELECTOR_MAX_VISIBLE;
|
|
152
213
|
const startIndex =
|
|
153
214
|
total <= maxVisible
|
|
@@ -156,7 +217,7 @@ export class OAuthSelectorComponent extends Container {
|
|
|
156
217
|
const endIndex = Math.min(startIndex + maxVisible, total);
|
|
157
218
|
|
|
158
219
|
for (let i = startIndex; i < endIndex; i++) {
|
|
159
|
-
const provider = this.#
|
|
220
|
+
const provider = this.#filteredProviders[i];
|
|
160
221
|
if (!provider) continue;
|
|
161
222
|
const isSelected = i === this.#selectedIndex;
|
|
162
223
|
const isAvailable = provider.available;
|
|
@@ -174,16 +235,19 @@ export class OAuthSelectorComponent extends Container {
|
|
|
174
235
|
this.#listContainer.addChild(new TruncatedText(line, 0, 0));
|
|
175
236
|
}
|
|
176
237
|
|
|
177
|
-
// Scroll indicator when list is windowed
|
|
178
|
-
if (startIndex > 0 || endIndex < total) {
|
|
179
|
-
|
|
180
|
-
this.#listContainer.addChild(new TruncatedText(scrollInfo, 0, 0));
|
|
238
|
+
// Scroll/search indicator when list is windowed or searchable
|
|
239
|
+
if (startIndex > 0 || endIndex < total || this.#shouldRenderSearchStatus()) {
|
|
240
|
+
this.#listContainer.addChild(new TruncatedText(this.#renderStatusLine(total), 0, 0));
|
|
181
241
|
}
|
|
182
242
|
|
|
183
243
|
// Show "no providers" if empty
|
|
184
244
|
if (total === 0) {
|
|
185
245
|
const message =
|
|
186
|
-
this.#
|
|
246
|
+
this.#allProviders.length === 0
|
|
247
|
+
? this.#mode === "login"
|
|
248
|
+
? "No OAuth providers available"
|
|
249
|
+
: "No OAuth providers logged in. Use /login first."
|
|
250
|
+
: "No matching providers";
|
|
187
251
|
this.#listContainer.addChild(new TruncatedText(theme.fg("muted", ` ${message}`), 0, 0));
|
|
188
252
|
}
|
|
189
253
|
if (this.#statusMessage) {
|
|
@@ -192,25 +256,38 @@ export class OAuthSelectorComponent extends Container {
|
|
|
192
256
|
}
|
|
193
257
|
}
|
|
194
258
|
handleInput(keyData: string): void {
|
|
259
|
+
// Escape or Ctrl+C
|
|
260
|
+
if (matchesSelectCancel(keyData)) {
|
|
261
|
+
this.stopValidation();
|
|
262
|
+
this.#onCancelCallback();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (this.#handleSearchInput(keyData)) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
195
270
|
// Up arrow
|
|
196
271
|
if (matchesSelectUp(keyData)) {
|
|
197
|
-
if (this.#
|
|
198
|
-
this.#selectedIndex =
|
|
272
|
+
if (this.#filteredProviders.length > 0) {
|
|
273
|
+
this.#selectedIndex =
|
|
274
|
+
this.#selectedIndex === 0 ? this.#filteredProviders.length - 1 : this.#selectedIndex - 1;
|
|
199
275
|
}
|
|
200
276
|
this.#statusMessage = undefined;
|
|
201
277
|
this.#updateList();
|
|
202
278
|
}
|
|
203
279
|
// Down arrow
|
|
204
280
|
else if (matchesSelectDown(keyData)) {
|
|
205
|
-
if (this.#
|
|
206
|
-
this.#selectedIndex =
|
|
281
|
+
if (this.#filteredProviders.length > 0) {
|
|
282
|
+
this.#selectedIndex =
|
|
283
|
+
this.#selectedIndex === this.#filteredProviders.length - 1 ? 0 : this.#selectedIndex + 1;
|
|
207
284
|
}
|
|
208
285
|
this.#statusMessage = undefined;
|
|
209
286
|
this.#updateList();
|
|
210
287
|
}
|
|
211
288
|
// Page up - jump up by one visible page
|
|
212
289
|
else if (matchesKey(keyData, "pageUp")) {
|
|
213
|
-
if (this.#
|
|
290
|
+
if (this.#filteredProviders.length > 0) {
|
|
214
291
|
this.#selectedIndex = Math.max(0, this.#selectedIndex - OAUTH_SELECTOR_MAX_VISIBLE);
|
|
215
292
|
}
|
|
216
293
|
this.#statusMessage = undefined;
|
|
@@ -218,9 +295,9 @@ export class OAuthSelectorComponent extends Container {
|
|
|
218
295
|
}
|
|
219
296
|
// Page down - jump down by one visible page
|
|
220
297
|
else if (matchesKey(keyData, "pageDown")) {
|
|
221
|
-
if (this.#
|
|
298
|
+
if (this.#filteredProviders.length > 0) {
|
|
222
299
|
this.#selectedIndex = Math.min(
|
|
223
|
-
this.#
|
|
300
|
+
this.#filteredProviders.length - 1,
|
|
224
301
|
this.#selectedIndex + OAUTH_SELECTOR_MAX_VISIBLE,
|
|
225
302
|
);
|
|
226
303
|
}
|
|
@@ -229,7 +306,7 @@ export class OAuthSelectorComponent extends Container {
|
|
|
229
306
|
}
|
|
230
307
|
// Enter
|
|
231
308
|
else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
232
|
-
const selectedProvider = this.#
|
|
309
|
+
const selectedProvider = this.#filteredProviders[this.#selectedIndex];
|
|
233
310
|
if (selectedProvider?.available) {
|
|
234
311
|
this.#statusMessage = undefined;
|
|
235
312
|
this.stopValidation();
|
|
@@ -239,10 +316,5 @@ export class OAuthSelectorComponent extends Container {
|
|
|
239
316
|
this.#updateList();
|
|
240
317
|
}
|
|
241
318
|
}
|
|
242
|
-
// Escape or Ctrl+C
|
|
243
|
-
else if (matchesSelectCancel(keyData)) {
|
|
244
|
-
this.stopValidation();
|
|
245
|
-
this.#onCancelCallback();
|
|
246
|
-
}
|
|
247
319
|
}
|
|
248
320
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { type Component, Container, Markdown, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import { replaceTabs } from "../../tools/render-utils";
|
|
3
|
+
import { getMarkdownTheme, theme } from "../theme/theme";
|
|
4
|
+
import { DynamicBorder } from "./dynamic-border";
|
|
5
|
+
|
|
6
|
+
export type OmfgPanelState =
|
|
7
|
+
| "generating"
|
|
8
|
+
| "validating"
|
|
9
|
+
| "confirming"
|
|
10
|
+
| "saving"
|
|
11
|
+
| "saved"
|
|
12
|
+
| "rejected"
|
|
13
|
+
| "aborted"
|
|
14
|
+
| "error";
|
|
15
|
+
|
|
16
|
+
interface OmfgPanelComponentOptions {
|
|
17
|
+
complaint: string;
|
|
18
|
+
tui: TUI;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class OmfgPanelComponent extends Container {
|
|
22
|
+
#complaint: string;
|
|
23
|
+
#tui: TUI;
|
|
24
|
+
#state: OmfgPanelState = "generating";
|
|
25
|
+
#status = "Generating TTSR rule…";
|
|
26
|
+
#preview = "";
|
|
27
|
+
#savedPath: string | undefined;
|
|
28
|
+
#errorMessage: string | undefined;
|
|
29
|
+
#closed = false;
|
|
30
|
+
|
|
31
|
+
constructor(options: OmfgPanelComponentOptions) {
|
|
32
|
+
super();
|
|
33
|
+
this.#complaint = options.complaint;
|
|
34
|
+
this.#tui = options.tui;
|
|
35
|
+
this.#rebuild();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
appendDraft(delta: string): void {
|
|
39
|
+
if (!delta || this.#closed) return;
|
|
40
|
+
this.#preview += delta;
|
|
41
|
+
this.#rebuild();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setRule(text: string): void {
|
|
45
|
+
if (this.#closed) return;
|
|
46
|
+
this.#preview = text;
|
|
47
|
+
this.#rebuild();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setStatus(state: OmfgPanelState, status: string): void {
|
|
51
|
+
if (this.#closed) return;
|
|
52
|
+
this.#state = state;
|
|
53
|
+
this.#status = status;
|
|
54
|
+
this.#errorMessage = undefined;
|
|
55
|
+
this.#rebuild();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
markSaved(path: string): void {
|
|
59
|
+
if (this.#closed) return;
|
|
60
|
+
this.#state = "saved";
|
|
61
|
+
this.#savedPath = path;
|
|
62
|
+
this.#status = `Saved ${path}`;
|
|
63
|
+
this.#errorMessage = undefined;
|
|
64
|
+
this.#rebuild();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
markRejected(): void {
|
|
68
|
+
if (this.#closed) return;
|
|
69
|
+
this.#state = "rejected";
|
|
70
|
+
this.#status = "Rule was not saved.";
|
|
71
|
+
this.#errorMessage = undefined;
|
|
72
|
+
this.#rebuild();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
markAborted(): void {
|
|
76
|
+
if (this.#closed) return;
|
|
77
|
+
this.#state = "aborted";
|
|
78
|
+
this.#status = "Cancelled.";
|
|
79
|
+
this.#errorMessage = undefined;
|
|
80
|
+
this.#rebuild();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
markError(message: string): void {
|
|
84
|
+
if (this.#closed) return;
|
|
85
|
+
this.#state = "error";
|
|
86
|
+
this.#status = "Could not create rule.";
|
|
87
|
+
this.#errorMessage = message;
|
|
88
|
+
this.#rebuild();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
close(): void {
|
|
92
|
+
this.#closed = true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
#rebuild(): void {
|
|
96
|
+
this.clear();
|
|
97
|
+
this.addChild(new DynamicBorder(str => theme.fg("dim", str)));
|
|
98
|
+
this.addChild(new Spacer(1));
|
|
99
|
+
this.addChild(new Text(theme.fg("accent", replaceTabs(`/omfg ${this.#complaint}`)), 1, 0));
|
|
100
|
+
this.addChild(new Text(theme.fg("muted", replaceTabs(this.#status)), 1, 0));
|
|
101
|
+
this.addChild(new Spacer(1));
|
|
102
|
+
this.addChild(this.#contentComponent());
|
|
103
|
+
this.addChild(new Spacer(1));
|
|
104
|
+
this.addChild(new Text(this.#footerLine(), 1, 0));
|
|
105
|
+
this.addChild(new Spacer(1));
|
|
106
|
+
this.addChild(new DynamicBorder(str => theme.fg("dim", str)));
|
|
107
|
+
this.#tui.requestRender();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
#footerLine(): string {
|
|
111
|
+
switch (this.#state) {
|
|
112
|
+
case "generating":
|
|
113
|
+
case "validating":
|
|
114
|
+
case "confirming":
|
|
115
|
+
case "saving":
|
|
116
|
+
return theme.fg("muted", "Esc cancel /omfg");
|
|
117
|
+
case "saved":
|
|
118
|
+
return theme.fg(
|
|
119
|
+
"success",
|
|
120
|
+
`${theme.status.success} Registered live · ${replaceTabs(this.#savedPath ?? "saved")}`,
|
|
121
|
+
);
|
|
122
|
+
case "rejected":
|
|
123
|
+
return theme.fg("warning", `${theme.status.warning} Not saved · Esc dismiss`);
|
|
124
|
+
case "aborted":
|
|
125
|
+
return theme.fg("warning", `${theme.status.warning} Cancelled · Esc dismiss`);
|
|
126
|
+
case "error":
|
|
127
|
+
return theme.fg("error", `${theme.status.error} Error · Esc dismiss`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#contentComponent(): Component {
|
|
132
|
+
if (this.#state === "error") {
|
|
133
|
+
return new Text(theme.fg("error", replaceTabs(this.#errorMessage ?? "Unknown error")), 1, 0);
|
|
134
|
+
}
|
|
135
|
+
const text = replaceTabs(this.#preview).trim();
|
|
136
|
+
if (!text) {
|
|
137
|
+
return new Text(theme.fg("dim", `${theme.status.pending} Waiting for candidate rule…`), 1, 0);
|
|
138
|
+
}
|
|
139
|
+
return new Markdown(text, 1, 0, getMarkdownTheme());
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -79,9 +79,9 @@ const CONDITIONS: Record<string, () => boolean> = {
|
|
|
79
79
|
return false;
|
|
80
80
|
}
|
|
81
81
|
},
|
|
82
|
-
|
|
82
|
+
mnemopiActive: () => {
|
|
83
83
|
try {
|
|
84
|
-
return Settings.instance.get("memory.backend") === "
|
|
84
|
+
return Settings.instance.get("memory.backend") === "mnemopi";
|
|
85
85
|
} catch {
|
|
86
86
|
return false;
|
|
87
87
|
}
|
|
@@ -4,10 +4,11 @@ Ctrl+D can be used to exit, but with your draft saved!
|
|
|
4
4
|
Find out which model you emotionally abuse the most with `omp stats`
|
|
5
5
|
Try task isolation to create CoW worktrees
|
|
6
6
|
Your LLM can call an LLM using `llm(x...)`. Have a big batch of tasks? Ask clanker to use it!
|
|
7
|
-
|
|
7
|
+
Spaghetti code? Try complaining with /omfg
|
|
8
8
|
Did you know? Each kitty/tmux split keeps its own session — `omp -c` resumes the right one
|
|
9
9
|
Drop the word `ultrathink` in your message for harder multi-step reasoning — watch it glow rainbow as you type
|
|
10
10
|
Say `orchestrate` in your message to drive a multi-phase task with parallel subagents — watch it glow as you type
|
|
11
|
+
Say `workflow` in your message to drive the task with parallel subagents in eval — watch it glow as you type
|
|
11
12
|
Log in to several accounts of the same provider — `/login` again — and omp load-balances across them automatically
|
|
12
13
|
Run `omp auth-broker serve` once and every machine pulls live tokens over the wire — refresh keys never leave the host; `omp auth-gateway` fronts it as a drop-in proxy any OpenAI-compatible client can hit
|
|
13
14
|
Press alt+p (or /switch) to switch provider, and ctrl+p to cycle role models smol -> slow -> etc
|
|
@@ -45,15 +45,6 @@ function ensureInvalidate(component: unknown): Component {
|
|
|
45
45
|
return c as Component;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
function cloneToolArgs<T>(args: T): T {
|
|
49
|
-
if (args === null || args === undefined) return args;
|
|
50
|
-
try {
|
|
51
|
-
return structuredClone(args);
|
|
52
|
-
} catch {
|
|
53
|
-
return args;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
48
|
/**
|
|
58
49
|
* Drop trailing removal/hunk-header lines that appear in a streaming diff
|
|
59
50
|
* before the matching `+added` lines have arrived. Without this, a partial
|
|
@@ -107,6 +98,27 @@ function resolveEditModeForTool(toolName: string, tool: AgentTool | undefined):
|
|
|
107
98
|
return (tool as { mode?: EditMode } | undefined)?.mode;
|
|
108
99
|
}
|
|
109
100
|
|
|
101
|
+
function rawTextInputFromPartialJson(partialJson: unknown): string | undefined {
|
|
102
|
+
if (typeof partialJson !== "string") return undefined;
|
|
103
|
+
if (partialJson.length === 0) return undefined;
|
|
104
|
+
const trimmed = partialJson.trimStart();
|
|
105
|
+
if (trimmed.length === 0) return undefined;
|
|
106
|
+
const first = trimmed[0];
|
|
107
|
+
// Function-tool arguments stream as JSON. Custom/free-form tools stream raw
|
|
108
|
+
// text in the same transport field; only the raw form is a valid fallback for
|
|
109
|
+
// the conventional `input` parameter.
|
|
110
|
+
if (first === "{" || first === "[" || first === '"') return undefined;
|
|
111
|
+
return partialJson;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function getArgsWithStreamedTextInput(args: unknown): unknown {
|
|
115
|
+
if (args == null || typeof args !== "object") return args;
|
|
116
|
+
const record = args as Record<string, unknown>;
|
|
117
|
+
if (typeof record.input === "string") return args;
|
|
118
|
+
const input = rawTextInputFromPartialJson(record.__partialJson);
|
|
119
|
+
return input === undefined ? args : { ...record, input };
|
|
120
|
+
}
|
|
121
|
+
|
|
110
122
|
export interface ToolExecutionOptions {
|
|
111
123
|
snapshots?: SnapshotStore;
|
|
112
124
|
showImages?: boolean; // default: true (only used if terminal supports images)
|
|
@@ -198,7 +210,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
198
210
|
this.#tool = tool;
|
|
199
211
|
this.#ui = ui;
|
|
200
212
|
this.#cwd = cwd;
|
|
201
|
-
this.#args =
|
|
213
|
+
this.#args = args;
|
|
202
214
|
|
|
203
215
|
this.addChild(new Spacer(1));
|
|
204
216
|
|
|
@@ -222,7 +234,12 @@ export class ToolExecutionComponent extends Container {
|
|
|
222
234
|
}
|
|
223
235
|
|
|
224
236
|
updateArgs(args: any, _toolCallId?: string): void {
|
|
225
|
-
|
|
237
|
+
// Reference-equality short-circuit before any further work. Callers
|
|
238
|
+
// always allocate a new arg object on each streamed delta (see
|
|
239
|
+
// event-controller.ts and ui-helpers.ts), so a same-reference assignment
|
|
240
|
+
// signals "nothing meaningful changed" and the renderer can skip.
|
|
241
|
+
if (args === this.#args) return;
|
|
242
|
+
this.#args = args;
|
|
226
243
|
this.#updateSpinnerAnimation();
|
|
227
244
|
void this.#runPreviewDiff();
|
|
228
245
|
this.#updateDisplay();
|
|
@@ -247,12 +264,13 @@ export class ToolExecutionComponent extends Container {
|
|
|
247
264
|
const args = this.#args;
|
|
248
265
|
if (args == null || typeof args !== "object") return;
|
|
249
266
|
|
|
250
|
-
const
|
|
267
|
+
const previewArgs = getArgsWithStreamedTextInput(args);
|
|
268
|
+
const partialJson = (previewArgs as { __partialJson?: string }).__partialJson;
|
|
251
269
|
let effectiveArgs: unknown;
|
|
252
270
|
try {
|
|
253
|
-
effectiveArgs = strategy.extractCompleteEdits(
|
|
271
|
+
effectiveArgs = strategy.extractCompleteEdits(previewArgs, partialJson);
|
|
254
272
|
} catch {
|
|
255
|
-
effectiveArgs =
|
|
273
|
+
effectiveArgs = previewArgs;
|
|
256
274
|
}
|
|
257
275
|
|
|
258
276
|
// Coalesce duplicate computes for identical args. The key pairs the
|
|
@@ -717,20 +735,21 @@ export class ToolExecutionComponent extends Container {
|
|
|
717
735
|
}
|
|
718
736
|
|
|
719
737
|
#getCallArgsForRender(): any {
|
|
738
|
+
const renderArgs = getArgsWithStreamedTextInput(this.#args);
|
|
720
739
|
if (!isEditLikeToolName(this.#toolName)) {
|
|
721
|
-
return
|
|
740
|
+
return renderArgs;
|
|
722
741
|
}
|
|
723
742
|
const previews = this.#editDiffPreview;
|
|
724
743
|
if (!previews || previews.length === 0) {
|
|
725
|
-
return
|
|
744
|
+
return renderArgs;
|
|
726
745
|
}
|
|
727
746
|
// Single-file previews feed the existing `previewDiff` channel consumed
|
|
728
747
|
// by `formatStreamingDiff` in the renderer.
|
|
729
748
|
const first = previews[0];
|
|
730
749
|
if (!first?.diff) {
|
|
731
|
-
return
|
|
750
|
+
return renderArgs;
|
|
732
751
|
}
|
|
733
|
-
return { ...(
|
|
752
|
+
return { ...(renderArgs as Record<string, unknown>), previewDiff: first.diff };
|
|
734
753
|
}
|
|
735
754
|
|
|
736
755
|
/**
|
|
@@ -781,7 +800,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
781
800
|
if (!previews?.some(preview => preview.diff)) {
|
|
782
801
|
const editMode = this.#editMode;
|
|
783
802
|
const strategy = editMode ? EDIT_MODE_STRATEGIES[editMode] : undefined;
|
|
784
|
-
const fallback = strategy?.renderStreamingFallback(this.#args, theme);
|
|
803
|
+
const fallback = strategy?.renderStreamingFallback(getArgsWithStreamedTextInput(this.#args), theme);
|
|
785
804
|
if (fallback) context.editStreamingFallback = fallback;
|
|
786
805
|
}
|
|
787
806
|
context.renderDiff = renderDiff;
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
type Component,
|
|
4
4
|
Container,
|
|
5
5
|
extractPrintableText,
|
|
6
|
+
fuzzyMatch,
|
|
6
7
|
Input,
|
|
7
8
|
matchesKey,
|
|
8
9
|
Spacer,
|
|
@@ -325,10 +326,10 @@ class TreeList implements Component {
|
|
|
325
326
|
|
|
326
327
|
if (!passesFilter) return false;
|
|
327
328
|
|
|
328
|
-
// Apply search filter
|
|
329
|
+
// Apply fuzzy search filter
|
|
329
330
|
if (searchTokens.length > 0) {
|
|
330
|
-
const nodeText = this.#getSearchableText(flatNode.node)
|
|
331
|
-
return searchTokens.every(token =>
|
|
331
|
+
const nodeText = this.#getSearchableText(flatNode.node);
|
|
332
|
+
return searchTokens.every(token => fuzzyMatch(token, nodeText).matches);
|
|
332
333
|
}
|
|
333
334
|
|
|
334
335
|
return true;
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Component,
|
|
3
|
+
Container,
|
|
4
|
+
extractPrintableText,
|
|
5
|
+
fuzzyFilter,
|
|
6
|
+
matchesKey,
|
|
7
|
+
Spacer,
|
|
8
|
+
Text,
|
|
9
|
+
truncateToWidth,
|
|
10
|
+
} from "@oh-my-pi/pi-tui";
|
|
2
11
|
import { theme } from "../../modes/theme/theme";
|
|
3
12
|
import { matchesSelectCancel, matchesSelectDown, matchesSelectUp } from "../../modes/utils/keybinding-matchers";
|
|
4
13
|
import { DynamicBorder } from "./dynamic-border";
|
|
@@ -13,6 +22,8 @@ interface UserMessageItem {
|
|
|
13
22
|
* Custom user message list component with selection
|
|
14
23
|
*/
|
|
15
24
|
class UserMessageList implements Component {
|
|
25
|
+
#filteredMessages: UserMessageItem[];
|
|
26
|
+
#searchQuery = "";
|
|
16
27
|
#selectedIndex: number = 0;
|
|
17
28
|
onSelect?: (entryId: string) => void;
|
|
18
29
|
onCancel?: () => void;
|
|
@@ -20,14 +31,60 @@ class UserMessageList implements Component {
|
|
|
20
31
|
|
|
21
32
|
constructor(private readonly messages: UserMessageItem[]) {
|
|
22
33
|
// Store messages in chronological order (oldest to newest)
|
|
34
|
+
this.#filteredMessages = messages;
|
|
23
35
|
// Start with the last (most recent) message selected
|
|
24
|
-
this.#selectedIndex = Math.max(0, this.
|
|
36
|
+
this.#selectedIndex = Math.max(0, this.#filteredMessages.length - 1);
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
invalidate(): void {
|
|
28
40
|
// No cached state to invalidate currently
|
|
29
41
|
}
|
|
30
42
|
|
|
43
|
+
#isSearchEnabled(): boolean {
|
|
44
|
+
return this.messages.length > this.#maxVisible;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#shouldRenderSearchStatus(): boolean {
|
|
48
|
+
return this.#isSearchEnabled() || this.#searchQuery.length > 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#renderStatusLine(total: number): string {
|
|
52
|
+
const selectedCount = total === 0 ? 0 : this.#selectedIndex + 1;
|
|
53
|
+
const count =
|
|
54
|
+
this.#searchQuery.trim() && total !== this.messages.length
|
|
55
|
+
? `${selectedCount}/${total} of ${this.messages.length}`
|
|
56
|
+
: `${selectedCount}/${total}`;
|
|
57
|
+
const suffix = this.#searchQuery.trim() ? ` Search: ${this.#searchQuery}` : " Type to search";
|
|
58
|
+
return theme.fg("muted", ` (${count})${suffix}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#setSearchQuery(query: string): void {
|
|
62
|
+
this.#searchQuery = query;
|
|
63
|
+
this.#filteredMessages = query.trim()
|
|
64
|
+
? fuzzyFilter(this.messages, query, message => `${message.text} ${message.timestamp ?? ""}`)
|
|
65
|
+
: this.messages;
|
|
66
|
+
this.#selectedIndex = query.trim() ? 0 : Math.max(0, this.#filteredMessages.length - 1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#handleSearchInput(keyData: string): boolean {
|
|
70
|
+
if (!this.#isSearchEnabled()) return false;
|
|
71
|
+
|
|
72
|
+
if (matchesKey(keyData, "backspace")) {
|
|
73
|
+
if (this.#searchQuery.length === 0) return false;
|
|
74
|
+
const chars = [...this.#searchQuery];
|
|
75
|
+
chars.pop();
|
|
76
|
+
this.#setSearchQuery(chars.join(""));
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const printableText = extractPrintableText(keyData);
|
|
81
|
+
if (printableText === undefined) return false;
|
|
82
|
+
if (this.#searchQuery.length === 0 && printableText.trim().length === 0) return false;
|
|
83
|
+
|
|
84
|
+
this.#setSearchQuery(this.#searchQuery + printableText);
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
31
88
|
render(width: number): string[] {
|
|
32
89
|
const lines: string[] = [];
|
|
33
90
|
|
|
@@ -36,16 +93,19 @@ class UserMessageList implements Component {
|
|
|
36
93
|
return lines;
|
|
37
94
|
}
|
|
38
95
|
|
|
96
|
+
const total = this.#filteredMessages.length;
|
|
97
|
+
|
|
39
98
|
// Calculate visible range with scrolling
|
|
40
99
|
const startIndex = Math.max(
|
|
41
100
|
0,
|
|
42
|
-
Math.min(this.#selectedIndex - Math.floor(this.#maxVisible / 2),
|
|
101
|
+
Math.min(this.#selectedIndex - Math.floor(this.#maxVisible / 2), total - this.#maxVisible),
|
|
43
102
|
);
|
|
44
|
-
const endIndex = Math.min(startIndex + this.#maxVisible,
|
|
103
|
+
const endIndex = Math.min(startIndex + this.#maxVisible, total);
|
|
45
104
|
|
|
46
105
|
// Render visible messages (2 lines per message + blank line)
|
|
47
106
|
for (let i = startIndex; i < endIndex; i++) {
|
|
48
|
-
const message = this
|
|
107
|
+
const message = this.#filteredMessages[i];
|
|
108
|
+
if (!message) continue;
|
|
49
109
|
const isSelected = i === this.#selectedIndex;
|
|
50
110
|
|
|
51
111
|
// Normalize message to single line
|
|
@@ -60,44 +120,59 @@ class UserMessageList implements Component {
|
|
|
60
120
|
lines.push(messageLine);
|
|
61
121
|
|
|
62
122
|
// Second line: metadata (position in history)
|
|
63
|
-
const position =
|
|
123
|
+
const position = this.messages.indexOf(message) + 1;
|
|
64
124
|
const metadata = ` Message ${position} of ${this.messages.length}`;
|
|
65
125
|
const metadataLine = theme.fg("muted", metadata);
|
|
66
126
|
lines.push(metadataLine);
|
|
67
127
|
lines.push(""); // Blank line between messages
|
|
68
128
|
}
|
|
69
129
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
130
|
+
if (total === 0) {
|
|
131
|
+
lines.push(theme.fg("muted", " No matching messages"));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add scroll/search indicator if needed
|
|
135
|
+
if (startIndex > 0 || endIndex < total || this.#shouldRenderSearchStatus()) {
|
|
136
|
+
lines.push(this.#renderStatusLine(total));
|
|
74
137
|
}
|
|
75
138
|
|
|
76
139
|
return lines;
|
|
77
140
|
}
|
|
78
141
|
|
|
79
142
|
handleInput(keyData: string): void {
|
|
143
|
+
// Escape / cancel
|
|
144
|
+
if (matchesSelectCancel(keyData)) {
|
|
145
|
+
if (this.onCancel) {
|
|
146
|
+
this.onCancel();
|
|
147
|
+
}
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.#handleSearchInput(keyData)) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
80
155
|
// Up arrow - go to previous (older) message, wrap to bottom when at top
|
|
81
156
|
if (matchesSelectUp(keyData)) {
|
|
82
|
-
|
|
157
|
+
if (this.#filteredMessages.length > 0) {
|
|
158
|
+
this.#selectedIndex =
|
|
159
|
+
this.#selectedIndex === 0 ? this.#filteredMessages.length - 1 : this.#selectedIndex - 1;
|
|
160
|
+
}
|
|
83
161
|
}
|
|
84
162
|
// Down arrow - go to next (newer) message, wrap to top when at bottom
|
|
85
163
|
else if (matchesSelectDown(keyData)) {
|
|
86
|
-
|
|
164
|
+
if (this.#filteredMessages.length > 0) {
|
|
165
|
+
this.#selectedIndex =
|
|
166
|
+
this.#selectedIndex === this.#filteredMessages.length - 1 ? 0 : this.#selectedIndex + 1;
|
|
167
|
+
}
|
|
87
168
|
}
|
|
88
169
|
// Enter - select message and branch
|
|
89
170
|
else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
|
|
90
|
-
const selected = this
|
|
171
|
+
const selected = this.#filteredMessages[this.#selectedIndex];
|
|
91
172
|
if (selected && this.onSelect) {
|
|
92
173
|
this.onSelect(selected.id);
|
|
93
174
|
}
|
|
94
175
|
}
|
|
95
|
-
// Escape / cancel
|
|
96
|
-
else if (matchesSelectCancel(keyData)) {
|
|
97
|
-
if (this.onCancel) {
|
|
98
|
-
this.onCancel();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
176
|
}
|
|
102
177
|
}
|
|
103
178
|
|