@mariozechner/pi-coding-agent 0.49.2 → 0.50.0
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 +126 -1
- package/README.md +310 -1229
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +57 -22
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts +14 -0
- package/dist/cli/config-selector.d.ts.map +1 -0
- package/dist/cli/config-selector.js +31 -0
- package/dist/cli/config-selector.js.map +1 -0
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +53 -34
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +262 -67
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +8 -18
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +39 -55
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +2 -1
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/diagnostics.d.ts +15 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/export-html/template.css +9 -0
- package/dist/core/export-html/template.js +6 -4
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -1
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +9 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +39 -12
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +112 -1
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +9 -2
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +13 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/model-registry.d.ts +42 -2
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +154 -44
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +3 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +129 -0
- package/dist/core/package-manager.d.ts.map +1 -0
- package/dist/core/package-manager.js +1148 -0
- package/dist/core/package-manager.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +6 -0
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +114 -54
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/resource-loader.d.ts +160 -0
- package/dist/core/resource-loader.d.ts.map +1 -0
- package/dist/core/resource-loader.js +604 -0
- package/dist/core/resource-loader.js.map +1 -0
- package/dist/core/sdk.d.ts +14 -105
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +52 -304
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +45 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +39 -16
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +107 -25
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +18 -10
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +126 -93
- package/dist/core/skills.js.map +1 -1
- package/dist/core/system-prompt.d.ts +3 -27
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +16 -103
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -4
- package/dist/core/tools/read.js.map +1 -1
- package/dist/index.d.ts +12 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +209 -97
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +3 -2
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +5 -3
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +5 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +29 -9
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +3 -2
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +4 -2
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +3 -2
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +4 -2
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +71 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/config-selector.js +468 -0
- package/dist/modes/interactive/components/config-selector.js.map +1 -0
- package/dist/modes/interactive/components/custom-message.d.ts +3 -2
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +4 -2
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +9 -0
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +3 -4
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +18 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +195 -87
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -5
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +2 -2
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +2 -2
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +2 -2
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +47 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +566 -211
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -1
- package/dist/modes/interactive/theme/light.json +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +8 -1
- package/dist/modes/interactive/theme/theme.d.ts +8 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +79 -28
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +25 -89
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +32 -92
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/git.d.ts +2 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +6 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/shell.d.ts +1 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +16 -2
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/sleep.d.ts +5 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +17 -0
- package/dist/utils/sleep.js.map +1 -0
- package/docs/compaction.md +23 -21
- package/docs/custom-provider.md +538 -0
- package/docs/development.md +69 -0
- package/docs/extensions.md +180 -118
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/json.md +79 -0
- package/docs/keybindings.md +162 -0
- package/docs/models.md +193 -0
- package/docs/packages.md +163 -0
- package/docs/prompt-templates.md +67 -0
- package/docs/providers.md +147 -0
- package/docs/sdk.md +111 -178
- package/docs/session.md +167 -16
- package/docs/settings.md +216 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +111 -202
- package/docs/terminal-setup.md +65 -0
- package/docs/themes.md +295 -0
- package/docs/tui.md +36 -5
- package/docs/windows.md +17 -0
- package/examples/README.md +1 -0
- package/examples/extensions/README.md +24 -2
- package/examples/extensions/antigravity-image-gen.ts +413 -0
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +349 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
- package/examples/extensions/doom-overlay/doom/build.sh +1 -1
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/inline-bash.ts +94 -0
- package/examples/extensions/message-renderer.ts +59 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/space-invaders.ts +560 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/02-custom-model.ts +3 -3
- package/examples/sdk/03-custom-prompt.ts +20 -9
- package/examples/sdk/04-skills.ts +26 -27
- package/examples/sdk/06-extensions.ts +15 -6
- package/examples/sdk/07-context-files.ts +22 -18
- package/examples/sdk/08-prompt-templates.ts +19 -14
- package/examples/sdk/09-api-keys-and-oauth.ts +5 -12
- package/examples/sdk/10-settings.ts +3 -3
- package/examples/sdk/12-full-control.ts +16 -7
- package/examples/sdk/README.md +24 -30
- package/package.json +4 -4
- package/docs/theme.md +0 -617
- package/examples/extensions/chalk-logger.ts +0 -26
|
@@ -2,10 +2,10 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { unlink } from "node:fs/promises";
|
|
4
4
|
import * as os from "node:os";
|
|
5
|
-
import { Container, getEditorKeybindings, Input, matchesKey, Spacer, truncateToWidth, visibleWidth, } from "@mariozechner/pi-tui";
|
|
5
|
+
import { Container, getEditorKeybindings, Input, matchesKey, Spacer, Text, truncateToWidth, visibleWidth, } from "@mariozechner/pi-tui";
|
|
6
6
|
import { theme } from "../theme/theme.js";
|
|
7
7
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
8
|
-
import { keyHint
|
|
8
|
+
import { keyHint } from "./keybinding-hints.js";
|
|
9
9
|
import { filterAndSortSessions } from "./session-selector-search.js";
|
|
10
10
|
function shortenPath(path) {
|
|
11
11
|
const home = os.homedir();
|
|
@@ -44,6 +44,7 @@ class SessionSelectorHeader {
|
|
|
44
44
|
confirmingDeletePath = null;
|
|
45
45
|
statusMessage = null;
|
|
46
46
|
statusTimeout = null;
|
|
47
|
+
showRenameHint = false;
|
|
47
48
|
constructor(scope, sortMode, requestRender) {
|
|
48
49
|
this.scope = scope;
|
|
49
50
|
this.sortMode = sortMode;
|
|
@@ -66,6 +67,9 @@ class SessionSelectorHeader {
|
|
|
66
67
|
setShowPath(showPath) {
|
|
67
68
|
this.showPath = showPath;
|
|
68
69
|
}
|
|
70
|
+
setShowRenameHint(show) {
|
|
71
|
+
this.showRenameHint = show;
|
|
72
|
+
}
|
|
69
73
|
setConfirmingDeletePath(path) {
|
|
70
74
|
this.confirmingDeletePath = path;
|
|
71
75
|
}
|
|
@@ -124,11 +128,15 @@ class SessionSelectorHeader {
|
|
|
124
128
|
const pathState = this.showPath ? "(on)" : "(off)";
|
|
125
129
|
const sep = theme.fg("muted", " · ");
|
|
126
130
|
const hint1 = keyHint("tab", "scope") + sep + theme.fg("muted", 're:<pattern> regex · "phrase" exact');
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
const hint2Parts = [
|
|
132
|
+
keyHint("toggleSessionSort", "sort"),
|
|
133
|
+
keyHint("deleteSession", "delete"),
|
|
134
|
+
keyHint("toggleSessionPath", `path ${pathState}`),
|
|
135
|
+
];
|
|
136
|
+
if (this.showRenameHint) {
|
|
137
|
+
hint2Parts.push(keyHint("renameSession", "rename"));
|
|
138
|
+
}
|
|
139
|
+
const hint2 = hint2Parts.join(sep);
|
|
132
140
|
hintLine1 = truncateToWidth(hint1, width, "…");
|
|
133
141
|
hintLine2 = truncateToWidth(hint2, width, "…");
|
|
134
142
|
}
|
|
@@ -139,6 +147,10 @@ class SessionSelectorHeader {
|
|
|
139
147
|
* Custom session list component with multi-line items and search
|
|
140
148
|
*/
|
|
141
149
|
class SessionList {
|
|
150
|
+
getSelectedSessionPath() {
|
|
151
|
+
const selected = this.filteredSessions[this.selectedIndex];
|
|
152
|
+
return selected?.path;
|
|
153
|
+
}
|
|
142
154
|
allSessions = [];
|
|
143
155
|
filteredSessions = [];
|
|
144
156
|
selectedIndex = 0;
|
|
@@ -156,6 +168,7 @@ class SessionList {
|
|
|
156
168
|
onTogglePath;
|
|
157
169
|
onDeleteConfirmationChange;
|
|
158
170
|
onDeleteSession;
|
|
171
|
+
onRenameSession;
|
|
159
172
|
onError;
|
|
160
173
|
maxVisible = 5; // Max sessions visible (each session: message + metadata + optional path + blank)
|
|
161
174
|
// Focusable implementation - propagate to searchInput for IME cursor positioning
|
|
@@ -311,24 +324,32 @@ class SessionList {
|
|
|
311
324
|
}
|
|
312
325
|
return;
|
|
313
326
|
}
|
|
314
|
-
if (
|
|
327
|
+
if (kb.matches(keyData, "toggleSessionSort")) {
|
|
315
328
|
this.onToggleSort?.();
|
|
316
329
|
return;
|
|
317
330
|
}
|
|
318
331
|
// Ctrl+P: toggle path display
|
|
319
|
-
if (
|
|
332
|
+
if (kb.matches(keyData, "toggleSessionPath")) {
|
|
320
333
|
this.showPath = !this.showPath;
|
|
321
334
|
this.onTogglePath?.(this.showPath);
|
|
322
335
|
return;
|
|
323
336
|
}
|
|
324
337
|
// Ctrl+D: initiate delete confirmation (useful on terminals that don't distinguish Ctrl+Backspace from Backspace)
|
|
325
|
-
if (
|
|
338
|
+
if (kb.matches(keyData, "deleteSession")) {
|
|
326
339
|
this.startDeleteConfirmationForSelectedSession();
|
|
327
340
|
return;
|
|
328
341
|
}
|
|
342
|
+
// Ctrl+R: rename selected session
|
|
343
|
+
if (matchesKey(keyData, "ctrl+r")) {
|
|
344
|
+
const selected = this.filteredSessions[this.selectedIndex];
|
|
345
|
+
if (selected) {
|
|
346
|
+
this.onRenameSession?.(selected.path);
|
|
347
|
+
}
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
329
350
|
// Ctrl+Backspace: non-invasive convenience alias for delete
|
|
330
351
|
// Only triggers deletion when the query is empty; otherwise it is forwarded to the input
|
|
331
|
-
if (
|
|
352
|
+
if (kb.matches(keyData, "deleteSessionNoninvasive")) {
|
|
332
353
|
if (this.searchInput.getValue().length > 0) {
|
|
333
354
|
this.searchInput.handleInput(keyData);
|
|
334
355
|
this.filterSessions(this.searchInput.getValue());
|
|
@@ -413,6 +434,19 @@ async function deleteSessionFile(sessionPath) {
|
|
|
413
434
|
* Component that renders a session selector
|
|
414
435
|
*/
|
|
415
436
|
export class SessionSelectorComponent extends Container {
|
|
437
|
+
handleInput(data) {
|
|
438
|
+
if (this.mode === "rename") {
|
|
439
|
+
const kb = getEditorKeybindings();
|
|
440
|
+
if (kb.matches(data, "selectCancel") || matchesKey(data, "ctrl+c")) {
|
|
441
|
+
this.exitRenameMode();
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
this.renameInput.handleInput(data);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
this.sessionList.handleInput(data);
|
|
448
|
+
}
|
|
449
|
+
canRename = true;
|
|
416
450
|
sessionList;
|
|
417
451
|
header;
|
|
418
452
|
scope = "current";
|
|
@@ -423,9 +457,13 @@ export class SessionSelectorComponent extends Container {
|
|
|
423
457
|
allSessionsLoader;
|
|
424
458
|
onCancel;
|
|
425
459
|
requestRender;
|
|
460
|
+
renameSession;
|
|
426
461
|
currentLoading = false;
|
|
427
462
|
allLoading = false;
|
|
428
463
|
allLoadSeq = 0;
|
|
464
|
+
mode = "list";
|
|
465
|
+
renameInput = new Input();
|
|
466
|
+
renameTargetPath = null;
|
|
429
467
|
// Focusable implementation - propagate to sessionList for IME cursor positioning
|
|
430
468
|
_focused = false;
|
|
431
469
|
get focused() {
|
|
@@ -434,22 +472,41 @@ export class SessionSelectorComponent extends Container {
|
|
|
434
472
|
set focused(value) {
|
|
435
473
|
this._focused = value;
|
|
436
474
|
this.sessionList.focused = value;
|
|
475
|
+
this.renameInput.focused = value;
|
|
476
|
+
if (value && this.mode === "rename") {
|
|
477
|
+
this.renameInput.focused = true;
|
|
478
|
+
}
|
|
437
479
|
}
|
|
438
|
-
|
|
480
|
+
buildBaseLayout(content, options) {
|
|
481
|
+
this.clear();
|
|
482
|
+
this.addChild(new Spacer(1));
|
|
483
|
+
this.addChild(new DynamicBorder((s) => theme.fg("accent", s)));
|
|
484
|
+
this.addChild(new Spacer(1));
|
|
485
|
+
if (options?.showHeader ?? true) {
|
|
486
|
+
this.addChild(this.header);
|
|
487
|
+
this.addChild(new Spacer(1));
|
|
488
|
+
}
|
|
489
|
+
this.addChild(content);
|
|
490
|
+
this.addChild(new Spacer(1));
|
|
491
|
+
this.addChild(new DynamicBorder((s) => theme.fg("accent", s)));
|
|
492
|
+
}
|
|
493
|
+
constructor(currentSessionsLoader, allSessionsLoader, onSelect, onCancel, onExit, requestRender, options, currentSessionFilePath) {
|
|
439
494
|
super();
|
|
440
495
|
this.currentSessionsLoader = currentSessionsLoader;
|
|
441
496
|
this.allSessionsLoader = allSessionsLoader;
|
|
442
497
|
this.onCancel = onCancel;
|
|
443
498
|
this.requestRender = requestRender;
|
|
444
499
|
this.header = new SessionSelectorHeader(this.scope, this.sortMode, this.requestRender);
|
|
445
|
-
|
|
446
|
-
this.
|
|
447
|
-
this.
|
|
448
|
-
this.
|
|
449
|
-
this.addChild(this.header);
|
|
450
|
-
this.addChild(new Spacer(1));
|
|
500
|
+
const renameSession = options?.renameSession;
|
|
501
|
+
this.renameSession = renameSession;
|
|
502
|
+
this.canRename = !!renameSession;
|
|
503
|
+
this.header.setShowRenameHint(options?.showRenameHint ?? this.canRename);
|
|
451
504
|
// Create session list (starts empty, will be populated after load)
|
|
452
505
|
this.sessionList = new SessionList([], false, this.sortMode, currentSessionFilePath);
|
|
506
|
+
this.buildBaseLayout(this.sessionList);
|
|
507
|
+
this.renameInput.onSubmit = (value) => {
|
|
508
|
+
void this.confirmRename(value);
|
|
509
|
+
};
|
|
453
510
|
// Ensure header status timeouts are cleared when leaving the selector
|
|
454
511
|
const clearStatusMessage = () => this.header.setStatusMessage(null);
|
|
455
512
|
this.sessionList.onSelect = (sessionPath) => {
|
|
@@ -466,6 +523,17 @@ export class SessionSelectorComponent extends Container {
|
|
|
466
523
|
};
|
|
467
524
|
this.sessionList.onToggleScope = () => this.toggleScope();
|
|
468
525
|
this.sessionList.onToggleSort = () => this.toggleSortMode();
|
|
526
|
+
this.sessionList.onRenameSession = (sessionPath) => {
|
|
527
|
+
if (!renameSession)
|
|
528
|
+
return;
|
|
529
|
+
if (this.scope === "current" && this.currentLoading)
|
|
530
|
+
return;
|
|
531
|
+
if (this.scope === "all" && this.allLoading)
|
|
532
|
+
return;
|
|
533
|
+
const sessions = this.scope === "all" ? (this.allSessions ?? []) : (this.currentSessions ?? []);
|
|
534
|
+
const session = sessions.find((s) => s.path === sessionPath);
|
|
535
|
+
this.enterRenameMode(sessionPath, session?.name);
|
|
536
|
+
};
|
|
469
537
|
// Sync list events to header
|
|
470
538
|
this.sessionList.onTogglePath = (showPath) => {
|
|
471
539
|
this.header.setShowPath(showPath);
|
|
@@ -494,6 +562,7 @@ export class SessionSelectorComponent extends Container {
|
|
|
494
562
|
this.sessionList.setSessions(sessions, showCwd);
|
|
495
563
|
const msg = result.method === "trash" ? "Session moved to trash" : "Session deleted";
|
|
496
564
|
this.header.setStatusMessage({ type: "info", message: msg }, 2000);
|
|
565
|
+
await this.refreshSessionsAfterMutation();
|
|
497
566
|
}
|
|
498
567
|
else {
|
|
499
568
|
const errorMessage = result.error ?? "Unknown error";
|
|
@@ -501,43 +570,118 @@ export class SessionSelectorComponent extends Container {
|
|
|
501
570
|
}
|
|
502
571
|
this.requestRender();
|
|
503
572
|
};
|
|
504
|
-
this.addChild(this.sessionList);
|
|
505
|
-
// Add bottom border
|
|
506
|
-
this.addChild(new Spacer(1));
|
|
507
|
-
this.addChild(new DynamicBorder());
|
|
508
573
|
// Start loading current sessions immediately
|
|
509
574
|
this.loadCurrentSessions();
|
|
510
575
|
}
|
|
511
576
|
loadCurrentSessions() {
|
|
512
|
-
this.
|
|
513
|
-
|
|
577
|
+
void this.loadScope("current", "initial");
|
|
578
|
+
}
|
|
579
|
+
enterRenameMode(sessionPath, currentName) {
|
|
580
|
+
this.mode = "rename";
|
|
581
|
+
this.renameTargetPath = sessionPath;
|
|
582
|
+
this.renameInput.setValue(currentName ?? "");
|
|
583
|
+
this.renameInput.focused = true;
|
|
584
|
+
const panel = new Container();
|
|
585
|
+
panel.addChild(new Text(theme.bold("Rename Session"), 1, 0));
|
|
586
|
+
panel.addChild(new Spacer(1));
|
|
587
|
+
panel.addChild(this.renameInput);
|
|
588
|
+
panel.addChild(new Spacer(1));
|
|
589
|
+
panel.addChild(new Text(theme.fg("muted", "Enter to save · Esc/Ctrl+C to cancel"), 1, 0));
|
|
590
|
+
this.buildBaseLayout(panel, { showHeader: false });
|
|
591
|
+
this.requestRender();
|
|
592
|
+
}
|
|
593
|
+
exitRenameMode() {
|
|
594
|
+
this.mode = "list";
|
|
595
|
+
this.renameTargetPath = null;
|
|
596
|
+
this.buildBaseLayout(this.sessionList);
|
|
597
|
+
this.requestRender();
|
|
598
|
+
}
|
|
599
|
+
async confirmRename(value) {
|
|
600
|
+
const next = value.trim();
|
|
601
|
+
if (!next)
|
|
602
|
+
return;
|
|
603
|
+
const target = this.renameTargetPath;
|
|
604
|
+
if (!target) {
|
|
605
|
+
this.exitRenameMode();
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
// Find current name for callback
|
|
609
|
+
const renameSession = this.renameSession;
|
|
610
|
+
if (!renameSession) {
|
|
611
|
+
this.exitRenameMode();
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
try {
|
|
615
|
+
await renameSession(target, next);
|
|
616
|
+
await this.refreshSessionsAfterMutation();
|
|
617
|
+
}
|
|
618
|
+
finally {
|
|
619
|
+
this.exitRenameMode();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
async loadScope(scope, reason) {
|
|
623
|
+
const showCwd = scope === "all";
|
|
624
|
+
// Mark loading
|
|
625
|
+
if (scope === "current") {
|
|
626
|
+
this.currentLoading = true;
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
this.allLoading = true;
|
|
630
|
+
}
|
|
631
|
+
const seq = scope === "all" ? ++this.allLoadSeq : undefined;
|
|
632
|
+
this.header.setScope(scope);
|
|
514
633
|
this.header.setLoading(true);
|
|
515
634
|
this.requestRender();
|
|
516
|
-
|
|
517
|
-
if (
|
|
635
|
+
const onProgress = (loaded, total) => {
|
|
636
|
+
if (scope !== this.scope)
|
|
637
|
+
return;
|
|
638
|
+
if (seq !== undefined && seq !== this.allLoadSeq)
|
|
518
639
|
return;
|
|
519
640
|
this.header.setProgress(loaded, total);
|
|
520
641
|
this.requestRender();
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
642
|
+
};
|
|
643
|
+
try {
|
|
644
|
+
const sessions = await (scope === "current"
|
|
645
|
+
? this.currentSessionsLoader(onProgress)
|
|
646
|
+
: this.allSessionsLoader(onProgress));
|
|
647
|
+
if (scope === "current") {
|
|
648
|
+
this.currentSessions = sessions;
|
|
649
|
+
this.currentLoading = false;
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
this.allSessions = sessions;
|
|
653
|
+
this.allLoading = false;
|
|
654
|
+
}
|
|
655
|
+
if (scope !== this.scope)
|
|
656
|
+
return;
|
|
657
|
+
if (seq !== undefined && seq !== this.allLoadSeq)
|
|
526
658
|
return;
|
|
527
659
|
this.header.setLoading(false);
|
|
528
|
-
this.sessionList.setSessions(sessions,
|
|
660
|
+
this.sessionList.setSessions(sessions, showCwd);
|
|
529
661
|
this.requestRender();
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
662
|
+
if (scope === "all" && sessions.length === 0 && (this.currentSessions?.length ?? 0) === 0) {
|
|
663
|
+
this.onCancel();
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
catch (err) {
|
|
667
|
+
if (scope === "current") {
|
|
668
|
+
this.currentLoading = false;
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
this.allLoading = false;
|
|
672
|
+
}
|
|
673
|
+
if (scope !== this.scope)
|
|
535
674
|
return;
|
|
675
|
+
if (seq !== undefined && seq !== this.allLoadSeq)
|
|
676
|
+
return;
|
|
677
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
536
678
|
this.header.setLoading(false);
|
|
537
679
|
this.header.setStatusMessage({ type: "error", message: `Failed to load sessions: ${message}` }, 4000);
|
|
538
|
-
|
|
680
|
+
if (reason === "initial") {
|
|
681
|
+
this.sessionList.setSessions([], showCwd);
|
|
682
|
+
}
|
|
539
683
|
this.requestRender();
|
|
540
|
-
}
|
|
684
|
+
}
|
|
541
685
|
}
|
|
542
686
|
toggleSortMode() {
|
|
543
687
|
this.sortMode = this.sortMode === "recent" ? "relevance" : "recent";
|
|
@@ -545,6 +689,9 @@ export class SessionSelectorComponent extends Container {
|
|
|
545
689
|
this.sessionList.setSortMode(this.sortMode);
|
|
546
690
|
this.requestRender();
|
|
547
691
|
}
|
|
692
|
+
async refreshSessionsAfterMutation() {
|
|
693
|
+
await this.loadScope(this.scope, "refresh");
|
|
694
|
+
}
|
|
548
695
|
toggleScope() {
|
|
549
696
|
if (this.scope === "current") {
|
|
550
697
|
this.scope = "all";
|
|
@@ -555,55 +702,16 @@ export class SessionSelectorComponent extends Container {
|
|
|
555
702
|
this.requestRender();
|
|
556
703
|
return;
|
|
557
704
|
}
|
|
558
|
-
this.
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
return;
|
|
563
|
-
this.allLoading = true;
|
|
564
|
-
const seq = ++this.allLoadSeq;
|
|
565
|
-
this.allSessionsLoader((loaded, total) => {
|
|
566
|
-
if (seq !== this.allLoadSeq)
|
|
567
|
-
return;
|
|
568
|
-
if (this.scope !== "all")
|
|
569
|
-
return;
|
|
570
|
-
this.header.setProgress(loaded, total);
|
|
571
|
-
this.requestRender();
|
|
572
|
-
})
|
|
573
|
-
.then((sessions) => {
|
|
574
|
-
this.allSessions = sessions;
|
|
575
|
-
this.allLoading = false;
|
|
576
|
-
if (seq !== this.allLoadSeq)
|
|
577
|
-
return;
|
|
578
|
-
if (this.scope !== "all")
|
|
579
|
-
return;
|
|
580
|
-
this.header.setLoading(false);
|
|
581
|
-
this.sessionList.setSessions(sessions, true);
|
|
582
|
-
this.requestRender();
|
|
583
|
-
if (sessions.length === 0 && (this.currentSessions?.length ?? 0) === 0) {
|
|
584
|
-
this.onCancel();
|
|
585
|
-
}
|
|
586
|
-
})
|
|
587
|
-
.catch((error) => {
|
|
588
|
-
this.allLoading = false;
|
|
589
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
590
|
-
if (seq !== this.allLoadSeq)
|
|
591
|
-
return;
|
|
592
|
-
if (this.scope !== "all")
|
|
593
|
-
return;
|
|
594
|
-
this.header.setLoading(false);
|
|
595
|
-
this.header.setStatusMessage({ type: "error", message: `Failed to load sessions: ${message}` }, 4000);
|
|
596
|
-
this.sessionList.setSessions([], true);
|
|
597
|
-
this.requestRender();
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
else {
|
|
601
|
-
this.scope = "current";
|
|
602
|
-
this.header.setScope(this.scope);
|
|
603
|
-
this.header.setLoading(this.currentLoading);
|
|
604
|
-
this.sessionList.setSessions(this.currentSessions ?? [], false);
|
|
605
|
-
this.requestRender();
|
|
705
|
+
if (!this.allLoading) {
|
|
706
|
+
void this.loadScope("all", "toggle");
|
|
707
|
+
}
|
|
708
|
+
return;
|
|
606
709
|
}
|
|
710
|
+
this.scope = "current";
|
|
711
|
+
this.header.setScope(this.scope);
|
|
712
|
+
this.header.setLoading(this.currentLoading);
|
|
713
|
+
this.sessionList.setSessions(this.currentSessions ?? [], false);
|
|
714
|
+
this.requestRender();
|
|
607
715
|
}
|
|
608
716
|
getSessionList() {
|
|
609
717
|
return this.sessionList;
|