@averyyy/pi-coding-agent 0.84.2-piclient.3 → 0.84.4-piclient.1
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 +127 -0
- package/README.md +13 -8
- package/dist/bundle/chunks/anthropic-messages-JWX2WP65.js +45 -0
- package/dist/bundle/chunks/anthropic.js +75 -0
- package/dist/bundle/chunks/azure-openai-responses-B2RCCN5N.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +74 -0
- package/dist/bundle/chunks/chunk-2KVJKXS2.js +50 -0
- package/dist/bundle/chunks/chunk-4L3WN2XY.js +2 -0
- package/dist/bundle/chunks/chunk-6CYCI3EJ.js +12 -0
- package/dist/bundle/chunks/chunk-6R7UI4IY.js +2 -0
- package/dist/bundle/chunks/chunk-AP7VCR6I.js +1633 -0
- package/dist/bundle/chunks/chunk-AXIIZGTV.js +2 -0
- package/dist/bundle/chunks/chunk-DRQDKNXA.js +2 -0
- package/dist/bundle/chunks/chunk-GMWCTUPB.js +2 -0
- package/dist/bundle/chunks/chunk-JUUC5AZY.js +2 -0
- package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
- package/dist/bundle/chunks/chunk-LRBSW3NQ.js +11 -0
- package/dist/bundle/chunks/chunk-MNAIPA3J.js +2 -0
- package/dist/bundle/chunks/chunk-NUHFSC37.js +25 -0
- package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
- package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
- package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
- package/dist/bundle/chunks/chunk-TEPMHNKQ.js +2 -0
- package/dist/bundle/chunks/chunk-TRDNDS6A.js +4 -0
- package/dist/bundle/chunks/chunk-UAQELI3K.js +2 -0
- package/dist/bundle/chunks/chunk-XDUWOHPL.js +6 -0
- package/dist/bundle/chunks/chunk-XNGRGP62.js +2 -0
- package/dist/bundle/chunks/github-copilot.js +2 -0
- package/dist/bundle/chunks/google-generative-ai-YMUPJBKR.js +2 -0
- package/dist/bundle/chunks/google-vertex-MPWMV4OF.js +2 -0
- package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +13 -0
- package/dist/bundle/chunks/image-resize-worker.js +2 -0
- package/dist/bundle/chunks/kimi-coding.js +2 -0
- package/dist/bundle/chunks/lib-HGQMNYVT.js +9 -0
- package/dist/bundle/chunks/mistral-conversations-Q3AWZJAZ.js +5 -0
- package/dist/bundle/chunks/multipart-parser-SQ6GDJL7.js +3 -0
- package/dist/bundle/chunks/openai-codex-responses-MJHX7NXI.js +8 -0
- package/dist/bundle/chunks/openai-codex.js +75 -0
- package/dist/bundle/chunks/openai-completions-ERMU2SS7.js +7 -0
- package/dist/bundle/chunks/openai-responses-CP4VKB27.js +2 -0
- package/dist/bundle/chunks/openrouter-images-N4XVVFSD.js +2 -0
- package/dist/bundle/chunks/openrouter.js +75 -0
- package/dist/bundle/chunks/pi-messages-TPFH44NP.js +8 -0
- package/dist/bundle/chunks/radius.js +75 -0
- package/dist/bundle/chunks/src-UK77SYZE.js +4 -0
- package/dist/bundle/chunks/xai.js +2 -0
- package/dist/bundle/cli.js +3 -0
- package/dist/bundle/client.js +2 -0
- package/dist/bundle/index.js +2 -0
- package/dist/bundle/rpc-entry.js +3 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +29 -9
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +2 -1
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/config.d.ts +4 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +22 -14
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +27 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +150 -58
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +6 -7
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +8 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +9 -4
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +42 -22
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +3 -1
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +115 -48
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +45 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +56 -10
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +20 -19
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-config.d.ts +5 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +3 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +14 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +22 -14
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-client-cli-adapter.d.ts +11 -0
- package/dist/core/pi-client-cli-adapter.d.ts.map +1 -0
- package/dist/core/pi-client-cli-adapter.js +91 -0
- package/dist/core/pi-client-cli-adapter.js.map +1 -0
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -3
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +6 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +17 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +69 -10
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +11 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +13 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +30 -14
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -4
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts +4 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +18 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/extensions/llama/client.d.ts +6 -0
- package/dist/extensions/llama/client.d.ts.map +1 -1
- package/dist/extensions/llama/client.js +7 -0
- package/dist/extensions/llama/client.js.map +1 -1
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts +3 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +29 -4
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +22 -15
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +2 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +9 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +56 -12
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +9 -2
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +128 -56
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +17 -12
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +244 -250
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/session-share.d.ts +16 -0
- package/dist/modes/interactive/session-share.d.ts.map +1 -0
- package/dist/modes/interactive/session-share.js +188 -0
- package/dist/modes/interactive/session-share.js.map +1 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +4 -3
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +16 -13
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +20 -6
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +7 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +12 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +186 -2
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +8 -3
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +27 -11
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +2 -1
- package/dist/utils/version-check.js.map +1 -1
- package/docs/compaction.md +17 -0
- package/docs/custom-provider.md +12 -9
- package/docs/environment-variables.md +11 -8
- package/docs/extensions.md +35 -7
- package/docs/json.md +7 -2
- package/docs/keybindings.md +9 -9
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/packages.md +2 -2
- package/docs/quickstart.md +1 -1
- package/docs/rpc.md +32 -3
- package/docs/sdk.md +7 -2
- package/docs/settings.md +23 -7
- package/docs/skills.md +4 -3
- package/docs/terminal-setup.md +23 -9
- package/docs/usage.md +10 -5
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +2 -2
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/subagent/index.ts +6 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/package.json +12 -14
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Container, fuzzyFilter, getKeybindings, Input, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
2
|
+
import { getSelectListTheme, theme } from "../theme/theme.js";
|
|
3
|
+
const SUBMENU_SELECT_LIST_LAYOUT = {
|
|
4
|
+
minPrimaryColumnWidth: 12,
|
|
5
|
+
maxPrimaryColumnWidth: 32,
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Single-step submenu that shows a titled select list.
|
|
9
|
+
* With `searchable: true`, typing filters the list using fuzzy matching.
|
|
10
|
+
*/
|
|
11
|
+
export class SelectSubmenu extends Container {
|
|
12
|
+
selectList;
|
|
13
|
+
listChildIndex;
|
|
14
|
+
allOptions;
|
|
15
|
+
listLayout;
|
|
16
|
+
searchInput;
|
|
17
|
+
onSelectCb;
|
|
18
|
+
onCancelCb;
|
|
19
|
+
onSelectionChangeCb;
|
|
20
|
+
constructor(title, description, options, currentValue, onSelect, onCancel, onSelectionChange, submenuOptions) {
|
|
21
|
+
super();
|
|
22
|
+
this.allOptions = options;
|
|
23
|
+
this.listLayout = submenuOptions?.layout ?? SUBMENU_SELECT_LIST_LAYOUT;
|
|
24
|
+
this.onSelectCb = onSelect;
|
|
25
|
+
this.onCancelCb = onCancel;
|
|
26
|
+
this.onSelectionChangeCb = onSelectionChange;
|
|
27
|
+
// Title
|
|
28
|
+
this.addChild(new Text(theme.bold(theme.fg("accent", title)), 0, 0));
|
|
29
|
+
// Description
|
|
30
|
+
if (description) {
|
|
31
|
+
this.addChild(new Spacer(1));
|
|
32
|
+
this.addChild(new Text(theme.fg("muted", description), 0, 0));
|
|
33
|
+
}
|
|
34
|
+
// Search input
|
|
35
|
+
if (submenuOptions?.searchable) {
|
|
36
|
+
this.addChild(new Spacer(1));
|
|
37
|
+
this.searchInput = new Input();
|
|
38
|
+
this.searchInput.onSubmit = () => {
|
|
39
|
+
this.selectList.handleInput("\r");
|
|
40
|
+
};
|
|
41
|
+
this.addChild(this.searchInput);
|
|
42
|
+
}
|
|
43
|
+
// Spacer
|
|
44
|
+
this.addChild(new Spacer(1));
|
|
45
|
+
// Select list
|
|
46
|
+
this.selectList = this.buildSelectList(options, currentValue);
|
|
47
|
+
this.listChildIndex = this.children.length;
|
|
48
|
+
this.addChild(this.selectList);
|
|
49
|
+
// Hint
|
|
50
|
+
this.addChild(new Spacer(1));
|
|
51
|
+
const hint = submenuOptions?.searchable
|
|
52
|
+
? " Type to filter \u00b7 Enter to select \u00b7 Esc to go back"
|
|
53
|
+
: " Enter to select \u00b7 Esc to go back";
|
|
54
|
+
this.addChild(new Text(theme.fg("dim", hint), 0, 0));
|
|
55
|
+
}
|
|
56
|
+
buildSelectList(options, preselect) {
|
|
57
|
+
const list = new SelectList(options, Math.min(options.length, 10), getSelectListTheme(), this.listLayout);
|
|
58
|
+
const idx = options.findIndex((o) => o.value === preselect);
|
|
59
|
+
if (idx !== -1)
|
|
60
|
+
list.setSelectedIndex(idx);
|
|
61
|
+
list.onSelect = (item) => this.onSelectCb(item.value);
|
|
62
|
+
list.onCancel = this.onCancelCb;
|
|
63
|
+
if (this.onSelectionChangeCb) {
|
|
64
|
+
const cb = this.onSelectionChangeCb;
|
|
65
|
+
list.onSelectionChange = (item) => cb(item.value);
|
|
66
|
+
}
|
|
67
|
+
return list;
|
|
68
|
+
}
|
|
69
|
+
applyFilter(query) {
|
|
70
|
+
const filtered = query
|
|
71
|
+
? fuzzyFilter(this.allOptions, query, (item) => `${item.label} ${item.description ?? ""}`)
|
|
72
|
+
: this.allOptions;
|
|
73
|
+
const newList = this.buildSelectList(filtered, "");
|
|
74
|
+
this.children[this.listChildIndex] = newList;
|
|
75
|
+
this.selectList = newList;
|
|
76
|
+
}
|
|
77
|
+
handleInput(data) {
|
|
78
|
+
if (this.searchInput) {
|
|
79
|
+
const kb = getKeybindings();
|
|
80
|
+
const isNav = kb.matches(data, "tui.select.up") ||
|
|
81
|
+
kb.matches(data, "tui.select.down") ||
|
|
82
|
+
kb.matches(data, "tui.select.confirm") ||
|
|
83
|
+
kb.matches(data, "tui.select.cancel");
|
|
84
|
+
if (isNav) {
|
|
85
|
+
this.selectList.handleInput(data);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
this.searchInput.handleInput(data);
|
|
89
|
+
this.applyFilter(this.searchInput.getValue());
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
this.selectList.handleInput(data);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Generic N-step submenu built on top of {@link SelectSubmenu}.
|
|
99
|
+
*
|
|
100
|
+
* Each step's options can depend on prior selections via the shared context.
|
|
101
|
+
* Esc goes back one step; Esc at step 0 cancels.
|
|
102
|
+
* With `loop: true`, completing the final step invokes `onComplete` then returns to step 0.
|
|
103
|
+
*/
|
|
104
|
+
export class SteppedSubmenu extends Container {
|
|
105
|
+
steps;
|
|
106
|
+
onComplete;
|
|
107
|
+
onCancel;
|
|
108
|
+
opts;
|
|
109
|
+
activeComponent;
|
|
110
|
+
context;
|
|
111
|
+
constructor(steps, onComplete, onCancel, opts = {}) {
|
|
112
|
+
super();
|
|
113
|
+
this.steps = steps;
|
|
114
|
+
this.onComplete = onComplete;
|
|
115
|
+
this.onCancel = onCancel;
|
|
116
|
+
this.opts = opts;
|
|
117
|
+
this.context = { ...(opts.initialContext ?? {}) };
|
|
118
|
+
this.activeComponent = this.buildStep(opts.startAtStep ?? 0);
|
|
119
|
+
}
|
|
120
|
+
buildStep(stepIndex) {
|
|
121
|
+
const step = this.steps[stepIndex];
|
|
122
|
+
const total = this.steps.length;
|
|
123
|
+
const stepLabel = total > 1 ? `Step ${stepIndex + 1}/${total} \u00b7 ` : "";
|
|
124
|
+
const title = typeof step.title === "function" ? step.title(this.context) : step.title;
|
|
125
|
+
const desc = typeof step.description === "function" ? step.description(this.context) : step.description;
|
|
126
|
+
const items = step.options(this.context);
|
|
127
|
+
const preselect = step.preselect?.(this.context) ?? "";
|
|
128
|
+
return new SelectSubmenu(title, `${stepLabel}${desc}`, items, preselect, (value) => {
|
|
129
|
+
this.context[step.key] = value;
|
|
130
|
+
if (stepIndex < total - 1) {
|
|
131
|
+
// Advance to next step
|
|
132
|
+
this.activeComponent = this.buildStep(stepIndex + 1);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Final step \u2014 deliver result
|
|
136
|
+
this.onComplete({ ...this.context });
|
|
137
|
+
if (this.opts.loop) {
|
|
138
|
+
this.context = {};
|
|
139
|
+
this.activeComponent = this.buildStep(0);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.onCancel();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}, () => {
|
|
146
|
+
if (stepIndex > 0) {
|
|
147
|
+
delete this.context[step.key];
|
|
148
|
+
this.activeComponent = this.buildStep(stepIndex - 1);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.onCancel();
|
|
152
|
+
}
|
|
153
|
+
}, undefined, step.searchable || step.layout ? { searchable: step.searchable, layout: step.layout } : undefined);
|
|
154
|
+
}
|
|
155
|
+
render(width) {
|
|
156
|
+
return this.activeComponent.render(width);
|
|
157
|
+
}
|
|
158
|
+
handleInput(data) {
|
|
159
|
+
this.activeComponent.handleInput?.(data);
|
|
160
|
+
}
|
|
161
|
+
invalidate() {
|
|
162
|
+
this.activeComponent.invalidate?.();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=settings-submenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-submenu.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/settings-submenu.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Component,\n\tContainer,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\tSpacer,\n\tText,\n} from \"@earendil-works/pi-tui\";\nimport { getSelectListTheme, theme } from \"../theme/theme.ts\";\n\nconst SUBMENU_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nexport interface SelectSubmenuOptions {\n\t/** Enable type-to-search fuzzy filtering. */\n\tsearchable?: boolean;\n\t/** Override the select list layout (column widths). */\n\tlayout?: SelectListLayoutOptions;\n}\n\n/**\n * Single-step submenu that shows a titled select list.\n * With `searchable: true`, typing filters the list using fuzzy matching.\n */\nexport class SelectSubmenu extends Container {\n\tprivate selectList: SelectList;\n\tprivate listChildIndex: number;\n\tprivate allOptions: SelectItem[];\n\tprivate listLayout: SelectListLayoutOptions;\n\tprivate searchInput: Input | undefined;\n\tprivate onSelectCb: (value: string) => void;\n\tprivate onCancelCb: () => void;\n\tprivate onSelectionChangeCb?: (value: string) => void;\n\n\tconstructor(\n\t\ttitle: string,\n\t\tdescription: string,\n\t\toptions: SelectItem[],\n\t\tcurrentValue: string,\n\t\tonSelect: (value: string) => void,\n\t\tonCancel: () => void,\n\t\tonSelectionChange?: (value: string) => void,\n\t\tsubmenuOptions?: SelectSubmenuOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.allOptions = options;\n\t\tthis.listLayout = submenuOptions?.layout ?? SUBMENU_SELECT_LIST_LAYOUT;\n\t\tthis.onSelectCb = onSelect;\n\t\tthis.onCancelCb = onCancel;\n\t\tthis.onSelectionChangeCb = onSelectionChange;\n\n\t\t// Title\n\t\tthis.addChild(new Text(theme.bold(theme.fg(\"accent\", title)), 0, 0));\n\n\t\t// Description\n\t\tif (description) {\n\t\t\tthis.addChild(new Spacer(1));\n\t\t\tthis.addChild(new Text(theme.fg(\"muted\", description), 0, 0));\n\t\t}\n\n\t\t// Search input\n\t\tif (submenuOptions?.searchable) {\n\t\t\tthis.addChild(new Spacer(1));\n\t\t\tthis.searchInput = new Input();\n\t\t\tthis.searchInput.onSubmit = () => {\n\t\t\t\tthis.selectList.handleInput(\"\\r\");\n\t\t\t};\n\t\t\tthis.addChild(this.searchInput);\n\t\t}\n\n\t\t// Spacer\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Select list\n\t\tthis.selectList = this.buildSelectList(options, currentValue);\n\t\tthis.listChildIndex = this.children.length;\n\t\tthis.addChild(this.selectList);\n\n\t\t// Hint\n\t\tthis.addChild(new Spacer(1));\n\t\tconst hint = submenuOptions?.searchable\n\t\t\t? \" Type to filter \\u00b7 Enter to select \\u00b7 Esc to go back\"\n\t\t\t: \" Enter to select \\u00b7 Esc to go back\";\n\t\tthis.addChild(new Text(theme.fg(\"dim\", hint), 0, 0));\n\t}\n\n\tprivate buildSelectList(options: SelectItem[], preselect: string): SelectList {\n\t\tconst list = new SelectList(options, Math.min(options.length, 10), getSelectListTheme(), this.listLayout);\n\n\t\tconst idx = options.findIndex((o) => o.value === preselect);\n\t\tif (idx !== -1) list.setSelectedIndex(idx);\n\n\t\tlist.onSelect = (item) => this.onSelectCb(item.value);\n\t\tlist.onCancel = this.onCancelCb;\n\t\tif (this.onSelectionChangeCb) {\n\t\t\tconst cb = this.onSelectionChangeCb;\n\t\t\tlist.onSelectionChange = (item) => cb(item.value);\n\t\t}\n\n\t\treturn list;\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tconst filtered = query\n\t\t\t? fuzzyFilter(this.allOptions, query, (item) => `${item.label} ${item.description ?? \"\"}`)\n\t\t\t: this.allOptions;\n\n\t\tconst newList = this.buildSelectList(filtered, \"\");\n\t\tthis.children[this.listChildIndex] = newList;\n\t\tthis.selectList = newList;\n\t}\n\n\thandleInput(data: string): void {\n\t\tif (this.searchInput) {\n\t\t\tconst kb = getKeybindings();\n\t\t\tconst isNav =\n\t\t\t\tkb.matches(data, \"tui.select.up\") ||\n\t\t\t\tkb.matches(data, \"tui.select.down\") ||\n\t\t\t\tkb.matches(data, \"tui.select.confirm\") ||\n\t\t\t\tkb.matches(data, \"tui.select.cancel\");\n\t\t\tif (isNav) {\n\t\t\t\tthis.selectList.handleInput(data);\n\t\t\t} else {\n\t\t\t\tthis.searchInput.handleInput(data);\n\t\t\t\tthis.applyFilter(this.searchInput.getValue());\n\t\t\t}\n\t\t} else {\n\t\t\tthis.selectList.handleInput(data);\n\t\t}\n\t}\n}\n\n// ============================================================================\n// SteppedSubmenu — reusable multi-step selector\n// ============================================================================\n\n/** One step in a {@link SteppedSubmenu}. */\nexport interface SteppedSubmenuStep {\n\t/** Unique key \\u2014 the selected value is stored in the result context under this key. */\n\tkey: string;\n\t/** Title shown at the top of the step. Receives prior selections. */\n\ttitle: string | ((context: Record<string, string>) => string);\n\t/** Description shown below the title. Receives prior selections. */\n\tdescription: string | ((context: Record<string, string>) => string);\n\t/** Build the option list for this step. Called fresh each time the step is shown. */\n\toptions: (context: Record<string, string>) => SelectItem[];\n\t/** Optionally pre-select a value when entering this step. */\n\tpreselect?: (context: Record<string, string>) => string | undefined;\n\t/** Enable type-to-search fuzzy filtering for this step. */\n\tsearchable?: boolean;\n\t/** Override the select list layout (column widths) for this step. */\n\tlayout?: SelectListLayoutOptions;\n}\n\ninterface SteppedSubmenuOptions {\n\t/** Start at this step index (0-based), skipping earlier steps. Requires initialContext for skipped keys. */\n\tstartAtStep?: number;\n\t/** Pre-fill selections for skipped steps. */\n\tinitialContext?: Record<string, string>;\n\t/** After completing the last step, loop back to step 0 instead of closing. */\n\tloop?: boolean;\n}\n\n/**\n * Generic N-step submenu built on top of {@link SelectSubmenu}.\n *\n * Each step's options can depend on prior selections via the shared context.\n * Esc goes back one step; Esc at step 0 cancels.\n * With `loop: true`, completing the final step invokes `onComplete` then returns to step 0.\n */\nexport class SteppedSubmenu extends Container {\n\tprivate readonly steps: SteppedSubmenuStep[];\n\tprivate readonly onComplete: (context: Record<string, string>) => void;\n\tprivate readonly onCancel: () => void;\n\tprivate readonly opts: SteppedSubmenuOptions;\n\tprivate activeComponent: Component;\n\tprivate context: Record<string, string>;\n\n\tconstructor(\n\t\tsteps: SteppedSubmenuStep[],\n\t\tonComplete: (context: Record<string, string>) => void,\n\t\tonCancel: () => void,\n\t\topts: SteppedSubmenuOptions = {},\n\t) {\n\t\tsuper();\n\t\tthis.steps = steps;\n\t\tthis.onComplete = onComplete;\n\t\tthis.onCancel = onCancel;\n\t\tthis.opts = opts;\n\t\tthis.context = { ...(opts.initialContext ?? {}) };\n\t\tthis.activeComponent = this.buildStep(opts.startAtStep ?? 0);\n\t}\n\n\tprivate buildStep(stepIndex: number): Component {\n\t\tconst step = this.steps[stepIndex];\n\t\tconst total = this.steps.length;\n\t\tconst stepLabel = total > 1 ? `Step ${stepIndex + 1}/${total} \\u00b7 ` : \"\";\n\n\t\tconst title = typeof step.title === \"function\" ? step.title(this.context) : step.title;\n\t\tconst desc = typeof step.description === \"function\" ? step.description(this.context) : step.description;\n\t\tconst items = step.options(this.context);\n\t\tconst preselect = step.preselect?.(this.context) ?? \"\";\n\n\t\treturn new SelectSubmenu(\n\t\t\ttitle,\n\t\t\t`${stepLabel}${desc}`,\n\t\t\titems,\n\t\t\tpreselect,\n\t\t\t(value) => {\n\t\t\t\tthis.context[step.key] = value;\n\n\t\t\t\tif (stepIndex < total - 1) {\n\t\t\t\t\t// Advance to next step\n\t\t\t\t\tthis.activeComponent = this.buildStep(stepIndex + 1);\n\t\t\t\t} else {\n\t\t\t\t\t// Final step \\u2014 deliver result\n\t\t\t\t\tthis.onComplete({ ...this.context });\n\n\t\t\t\t\tif (this.opts.loop) {\n\t\t\t\t\t\tthis.context = {};\n\t\t\t\t\t\tthis.activeComponent = this.buildStep(0);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.onCancel();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t\t() => {\n\t\t\t\tif (stepIndex > 0) {\n\t\t\t\t\tdelete this.context[step.key];\n\t\t\t\t\tthis.activeComponent = this.buildStep(stepIndex - 1);\n\t\t\t\t} else {\n\t\t\t\t\tthis.onCancel();\n\t\t\t\t}\n\t\t\t},\n\t\t\tundefined,\n\t\t\tstep.searchable || step.layout ? { searchable: step.searchable, layout: step.layout } : undefined,\n\t\t);\n\t}\n\n\trender(width: number): string[] {\n\t\treturn this.activeComponent.render(width);\n\t}\n\n\thandleInput(data: string): void {\n\t\tthis.activeComponent.handleInput?.(data);\n\t}\n\n\tinvalidate(): void {\n\t\tthis.activeComponent.invalidate?.();\n\t}\n}\n"]}
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
|
-
import { Container, SelectList } from "@earendil-works/pi-tui";
|
|
2
|
+
import { Container, type Focusable, SelectList } from "@earendil-works/pi-tui";
|
|
3
3
|
/**
|
|
4
4
|
* Component that renders a thinking level selector with borders
|
|
5
5
|
*/
|
|
6
|
-
export declare class ThinkingSelectorComponent extends Container {
|
|
6
|
+
export declare class ThinkingSelectorComponent extends Container implements Focusable {
|
|
7
|
+
private searchInput;
|
|
7
8
|
private selectList;
|
|
8
|
-
|
|
9
|
+
private selectListChildIndex;
|
|
10
|
+
private allItems;
|
|
11
|
+
private onSelect;
|
|
12
|
+
private onCancel;
|
|
13
|
+
private onSelectAsDefault?;
|
|
14
|
+
private _focused;
|
|
15
|
+
get focused(): boolean;
|
|
16
|
+
set focused(value: boolean);
|
|
17
|
+
constructor(currentLevel: ThinkingLevel, availableLevels: ThinkingLevel[], onSelect: (level: ThinkingLevel) => void, onCancel: () => void, onSelectAsDefault?: (level: ThinkingLevel) => void, defaultThinkingLevel?: ThinkingLevel);
|
|
18
|
+
private buildSelectList;
|
|
19
|
+
private applyFilter;
|
|
20
|
+
handleInput(keyData: string): void;
|
|
9
21
|
getSelectList(): SelectList;
|
|
10
22
|
}
|
|
11
23
|
//# sourceMappingURL=thinking-selector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinking-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,
|
|
1
|
+
{"version":3,"file":"thinking-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAMd,UAAU,EAIV,MAAM,wBAAwB,CAAC;AAoBhC;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAU,YAAW,SAAS;IAC5E,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,iBAAiB,CAAC,CAAiC;IAC3D,OAAO,CAAC,QAAQ,CAAS;IAEzB,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YACC,YAAY,EAAE,aAAa,EAC3B,eAAe,EAAE,aAAa,EAAE,EAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EACxC,QAAQ,EAAE,MAAM,IAAI,EACpB,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,EAClD,oBAAoB,CAAC,EAAE,aAAa,EAoCpC;IAED,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,WAAW;IAUnB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAoBjC;IAED,aAAa,IAAI,UAAU,CAE1B;CACD","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\tSpacer,\n\tText,\n} from \"@earendil-works/pi-tui\";\nimport { getSelectListTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyDisplayText } from \"./keybinding-hints.ts\";\n\nconst THINKING_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nconst LEVEL_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Extra-high reasoning (~32k tokens)\",\n\tmax: \"Maximum reasoning\",\n};\n\n/**\n * Component that renders a thinking level selector with borders\n */\nexport class ThinkingSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\tprivate selectList: SelectList;\n\tprivate selectListChildIndex: number;\n\tprivate allItems: SelectItem[];\n\tprivate onSelect: (level: ThinkingLevel) => void;\n\tprivate onCancel: () => void;\n\tprivate onSelectAsDefault?: (level: ThinkingLevel) => void;\n\tprivate _focused = false;\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(\n\t\tcurrentLevel: ThinkingLevel,\n\t\tavailableLevels: ThinkingLevel[],\n\t\tonSelect: (level: ThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t\tonSelectAsDefault?: (level: ThinkingLevel) => void,\n\t\tdefaultThinkingLevel?: ThinkingLevel,\n\t) {\n\t\tsuper();\n\t\tthis.onSelect = onSelect;\n\t\tthis.onCancel = onCancel;\n\t\tthis.onSelectAsDefault = onSelectAsDefault;\n\n\t\tthis.allItems = availableLevels.map((level) => ({\n\t\t\tvalue: level,\n\t\t\tlabel: level,\n\t\t\tdescription:\n\t\t\t\tlevel === defaultThinkingLevel ? `${LEVEL_DESCRIPTIONS[level]} · default` : LEVEL_DESCRIPTIONS[level],\n\t\t}));\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(\"Thinking Level\", 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(`${keyDisplayText(\"app.thinking.cycle\")} cycles thinking levels in-session`, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.searchInput = new Input();\n\t\tthis.searchInput.onSubmit = () => this.selectList.handleInput(\"\\r\");\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create selector\n\t\tthis.selectList = this.buildSelectList(this.allItems, currentLevel);\n\t\tthis.selectListChildIndex = this.children.length;\n\t\tthis.addChild(this.selectList);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"dim\", \" Enter to select · Ctrl+S to set as default · Esc to cancel\"), 0, 0));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate buildSelectList(items: SelectItem[], preselect?: ThinkingLevel): SelectList {\n\t\tconst list = new SelectList(items, Math.max(1, items.length), getSelectListTheme(), THINKING_SELECT_LIST_LAYOUT);\n\t\tconst currentIndex = items.findIndex((item) => item.value === preselect);\n\t\tif (currentIndex !== -1) {\n\t\t\tlist.setSelectedIndex(currentIndex);\n\t\t}\n\t\tlist.onSelect = (item) => this.onSelect(item.value as ThinkingLevel);\n\t\tlist.onCancel = () => this.onCancel();\n\t\treturn list;\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tconst filtered = query\n\t\t\t? fuzzyFilter(this.allItems, query, (item) => `${item.label} ${item.description ?? \"\"}`)\n\t\t\t: this.allItems;\n\t\tconst selectedValue = this.selectList.getSelectedItem()?.value as ThinkingLevel | undefined;\n\t\tconst newList = this.buildSelectList(filtered, selectedValue);\n\t\tthis.children[this.selectListChildIndex] = newList;\n\t\tthis.selectList = newList;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tif (matchesKey(keyData, \"ctrl+s\") && this.onSelectAsDefault) {\n\t\t\tconst item = this.selectList.getSelectedItem();\n\t\t\tif (item) this.onSelectAsDefault(item.value as ThinkingLevel);\n\t\t\treturn;\n\t\t}\n\n\t\tconst kb = getKeybindings();\n\t\tconst isNav =\n\t\t\tkb.matches(keyData, \"tui.select.up\") ||\n\t\t\tkb.matches(keyData, \"tui.select.down\") ||\n\t\t\tkb.matches(keyData, \"tui.select.confirm\") ||\n\t\t\tkb.matches(keyData, \"tui.select.cancel\");\n\t\tif (isNav) {\n\t\t\tthis.selectList.handleInput(keyData);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.searchInput.handleInput(keyData);\n\t\tthis.applyFilter(this.searchInput.getValue());\n\t}\n\n\tgetSelectList(): SelectList {\n\t\treturn this.selectList;\n\t}\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Container, SelectList } from "@earendil-works/pi-tui";
|
|
2
|
-
import { getSelectListTheme } from "../theme/theme.js";
|
|
1
|
+
import { Container, fuzzyFilter, getKeybindings, Input, matchesKey, SelectList, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
2
|
+
import { getSelectListTheme, theme } from "../theme/theme.js";
|
|
3
3
|
import { DynamicBorder } from "./dynamic-border.js";
|
|
4
|
+
import { keyDisplayText } from "./keybinding-hints.js";
|
|
4
5
|
const THINKING_SELECT_LIST_LAYOUT = {
|
|
5
6
|
minPrimaryColumnWidth: 12,
|
|
6
7
|
maxPrimaryColumnWidth: 32,
|
|
@@ -18,33 +19,89 @@ const LEVEL_DESCRIPTIONS = {
|
|
|
18
19
|
* Component that renders a thinking level selector with borders
|
|
19
20
|
*/
|
|
20
21
|
export class ThinkingSelectorComponent extends Container {
|
|
22
|
+
searchInput;
|
|
21
23
|
selectList;
|
|
22
|
-
|
|
24
|
+
selectListChildIndex;
|
|
25
|
+
allItems;
|
|
26
|
+
onSelect;
|
|
27
|
+
onCancel;
|
|
28
|
+
onSelectAsDefault;
|
|
29
|
+
_focused = false;
|
|
30
|
+
get focused() {
|
|
31
|
+
return this._focused;
|
|
32
|
+
}
|
|
33
|
+
set focused(value) {
|
|
34
|
+
this._focused = value;
|
|
35
|
+
this.searchInput.focused = value;
|
|
36
|
+
}
|
|
37
|
+
constructor(currentLevel, availableLevels, onSelect, onCancel, onSelectAsDefault, defaultThinkingLevel) {
|
|
23
38
|
super();
|
|
24
|
-
|
|
39
|
+
this.onSelect = onSelect;
|
|
40
|
+
this.onCancel = onCancel;
|
|
41
|
+
this.onSelectAsDefault = onSelectAsDefault;
|
|
42
|
+
this.allItems = availableLevels.map((level) => ({
|
|
25
43
|
value: level,
|
|
26
44
|
label: level,
|
|
27
|
-
description: LEVEL_DESCRIPTIONS[level],
|
|
45
|
+
description: level === defaultThinkingLevel ? `${LEVEL_DESCRIPTIONS[level]} · default` : LEVEL_DESCRIPTIONS[level],
|
|
28
46
|
}));
|
|
29
47
|
// Add top border
|
|
30
48
|
this.addChild(new DynamicBorder());
|
|
49
|
+
this.addChild(new Spacer(1));
|
|
50
|
+
this.addChild(new Text("Thinking Level", 0, 0));
|
|
51
|
+
this.addChild(new Spacer(1));
|
|
52
|
+
this.addChild(new Text(`${keyDisplayText("app.thinking.cycle")} cycles thinking levels in-session`, 0, 0));
|
|
53
|
+
this.addChild(new Spacer(1));
|
|
54
|
+
this.searchInput = new Input();
|
|
55
|
+
this.searchInput.onSubmit = () => this.selectList.handleInput("\r");
|
|
56
|
+
this.addChild(this.searchInput);
|
|
57
|
+
this.addChild(new Spacer(1));
|
|
31
58
|
// Create selector
|
|
32
|
-
this.selectList =
|
|
33
|
-
|
|
34
|
-
const currentIndex = thinkingLevels.findIndex((item) => item.value === currentLevel);
|
|
35
|
-
if (currentIndex !== -1) {
|
|
36
|
-
this.selectList.setSelectedIndex(currentIndex);
|
|
37
|
-
}
|
|
38
|
-
this.selectList.onSelect = (item) => {
|
|
39
|
-
onSelect(item.value);
|
|
40
|
-
};
|
|
41
|
-
this.selectList.onCancel = () => {
|
|
42
|
-
onCancel();
|
|
43
|
-
};
|
|
59
|
+
this.selectList = this.buildSelectList(this.allItems, currentLevel);
|
|
60
|
+
this.selectListChildIndex = this.children.length;
|
|
44
61
|
this.addChild(this.selectList);
|
|
62
|
+
this.addChild(new Spacer(1));
|
|
63
|
+
this.addChild(new Text(theme.fg("dim", " Enter to select · Ctrl+S to set as default · Esc to cancel"), 0, 0));
|
|
45
64
|
// Add bottom border
|
|
46
65
|
this.addChild(new DynamicBorder());
|
|
47
66
|
}
|
|
67
|
+
buildSelectList(items, preselect) {
|
|
68
|
+
const list = new SelectList(items, Math.max(1, items.length), getSelectListTheme(), THINKING_SELECT_LIST_LAYOUT);
|
|
69
|
+
const currentIndex = items.findIndex((item) => item.value === preselect);
|
|
70
|
+
if (currentIndex !== -1) {
|
|
71
|
+
list.setSelectedIndex(currentIndex);
|
|
72
|
+
}
|
|
73
|
+
list.onSelect = (item) => this.onSelect(item.value);
|
|
74
|
+
list.onCancel = () => this.onCancel();
|
|
75
|
+
return list;
|
|
76
|
+
}
|
|
77
|
+
applyFilter(query) {
|
|
78
|
+
const filtered = query
|
|
79
|
+
? fuzzyFilter(this.allItems, query, (item) => `${item.label} ${item.description ?? ""}`)
|
|
80
|
+
: this.allItems;
|
|
81
|
+
const selectedValue = this.selectList.getSelectedItem()?.value;
|
|
82
|
+
const newList = this.buildSelectList(filtered, selectedValue);
|
|
83
|
+
this.children[this.selectListChildIndex] = newList;
|
|
84
|
+
this.selectList = newList;
|
|
85
|
+
}
|
|
86
|
+
handleInput(keyData) {
|
|
87
|
+
if (matchesKey(keyData, "ctrl+s") && this.onSelectAsDefault) {
|
|
88
|
+
const item = this.selectList.getSelectedItem();
|
|
89
|
+
if (item)
|
|
90
|
+
this.onSelectAsDefault(item.value);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const kb = getKeybindings();
|
|
94
|
+
const isNav = kb.matches(keyData, "tui.select.up") ||
|
|
95
|
+
kb.matches(keyData, "tui.select.down") ||
|
|
96
|
+
kb.matches(keyData, "tui.select.confirm") ||
|
|
97
|
+
kb.matches(keyData, "tui.select.cancel");
|
|
98
|
+
if (isNav) {
|
|
99
|
+
this.selectList.handleInput(keyData);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.searchInput.handleInput(keyData);
|
|
103
|
+
this.applyFilter(this.searchInput.getValue());
|
|
104
|
+
}
|
|
48
105
|
getSelectList() {
|
|
49
106
|
return this.selectList;
|
|
50
107
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinking-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"thinking-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/thinking-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\ttype SelectItem,\n\tSelectList,\n\ttype SelectListLayoutOptions,\n\tSpacer,\n\tText,\n} from \"@earendil-works/pi-tui\";\nimport { getSelectListTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyDisplayText } from \"./keybinding-hints.ts\";\n\nconst THINKING_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {\n\tminPrimaryColumnWidth: 12,\n\tmaxPrimaryColumnWidth: 32,\n};\n\nconst LEVEL_DESCRIPTIONS: Record<ThinkingLevel, string> = {\n\toff: \"No reasoning\",\n\tminimal: \"Very brief reasoning (~1k tokens)\",\n\tlow: \"Light reasoning (~2k tokens)\",\n\tmedium: \"Moderate reasoning (~8k tokens)\",\n\thigh: \"Deep reasoning (~16k tokens)\",\n\txhigh: \"Extra-high reasoning (~32k tokens)\",\n\tmax: \"Maximum reasoning\",\n};\n\n/**\n * Component that renders a thinking level selector with borders\n */\nexport class ThinkingSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\tprivate selectList: SelectList;\n\tprivate selectListChildIndex: number;\n\tprivate allItems: SelectItem[];\n\tprivate onSelect: (level: ThinkingLevel) => void;\n\tprivate onCancel: () => void;\n\tprivate onSelectAsDefault?: (level: ThinkingLevel) => void;\n\tprivate _focused = false;\n\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tconstructor(\n\t\tcurrentLevel: ThinkingLevel,\n\t\tavailableLevels: ThinkingLevel[],\n\t\tonSelect: (level: ThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t\tonSelectAsDefault?: (level: ThinkingLevel) => void,\n\t\tdefaultThinkingLevel?: ThinkingLevel,\n\t) {\n\t\tsuper();\n\t\tthis.onSelect = onSelect;\n\t\tthis.onCancel = onCancel;\n\t\tthis.onSelectAsDefault = onSelectAsDefault;\n\n\t\tthis.allItems = availableLevels.map((level) => ({\n\t\t\tvalue: level,\n\t\t\tlabel: level,\n\t\t\tdescription:\n\t\t\t\tlevel === defaultThinkingLevel ? `${LEVEL_DESCRIPTIONS[level]} · default` : LEVEL_DESCRIPTIONS[level],\n\t\t}));\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(\"Thinking Level\", 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(`${keyDisplayText(\"app.thinking.cycle\")} cycles thinking levels in-session`, 0, 0));\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.searchInput = new Input();\n\t\tthis.searchInput.onSubmit = () => this.selectList.handleInput(\"\\r\");\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create selector\n\t\tthis.selectList = this.buildSelectList(this.allItems, currentLevel);\n\t\tthis.selectListChildIndex = this.children.length;\n\t\tthis.addChild(this.selectList);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"dim\", \" Enter to select · Ctrl+S to set as default · Esc to cancel\"), 0, 0));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate buildSelectList(items: SelectItem[], preselect?: ThinkingLevel): SelectList {\n\t\tconst list = new SelectList(items, Math.max(1, items.length), getSelectListTheme(), THINKING_SELECT_LIST_LAYOUT);\n\t\tconst currentIndex = items.findIndex((item) => item.value === preselect);\n\t\tif (currentIndex !== -1) {\n\t\t\tlist.setSelectedIndex(currentIndex);\n\t\t}\n\t\tlist.onSelect = (item) => this.onSelect(item.value as ThinkingLevel);\n\t\tlist.onCancel = () => this.onCancel();\n\t\treturn list;\n\t}\n\n\tprivate applyFilter(query: string): void {\n\t\tconst filtered = query\n\t\t\t? fuzzyFilter(this.allItems, query, (item) => `${item.label} ${item.description ?? \"\"}`)\n\t\t\t: this.allItems;\n\t\tconst selectedValue = this.selectList.getSelectedItem()?.value as ThinkingLevel | undefined;\n\t\tconst newList = this.buildSelectList(filtered, selectedValue);\n\t\tthis.children[this.selectListChildIndex] = newList;\n\t\tthis.selectList = newList;\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tif (matchesKey(keyData, \"ctrl+s\") && this.onSelectAsDefault) {\n\t\t\tconst item = this.selectList.getSelectedItem();\n\t\t\tif (item) this.onSelectAsDefault(item.value as ThinkingLevel);\n\t\t\treturn;\n\t\t}\n\n\t\tconst kb = getKeybindings();\n\t\tconst isNav =\n\t\t\tkb.matches(keyData, \"tui.select.up\") ||\n\t\t\tkb.matches(keyData, \"tui.select.down\") ||\n\t\t\tkb.matches(keyData, \"tui.select.confirm\") ||\n\t\t\tkb.matches(keyData, \"tui.select.cancel\");\n\t\tif (isNav) {\n\t\t\tthis.selectList.handleInput(keyData);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.searchInput.handleInput(keyData);\n\t\tthis.applyFilter(this.searchInput.getValue());\n\t}\n\n\tgetSelectList(): SelectList {\n\t\treturn this.selectList;\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-editor.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/external-editor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"external-editor.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/external-editor.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,oBAAoB,GAAG;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,CAAC;AAElG,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAmCxG","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { mkdtempSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripBom } from \"../../utils/text.ts\";\n\nexport interface ExternalEditorOptions {\n\tcommand: string;\n\tcontent: string;\n}\n\nexport type ExternalEditorResult = { status: \"complete\"; content: string } | { status: \"failed\" };\n\nexport async function editInExternalEditor(options: ExternalEditorOptions): Promise<ExternalEditorResult> {\n\tconst directory = mkdtempSync(join(tmpdir(), \"pi-editor-\"));\n\tconst filePath = join(directory, \"prompt.md\");\n\ttry {\n\t\twriteFileSync(filePath, options.content, \"utf-8\");\n\t\tprocess.stdout.write(`Launching external editor: ${options.command}\\nPi will resume when the editor exits.\\n`);\n\n\t\t// Do not use spawnSync here. On Windows, synchronous child_process calls can keep\n\t\t// Node/libuv's console input read active after the parent pauses stdin, racing\n\t\t// vim/nvim for the console input buffer until Ctrl+C cancels the pending read.\n\t\tconst exitCode = await new Promise<number | null>((resolve) => {\n\t\t\tconst [editor, ...editorArgs] = options.command.split(\" \");\n\t\t\tconst child =\n\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t? spawn(`${options.command} \"${filePath.replaceAll('\"', '\"\"')}\"`, {\n\t\t\t\t\t\t\tstdio: \"inherit\",\n\t\t\t\t\t\t\tshell: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t: spawn(editor, [...editorArgs, filePath], { stdio: \"inherit\" });\n\t\t\tchild.on(\"error\", () => resolve(null));\n\t\t\tchild.on(\"close\", (code) => resolve(code));\n\t\t});\n\n\t\tif (exitCode !== 0) {\n\t\t\treturn { status: \"failed\" };\n\t\t}\n\n\t\treturn { status: \"complete\", content: stripBom(readFileSync(filePath, \"utf-8\")).replace(/\\n$/, \"\") };\n\t} finally {\n\t\ttry {\n\t\t\trmSync(directory, { recursive: true, force: true });\n\t\t} catch {\n\t\t\t// Cleanup is best effort.\n\t\t}\n\t}\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { stripBom } from "../../utils/text.js";
|
|
5
6
|
export async function editInExternalEditor(options) {
|
|
6
7
|
const directory = mkdtempSync(join(tmpdir(), "pi-editor-"));
|
|
7
8
|
const filePath = join(directory, "prompt.md");
|
|
@@ -25,7 +26,7 @@ export async function editInExternalEditor(options) {
|
|
|
25
26
|
if (exitCode !== 0) {
|
|
26
27
|
return { status: "failed" };
|
|
27
28
|
}
|
|
28
|
-
return { status: "complete", content: readFileSync(filePath, "utf-8").replace(/\n$/, "") };
|
|
29
|
+
return { status: "complete", content: stripBom(readFileSync(filePath, "utf-8")).replace(/\n$/, "") };
|
|
29
30
|
}
|
|
30
31
|
finally {
|
|
31
32
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-editor.js","sourceRoot":"","sources":["../../../src/modes/interactive/external-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"external-editor.js","sourceRoot":"","sources":["../../../src/modes/interactive/external-editor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAS/C,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,OAA8B,EAAiC;IACzG,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC;QACJ,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAClD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,OAAO,CAAC,OAAO,2CAA2C,CAAC,CAAC;QAE/G,kFAAkF;QAClF,+EAA+E;QAC/E,+EAA+E;QAC/E,MAAM,QAAQ,GAAG,MAAM,IAAI,OAAO,CAAgB,CAAC,OAAO,EAAE,EAAE,CAAC;YAC9D,MAAM,CAAC,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC3D,MAAM,KAAK,GACV,OAAO,CAAC,QAAQ,KAAK,OAAO;gBAC3B,CAAC,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;oBAChE,KAAK,EAAE,SAAS;oBAChB,KAAK,EAAE,IAAI;iBACX,CAAC;gBACH,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YACnE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YACvC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAAA,CAC3C,CAAC,CAAC;QAEH,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACpB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;IACtG,CAAC;YAAS,CAAC;QACV,IAAI,CAAC;YACJ,MAAM,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACR,0BAA0B;QAC3B,CAAC;IACF,CAAC;AAAA,CACD","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { mkdtempSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { stripBom } from \"../../utils/text.ts\";\n\nexport interface ExternalEditorOptions {\n\tcommand: string;\n\tcontent: string;\n}\n\nexport type ExternalEditorResult = { status: \"complete\"; content: string } | { status: \"failed\" };\n\nexport async function editInExternalEditor(options: ExternalEditorOptions): Promise<ExternalEditorResult> {\n\tconst directory = mkdtempSync(join(tmpdir(), \"pi-editor-\"));\n\tconst filePath = join(directory, \"prompt.md\");\n\ttry {\n\t\twriteFileSync(filePath, options.content, \"utf-8\");\n\t\tprocess.stdout.write(`Launching external editor: ${options.command}\\nPi will resume when the editor exits.\\n`);\n\n\t\t// Do not use spawnSync here. On Windows, synchronous child_process calls can keep\n\t\t// Node/libuv's console input read active after the parent pauses stdin, racing\n\t\t// vim/nvim for the console input buffer until Ctrl+C cancels the pending read.\n\t\tconst exitCode = await new Promise<number | null>((resolve) => {\n\t\t\tconst [editor, ...editorArgs] = options.command.split(\" \");\n\t\t\tconst child =\n\t\t\t\tprocess.platform === \"win32\"\n\t\t\t\t\t? spawn(`${options.command} \"${filePath.replaceAll('\"', '\"\"')}\"`, {\n\t\t\t\t\t\t\tstdio: \"inherit\",\n\t\t\t\t\t\t\tshell: true,\n\t\t\t\t\t\t})\n\t\t\t\t\t: spawn(editor, [...editorArgs, filePath], { stdio: \"inherit\" });\n\t\t\tchild.on(\"error\", () => resolve(null));\n\t\t\tchild.on(\"close\", (code) => resolve(code));\n\t\t});\n\n\t\tif (exitCode !== 0) {\n\t\t\treturn { status: \"failed\" };\n\t\t}\n\n\t\treturn { status: \"complete\", content: stripBom(readFileSync(filePath, \"utf-8\")).replace(/\\n$/, \"\") };\n\t} finally {\n\t\ttry {\n\t\t\trmSync(directory, { recursive: true, force: true });\n\t\t} catch {\n\t\t\t// Cleanup is best effort.\n\t\t}\n\t}\n}\n"]}
|
|
@@ -6,6 +6,7 @@ import type { ImageContent } from "@earendil-works/pi-ai/compat";
|
|
|
6
6
|
import type { Terminal } from "@earendil-works/pi-tui";
|
|
7
7
|
import { type TUI, TuiAltScreen, TuiMainScreen } from "@earendil-works/pi-tui";
|
|
8
8
|
import { type AgentSessionRuntime } from "../../core/agent-session-runtime.ts";
|
|
9
|
+
import type { AgentSessionRuntimeDiagnostic } from "../../core/agent-session-services.ts";
|
|
9
10
|
import { SessionManager } from "../../core/session-manager.ts";
|
|
10
11
|
import type { FullscreenExitOutput, TuiMode } from "../../core/settings-manager.ts";
|
|
11
12
|
import { type LatestPiRelease } from "../../utils/version-check.ts";
|
|
@@ -16,6 +17,8 @@ export declare function formatResumeCommand(sessionManager: SessionManager): str
|
|
|
16
17
|
export interface InteractiveModeOptions {
|
|
17
18
|
/** Providers that were migrated to auth.json (shows warning) */
|
|
18
19
|
migratedProviders?: string[];
|
|
20
|
+
/** Diagnostics collected before the interactive TUI was initialized. */
|
|
21
|
+
startupDiagnostics?: AgentSessionRuntimeDiagnostic[];
|
|
19
22
|
/** Warning message if session model couldn't be restored */
|
|
20
23
|
modelFallbackMessage?: string;
|
|
21
24
|
/** Cwd to trust after reload if it gained a .pi directory during this implicitly trusted session. */
|
|
@@ -39,6 +42,7 @@ interface InteractiveTuiOptions {
|
|
|
39
42
|
logDirectory: string;
|
|
40
43
|
terminal?: Terminal;
|
|
41
44
|
onRightClickPaste?: () => void;
|
|
45
|
+
fullscreenCopyOnSelect?: boolean;
|
|
42
46
|
}
|
|
43
47
|
/** Composition root for selecting the interactive terminal renderer. */
|
|
44
48
|
export declare function createInteractiveTui(options: InteractiveTuiOptions): TuiMainScreen | TuiAltScreen;
|
|
@@ -53,11 +57,6 @@ export declare class InteractiveMode {
|
|
|
53
57
|
private chatContainer;
|
|
54
58
|
private documentContainer;
|
|
55
59
|
private transcriptScrollView;
|
|
56
|
-
private transcriptLazyEntries;
|
|
57
|
-
private loadedTranscriptEntryIds;
|
|
58
|
-
private transcriptWindowInitialized;
|
|
59
|
-
private transcriptLatestCompactionId;
|
|
60
|
-
private loadingEarlierTranscript;
|
|
61
60
|
private fullscreenLayoutRoot;
|
|
62
61
|
private pendingMessagesContainer;
|
|
63
62
|
private statusContainer;
|
|
@@ -204,9 +203,9 @@ export declare class InteractiveMode {
|
|
|
204
203
|
private setExtensionStatus;
|
|
205
204
|
private showStatusIndicator;
|
|
206
205
|
private clearStatusIndicator;
|
|
206
|
+
private showWorkingStatusIndicator;
|
|
207
207
|
private setWorkingVisible;
|
|
208
208
|
private setWorkingIndicator;
|
|
209
|
-
private restoreWorkingIndicator;
|
|
210
209
|
private setHiddenThinkingLabel;
|
|
211
210
|
/**
|
|
212
211
|
* Set an extension widget (string array or custom component).
|
|
@@ -298,12 +297,17 @@ export declare class InteractiveMode {
|
|
|
298
297
|
private addMessageToChat;
|
|
299
298
|
private renderSessionItems;
|
|
300
299
|
/**
|
|
301
|
-
* Render session entries to chat. Used for initial load and
|
|
302
|
-
* @param entries
|
|
300
|
+
* Render session entries to chat. Used for initial load and rebuild after compaction.
|
|
301
|
+
* @param entries Compaction-aware session entries to render
|
|
303
302
|
* @param options.updateFooter Update footer state
|
|
304
303
|
* @param options.populateHistory Add user messages to editor history
|
|
305
304
|
*/
|
|
306
305
|
private renderSessionEntries;
|
|
306
|
+
/**
|
|
307
|
+
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
308
|
+
* from persisted summary usage and is not stored as a separate session entry.
|
|
309
|
+
*/
|
|
310
|
+
private addCompactionCostNotice;
|
|
307
311
|
/**
|
|
308
312
|
* Show a transcript notice when a completed assistant message paid for a
|
|
309
313
|
* significant cache miss. Only states observable facts: the miss itself,
|
|
@@ -311,10 +315,6 @@ export declare class InteractiveMode {
|
|
|
311
315
|
*/
|
|
312
316
|
private maybeShowCacheMissNotice;
|
|
313
317
|
private addCacheMissNotice;
|
|
314
|
-
private getTranscriptEntriesForRender;
|
|
315
|
-
private renderTranscriptEntries;
|
|
316
|
-
private handleTranscriptScroll;
|
|
317
|
-
private loadEarlierTranscript;
|
|
318
318
|
renderInitialMessages(): void;
|
|
319
319
|
private renderProjectTrustWarningIfNeeded;
|
|
320
320
|
getUserInput(): Promise<string>;
|
|
@@ -352,6 +352,8 @@ export declare class InteractiveMode {
|
|
|
352
352
|
private cycleModel;
|
|
353
353
|
private toggleToolOutputExpansion;
|
|
354
354
|
private setToolsExpanded;
|
|
355
|
+
/** Update rendered assistant messages without rebuilding live tool components. */
|
|
356
|
+
private updateThinkingBlockVisibility;
|
|
355
357
|
private toggleThinkingBlockVisibility;
|
|
356
358
|
private handleOpenExternalEditor;
|
|
357
359
|
clearEditor(): void;
|
|
@@ -383,6 +385,9 @@ export declare class InteractiveMode {
|
|
|
383
385
|
*/
|
|
384
386
|
private showSelector;
|
|
385
387
|
private showSettingsSelector;
|
|
388
|
+
private handleThinkingCommand;
|
|
389
|
+
private selectThinkingLevel;
|
|
390
|
+
private showThinkingSelector;
|
|
386
391
|
private handleModelCommand;
|
|
387
392
|
private findExactModelMatch;
|
|
388
393
|
/** Update the footer's available provider count from the current snapshot without refreshing catalogs. */
|