@oh-my-pi/pi-coding-agent 16.0.4 → 16.0.6
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 +94 -0
- package/dist/cli.js +2027 -1396
- package/dist/types/advisor/advise-tool.d.ts +31 -19
- package/dist/types/autoresearch/tools/init-experiment.d.ts +13 -17
- package/dist/types/autoresearch/tools/log-experiment.d.ts +17 -19
- package/dist/types/autoresearch/tools/run-experiment.d.ts +3 -4
- package/dist/types/autoresearch/tools/update-notes.d.ts +4 -5
- package/dist/types/cli/args.d.ts +1 -0
- package/dist/types/cli/bench-cli.d.ts +6 -0
- package/dist/types/cli/ttsr-cli.d.ts +39 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commands/ttsr.d.ts +57 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +4 -5
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +5 -6
- package/dist/types/commit/agentic/tools/git-overview.d.ts +4 -5
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +23 -24
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +11 -32
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +3 -4
- package/dist/types/commit/agentic/tools/schemas.d.ts +6 -27
- package/dist/types/commit/agentic/tools/split-commit.d.ts +28 -49
- package/dist/types/commit/changelog/generate.d.ts +12 -13
- package/dist/types/commit/shared-llm.d.ts +10 -37
- package/dist/types/config/config-file.d.ts +4 -4
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/models-config-schema.d.ts +625 -990
- package/dist/types/config/models-config.d.ts +229 -217
- package/dist/types/config/settings-schema.d.ts +144 -25
- package/dist/types/edit/hashline/params.d.ts +7 -11
- package/dist/types/edit/index.d.ts +2 -1
- package/dist/types/edit/modes/apply-patch.d.ts +4 -5
- package/dist/types/edit/modes/patch.d.ts +15 -24
- package/dist/types/edit/modes/replace.d.ts +16 -17
- package/dist/types/eval/js/index.d.ts +1 -0
- package/dist/types/extensibility/custom-commands/types.d.ts +6 -3
- package/dist/types/extensibility/custom-tools/types.d.ts +8 -5
- package/dist/types/extensibility/extensions/runner.d.ts +5 -2
- package/dist/types/extensibility/extensions/types.d.ts +14 -10
- package/dist/types/extensibility/hooks/types.d.ts +7 -4
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +13 -5
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +17 -0
- package/dist/types/extensibility/shared-events.d.ts +22 -1
- package/dist/types/extensibility/typebox.d.ts +80 -58
- package/dist/types/goals/tools/goal-tool.d.ts +11 -24
- package/dist/types/index.d.ts +2 -0
- package/dist/types/lsp/index.d.ts +11 -26
- package/dist/types/lsp/types.d.ts +12 -28
- package/dist/types/main.d.ts +1 -0
- package/dist/types/mcp/client.d.ts +8 -0
- package/dist/types/modes/components/btw-panel.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/status-line/context-thresholds.d.ts +0 -1
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +3 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/index.d.ts +1 -0
- package/dist/types/modes/setup-wizard/startup-splash.d.ts +7 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/modes/utils/context-usage.d.ts +12 -0
- package/dist/types/sdk.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +24 -0
- package/dist/types/session/session-persistence.d.ts +4 -0
- package/dist/types/startup-splash.d.ts +12 -0
- package/dist/types/task/types.d.ts +47 -48
- package/dist/types/tools/ask.d.ts +26 -27
- package/dist/types/tools/ast-edit.d.ts +17 -17
- package/dist/types/tools/ast-grep.d.ts +12 -13
- package/dist/types/tools/bash.d.ts +20 -17
- package/dist/types/tools/browser.d.ts +46 -71
- package/dist/types/tools/checkpoint.d.ts +14 -15
- package/dist/types/tools/debug.d.ts +82 -145
- package/dist/types/tools/eval.d.ts +30 -40
- package/dist/types/tools/find.d.ts +17 -18
- package/dist/types/tools/gh.d.ts +49 -78
- package/dist/types/tools/image-gen.d.ts +20 -36
- package/dist/types/tools/inspect-image.d.ts +10 -11
- package/dist/types/tools/irc.d.ts +22 -33
- package/dist/types/tools/job.d.ts +11 -12
- package/dist/types/tools/learn.d.ts +21 -28
- package/dist/types/tools/manage-skill.d.ts +13 -22
- package/dist/types/tools/memory-edit.d.ts +15 -24
- package/dist/types/tools/memory-recall.d.ts +7 -8
- package/dist/types/tools/memory-reflect.d.ts +9 -10
- package/dist/types/tools/memory-retain.d.ts +13 -14
- package/dist/types/tools/read.d.ts +8 -8
- package/dist/types/tools/resolve.d.ts +11 -18
- package/dist/types/tools/review.d.ts +9 -15
- package/dist/types/tools/search-tool-bm25.d.ts +9 -10
- package/dist/types/tools/search.d.ts +16 -17
- package/dist/types/tools/ssh.d.ts +14 -15
- package/dist/types/tools/todo.d.ts +27 -43
- package/dist/types/tools/tts.d.ts +8 -9
- package/dist/types/tools/write.d.ts +9 -10
- package/dist/types/tui/code-cell.d.ts +2 -0
- package/dist/types/tui/index.d.ts +1 -0
- package/dist/types/tui/width-aware-text.d.ts +23 -0
- package/dist/types/utils/image-vision-fallback.d.ts +28 -0
- package/dist/types/utils/markit.d.ts +10 -1
- package/dist/types/web/search/index.d.ts +17 -28
- package/dist/types/web/search/providers/base.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +0 -2
- package/dist/types/web/search/types.d.ts +32 -26
- package/package.json +14 -13
- package/scripts/omp +1 -1
- package/src/advisor/__tests__/advisor.test.ts +103 -1
- package/src/advisor/advise-tool.ts +47 -11
- package/src/autoresearch/tools/init-experiment.ts +13 -16
- package/src/autoresearch/tools/log-experiment.ts +15 -18
- package/src/autoresearch/tools/run-experiment.ts +3 -3
- package/src/autoresearch/tools/update-notes.ts +4 -4
- package/src/cli/args.ts +1 -0
- package/src/cli/bench-cli.ts +30 -7
- package/src/cli/flag-tables.ts +8 -0
- package/src/cli/ttsr-cli.ts +995 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +7 -1
- package/src/collab/host.ts +2 -2
- package/src/commands/launch.ts +3 -0
- package/src/commands/ttsr.ts +125 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -4
- package/src/commit/agentic/tools/git-file-diff.ts +4 -4
- package/src/commit/agentic/tools/git-hunk.ts +7 -5
- package/src/commit/agentic/tools/git-overview.ts +4 -4
- package/src/commit/agentic/tools/propose-changelog.ts +18 -15
- package/src/commit/agentic/tools/propose-commit.ts +6 -6
- package/src/commit/agentic/tools/recent-commits.ts +3 -3
- package/src/commit/agentic/tools/schemas.ts +8 -20
- package/src/commit/agentic/tools/split-commit.ts +19 -23
- package/src/commit/analysis/summary.ts +7 -5
- package/src/commit/changelog/generate.ts +15 -11
- package/src/commit/shared-llm.ts +17 -24
- package/src/config/config-file.ts +13 -15
- package/src/config/keybindings.ts +6 -0
- package/src/config/models-config-schema.ts +206 -179
- package/src/config/settings-schema.ts +118 -2
- package/src/discovery/builtin-rules/index.ts +2 -0
- package/src/discovery/builtin-rules/ts-import-type.md +2 -2
- package/src/discovery/builtin-rules/ts-no-any.md +11 -2
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
- package/src/edit/hashline/params.ts +12 -11
- package/src/edit/index.ts +5 -4
- package/src/edit/modes/apply-patch.ts +4 -4
- package/src/edit/modes/patch.ts +15 -18
- package/src/edit/modes/replace.ts +13 -17
- package/src/edit/renderer.ts +0 -1
- package/src/eval/agent-bridge.ts +11 -13
- package/src/eval/completion-bridge.ts +25 -17
- package/src/eval/js/context-manager.ts +17 -2
- package/src/eval/js/index.ts +1 -1
- package/src/eval/py/executor.ts +2 -2
- package/src/eval/py/runner.py +44 -0
- package/src/extensibility/custom-commands/loader.ts +5 -3
- package/src/extensibility/custom-commands/types.ts +6 -3
- package/src/extensibility/custom-tools/loader.ts +4 -2
- package/src/extensibility/custom-tools/types.ts +8 -5
- package/src/extensibility/extensions/loader.ts +4 -2
- package/src/extensibility/extensions/runner.ts +20 -2
- package/src/extensibility/extensions/types.ts +22 -8
- package/src/extensibility/hooks/loader.ts +5 -2
- package/src/extensibility/hooks/types.ts +7 -4
- package/src/extensibility/legacy-pi-ai-shim.ts +42 -5
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +113 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +13 -13
- package/src/extensibility/shared-events.ts +24 -0
- package/src/extensibility/tool-proxy.ts +4 -1
- package/src/extensibility/typebox.ts +778 -251
- package/src/goals/guided-setup.ts +12 -3
- package/src/goals/tools/goal-tool.ts +6 -6
- package/src/index.ts +2 -0
- package/src/internal-urls/docs-index.generated.ts +15 -13
- package/src/lsp/types.ts +13 -27
- package/src/main.ts +29 -21
- package/src/mcp/client.ts +38 -13
- package/src/mcp/render.ts +102 -89
- package/src/modes/components/agent-hub.ts +11 -4
- package/src/modes/components/branch-summary-message.ts +1 -0
- package/src/modes/components/btw-panel.ts +5 -1
- package/src/modes/components/collab-prompt-message.ts +9 -7
- package/src/modes/components/compaction-summary-message.ts +1 -0
- package/src/modes/components/custom-editor.ts +18 -0
- package/src/modes/components/custom-message.ts +1 -0
- package/src/modes/components/footer.ts +6 -5
- package/src/modes/components/hook-message.ts +1 -0
- package/src/modes/components/read-tool-group.ts +9 -3
- package/src/modes/components/skill-message.ts +1 -0
- package/src/modes/components/status-line/component.ts +139 -15
- package/src/modes/components/status-line/context-thresholds.ts +0 -1
- package/src/modes/components/todo-reminder.ts +1 -0
- package/src/modes/components/tool-execution.ts +17 -10
- package/src/modes/components/ttsr-notification.ts +1 -0
- package/src/modes/components/user-message.ts +6 -6
- package/src/modes/controllers/btw-controller.ts +69 -1
- package/src/modes/controllers/event-controller.ts +2 -7
- package/src/modes/controllers/input-controller.ts +29 -0
- package/src/modes/controllers/selector-controller.ts +10 -3
- package/src/modes/interactive-mode.ts +42 -10
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/setup-wizard/index.ts +1 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +77 -5
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/theme/theme.ts +133 -143
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/context-usage.ts +37 -20
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/system-prompt.md +1 -0
- package/src/prompts/tools/image-attachment-describe-system.md +8 -0
- package/src/prompts/tools/image-attachment-describe.md +10 -0
- package/src/sdk.ts +35 -22
- package/src/session/agent-session.ts +715 -255
- package/src/session/session-history-format.ts +11 -2
- package/src/session/session-loader.ts +19 -32
- package/src/session/session-persistence.ts +27 -11
- package/src/session/snapcompact-inline.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +4 -11
- package/src/ssh/connection-manager.ts +3 -2
- package/src/startup-splash.ts +19 -0
- package/src/task/executor.ts +12 -7
- package/src/task/types.ts +44 -41
- package/src/tool-discovery/tool-index.ts +17 -4
- package/src/tools/ask.ts +14 -14
- package/src/tools/ast-edit.ts +17 -14
- package/src/tools/ast-grep.ts +10 -9
- package/src/tools/bash.ts +15 -10
- package/src/tools/browser/launch.ts +13 -0
- package/src/tools/browser.ts +26 -32
- package/src/tools/checkpoint.ts +7 -7
- package/src/tools/debug.ts +72 -69
- package/src/tools/eval.ts +18 -19
- package/src/tools/find.ts +20 -13
- package/src/tools/gh.ts +29 -49
- package/src/tools/image-gen.ts +94 -57
- package/src/tools/inspect-image.ts +8 -9
- package/src/tools/irc.ts +12 -12
- package/src/tools/job.ts +6 -6
- package/src/tools/learn.ts +11 -14
- package/src/tools/manage-skill.ts +19 -23
- package/src/tools/memory-edit.ts +8 -8
- package/src/tools/memory-recall.ts +4 -4
- package/src/tools/memory-reflect.ts +5 -5
- package/src/tools/memory-retain.ts +9 -11
- package/src/tools/puppeteer/02_stealth_hairline.txt +1 -1
- package/src/tools/puppeteer/04_stealth_iframe.txt +4 -4
- package/src/tools/puppeteer/05_stealth_webgl.txt +1 -1
- package/src/tools/puppeteer/10_stealth_plugins.txt +6 -4
- package/src/tools/puppeteer/12_stealth_codecs.txt +2 -2
- package/src/tools/puppeteer/13_stealth_worker.txt +1 -1
- package/src/tools/read.ts +197 -19
- package/src/tools/report-tool-issue.ts +6 -6
- package/src/tools/resolve.ts +6 -6
- package/src/tools/review.ts +10 -12
- package/src/tools/search-tool-bm25.ts +5 -5
- package/src/tools/search.ts +20 -29
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +16 -19
- package/src/tools/tts.ts +16 -15
- package/src/tools/write.ts +5 -5
- package/src/tui/code-cell.ts +44 -3
- package/src/tui/index.ts +1 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/image-vision-fallback.ts +197 -0
- package/src/utils/markit.ts +17 -2
- package/src/web/search/index.ts +21 -9
- package/src/web/search/providers/base.ts +1 -0
- package/src/web/search/providers/gemini.ts +56 -18
- package/src/web/search/providers/perplexity.ts +373 -126
- package/src/web/search/types.ts +28 -48
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
|
1
2
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
2
3
|
import btwUserPrompt from "../../prompts/system/btw-user.md" with { type: "text" };
|
|
3
4
|
import { BtwPanelComponent } from "../components/btw-panel";
|
|
@@ -7,10 +8,37 @@ interface BtwRequest {
|
|
|
7
8
|
component: BtwPanelComponent;
|
|
8
9
|
abortController: AbortController;
|
|
9
10
|
question: string;
|
|
11
|
+
leafId: string | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function assistantMessageWithReplyText(assistantMessage: AssistantMessage, replyText: string): AssistantMessage {
|
|
15
|
+
const content: AssistantMessage["content"] = [];
|
|
16
|
+
let replacedText = false;
|
|
17
|
+
for (const part of assistantMessage.content) {
|
|
18
|
+
if (part.type === "thinking") {
|
|
19
|
+
content.push({ type: "thinking", thinking: part.thinking });
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (part.type === "redactedThinking") continue;
|
|
23
|
+
if (part.type !== "text") {
|
|
24
|
+
content.push(part);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (replacedText) continue;
|
|
28
|
+
content.push({ type: "text", text: replyText });
|
|
29
|
+
replacedText = true;
|
|
30
|
+
}
|
|
31
|
+
if (!replacedText) content.push({ type: "text", text: replyText });
|
|
32
|
+
return { ...assistantMessage, content, providerPayload: undefined };
|
|
10
33
|
}
|
|
11
34
|
|
|
12
35
|
export class BtwController {
|
|
13
36
|
#activeRequest: BtwRequest | undefined;
|
|
37
|
+
#lastQuestion: string | undefined;
|
|
38
|
+
#lastReplyText: string | undefined;
|
|
39
|
+
#lastAssistantMessage: AssistantMessage | undefined;
|
|
40
|
+
#lastLeafId: string | null | undefined;
|
|
41
|
+
#branchInFlight = false;
|
|
14
42
|
|
|
15
43
|
constructor(private readonly ctx: InteractiveModeContext) {}
|
|
16
44
|
|
|
@@ -18,6 +46,29 @@ export class BtwController {
|
|
|
18
46
|
return this.#activeRequest !== undefined;
|
|
19
47
|
}
|
|
20
48
|
|
|
49
|
+
canBranch(): boolean {
|
|
50
|
+
return (
|
|
51
|
+
!this.#branchInFlight &&
|
|
52
|
+
this.#activeRequest?.component.isBranchable() === true &&
|
|
53
|
+
this.#lastQuestion !== undefined &&
|
|
54
|
+
this.#lastReplyText !== undefined &&
|
|
55
|
+
this.#lastAssistantMessage !== undefined &&
|
|
56
|
+
this.#lastLeafId !== null &&
|
|
57
|
+
this.#lastLeafId === this.ctx.sessionManager.getLeafId()
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async handleBranch(): Promise<boolean> {
|
|
62
|
+
if (!this.canBranch() || !this.#lastQuestion || !this.#lastAssistantMessage) return false;
|
|
63
|
+
this.#branchInFlight = true;
|
|
64
|
+
try {
|
|
65
|
+
await this.ctx.handleBtwBranch(this.#lastQuestion, this.#lastAssistantMessage);
|
|
66
|
+
return true;
|
|
67
|
+
} finally {
|
|
68
|
+
this.#branchInFlight = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
21
72
|
handleEscape(): boolean {
|
|
22
73
|
if (!this.#activeRequest) return false;
|
|
23
74
|
this.#closeActiveRequest({ abort: this.#activeRequest.abortController.signal.aborted === false });
|
|
@@ -47,6 +98,7 @@ export class BtwController {
|
|
|
47
98
|
component: new BtwPanelComponent({ question: trimmedQuestion, tui: this.ctx.ui }),
|
|
48
99
|
abortController: new AbortController(),
|
|
49
100
|
question: trimmedQuestion,
|
|
101
|
+
leafId: this.ctx.sessionManager.getLeafId(),
|
|
50
102
|
};
|
|
51
103
|
this.ctx.btwContainer.clear();
|
|
52
104
|
this.ctx.btwContainer.addChild(request.component);
|
|
@@ -58,7 +110,7 @@ export class BtwController {
|
|
|
58
110
|
async #runRequest(request: BtwRequest): Promise<void> {
|
|
59
111
|
try {
|
|
60
112
|
const promptText = prompt.render(btwUserPrompt, { question: request.question });
|
|
61
|
-
const { replyText } = await this.ctx.session.runEphemeralTurn({
|
|
113
|
+
const { replyText, assistantMessage } = await this.ctx.session.runEphemeralTurn({
|
|
62
114
|
promptText,
|
|
63
115
|
onTextDelta: delta => {
|
|
64
116
|
if (this.#isActiveRequest(request)) {
|
|
@@ -75,6 +127,14 @@ export class BtwController {
|
|
|
75
127
|
request.component.setAnswer(replyText);
|
|
76
128
|
}
|
|
77
129
|
request.component.markComplete();
|
|
130
|
+
if (request.component.isBranchable()) {
|
|
131
|
+
this.#lastQuestion = request.question;
|
|
132
|
+
this.#lastReplyText = replyText;
|
|
133
|
+
this.#lastAssistantMessage = assistantMessageWithReplyText(assistantMessage, replyText);
|
|
134
|
+
this.#lastLeafId = request.leafId;
|
|
135
|
+
} else {
|
|
136
|
+
this.#clearBranchState();
|
|
137
|
+
}
|
|
78
138
|
} catch (error) {
|
|
79
139
|
if (!this.#isActiveRequest(request)) {
|
|
80
140
|
return;
|
|
@@ -91,6 +151,7 @@ export class BtwController {
|
|
|
91
151
|
const request = this.#activeRequest;
|
|
92
152
|
if (!request) return;
|
|
93
153
|
this.#activeRequest = undefined;
|
|
154
|
+
this.#clearBranchState();
|
|
94
155
|
if (options.abort) {
|
|
95
156
|
request.abortController.abort();
|
|
96
157
|
}
|
|
@@ -99,6 +160,13 @@ export class BtwController {
|
|
|
99
160
|
this.ctx.ui.requestRender();
|
|
100
161
|
}
|
|
101
162
|
|
|
163
|
+
#clearBranchState(): void {
|
|
164
|
+
this.#lastQuestion = undefined;
|
|
165
|
+
this.#lastReplyText = undefined;
|
|
166
|
+
this.#lastAssistantMessage = undefined;
|
|
167
|
+
this.#lastLeafId = undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
102
170
|
#isActiveRequest(request: BtwRequest): boolean {
|
|
103
171
|
return this.#activeRequest === request;
|
|
104
172
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { INTENT_FIELD } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import {
|
|
3
|
-
import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
4
3
|
import { type Component, Loader, TERMINAL } from "@oh-my-pi/pi-tui";
|
|
5
4
|
import { extractTextContent } from "../../commit/utils";
|
|
6
5
|
import { settings } from "../../config/settings";
|
|
@@ -1107,11 +1106,7 @@ export class EventController {
|
|
|
1107
1106
|
}
|
|
1108
1107
|
|
|
1109
1108
|
#currentContextTokens(): number {
|
|
1110
|
-
|
|
1111
|
-
.slice()
|
|
1112
|
-
.reverse()
|
|
1113
|
-
.find((m): m is AssistantMessage => m.role === "assistant" && m.stopReason !== "aborted");
|
|
1114
|
-
return lastAssistant?.usage ? calculatePromptTokens(lastAssistant.usage) : 0;
|
|
1109
|
+
return this.ctx.viewSession.getContextUsage()?.tokens ?? 0;
|
|
1115
1110
|
}
|
|
1116
1111
|
|
|
1117
1112
|
sendCompletionNotification(): void {
|
|
@@ -78,6 +78,7 @@ export class InputController {
|
|
|
78
78
|
|
|
79
79
|
#enhancedPaste?: EnhancedPasteController;
|
|
80
80
|
#focusedLeftTapListenerInstalled = false;
|
|
81
|
+
#btwBranchListenerInstalled = false;
|
|
81
82
|
// Tap counter for the double-← gesture; reset whenever a quiet gap
|
|
82
83
|
// (>= LEFT_DOUBLE_TAP_MAX_GAP_MS) starts a fresh sequence. See
|
|
83
84
|
// #detectLeftDoubleTap.
|
|
@@ -143,6 +144,16 @@ export class InputController {
|
|
|
143
144
|
return { consume: true };
|
|
144
145
|
});
|
|
145
146
|
}
|
|
147
|
+
if (!this.#btwBranchListenerInstalled) {
|
|
148
|
+
this.#btwBranchListenerInstalled = true;
|
|
149
|
+
this.ctx.ui.addInputListener(data => {
|
|
150
|
+
if (!matchesKey(data, "b")) return undefined;
|
|
151
|
+
if (!this.ctx.canBranchBtw()) return undefined;
|
|
152
|
+
if (this.ctx.editor.getText().trim()) return undefined;
|
|
153
|
+
void this.ctx.handleBtwBranchKey();
|
|
154
|
+
return { consume: true };
|
|
155
|
+
});
|
|
156
|
+
}
|
|
146
157
|
this.ctx.editor.onEscape = () => {
|
|
147
158
|
// Active context maintenance owns Esc: auto/manual compaction,
|
|
148
159
|
// handoff generation, and auto-retry backoff all advertise
|
|
@@ -304,6 +315,8 @@ export class InputController {
|
|
|
304
315
|
this.ctx.editor.onExpandTools = () => this.toggleToolOutputExpansion();
|
|
305
316
|
this.ctx.editor.setActionKeys("app.message.dequeue", this.ctx.keybindings.getKeys("app.message.dequeue"));
|
|
306
317
|
this.ctx.editor.onDequeue = () => this.handleDequeue();
|
|
318
|
+
this.ctx.editor.setActionKeys("app.retry", this.ctx.keybindings.getKeys("app.retry"));
|
|
319
|
+
this.ctx.editor.onRetry = () => void this.handleRetry();
|
|
307
320
|
this.ctx.editor.clearCustomKeyHandlers();
|
|
308
321
|
// Wire up extension shortcuts
|
|
309
322
|
this.registerExtensionShortcuts();
|
|
@@ -925,6 +938,22 @@ export class InputController {
|
|
|
925
938
|
return true;
|
|
926
939
|
}
|
|
927
940
|
|
|
941
|
+
async handleRetry(): Promise<void> {
|
|
942
|
+
if (this.ctx.collabGuest) {
|
|
943
|
+
this.ctx.showStatus("/retry is host-only during a collab session");
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
const didRetry = await this.ctx.viewSession.retry();
|
|
947
|
+
if (didRetry) {
|
|
948
|
+
this.ctx.editor.setText("");
|
|
949
|
+
this.ctx.pendingImages = [];
|
|
950
|
+
this.ctx.pendingImageLinks = [];
|
|
951
|
+
this.ctx.editor.imageLinks = undefined;
|
|
952
|
+
} else {
|
|
953
|
+
this.ctx.showStatus("Nothing to retry");
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
928
957
|
/** Send editor text as a follow-up message (queued behind current stream). */
|
|
929
958
|
async handleFollowUp(): Promise<void> {
|
|
930
959
|
let text = this.ctx.editor.getText().trim();
|
|
@@ -3,7 +3,7 @@ import { PASTE_CODE_LOGIN_PROVIDERS } from "@oh-my-pi/pi-ai";
|
|
|
3
3
|
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
4
4
|
import type { OAuthProvider } from "@oh-my-pi/pi-ai/oauth/types";
|
|
5
5
|
import type { Component, OverlayHandle } from "@oh-my-pi/pi-tui";
|
|
6
|
-
import { Input, Loader, Spacer, Text } from "@oh-my-pi/pi-tui";
|
|
6
|
+
import { Input, Loader, Spacer, setTuiTight, Text } from "@oh-my-pi/pi-tui";
|
|
7
7
|
import { getAgentDbPath, getProjectDir, normalizePathForComparison } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import { formatModelSelectorValue } from "../../config/model-resolver";
|
|
9
9
|
import { getRoleInfo } from "../../config/model-roles";
|
|
@@ -67,7 +67,6 @@ import { TranscriptBlock } from "../components/transcript-container";
|
|
|
67
67
|
import { TreeSelectorComponent } from "../components/tree-selector";
|
|
68
68
|
import { UserMessageSelectorComponent } from "../components/user-message-selector";
|
|
69
69
|
import type { SessionObserverRegistry } from "../session-observer-registry";
|
|
70
|
-
import { computeContextBreakdown } from "../utils/context-usage";
|
|
71
70
|
import { buildCopyTargets } from "../utils/copy-targets";
|
|
72
71
|
|
|
73
72
|
const MANUAL_LOGIN_TIP = "Tip: You can complete pairing with /login <redirect URL>.";
|
|
@@ -325,6 +324,13 @@ export class SelectorController {
|
|
|
325
324
|
}
|
|
326
325
|
}
|
|
327
326
|
break;
|
|
327
|
+
case "tui.tight":
|
|
328
|
+
setTuiTight(value as boolean);
|
|
329
|
+
this.ctx.ui.invalidate();
|
|
330
|
+
this.ctx.updateEditorTopBorder();
|
|
331
|
+
this.ctx.ui.requestRender();
|
|
332
|
+
break;
|
|
333
|
+
|
|
328
334
|
case "theme": {
|
|
329
335
|
setTheme(value as string, true).then(result => {
|
|
330
336
|
this.ctx.statusLine.invalidate();
|
|
@@ -380,6 +386,7 @@ export class SelectorController {
|
|
|
380
386
|
this.ctx.session.agent.repetitionPenalty = repetitionPenalty >= 0 ? repetitionPenalty : undefined;
|
|
381
387
|
break;
|
|
382
388
|
}
|
|
389
|
+
case "git.enabled":
|
|
383
390
|
case "statusLinePreset":
|
|
384
391
|
case "statusLine.preset":
|
|
385
392
|
case "statusLineSeparator":
|
|
@@ -443,7 +450,7 @@ export class SelectorController {
|
|
|
443
450
|
}
|
|
444
451
|
|
|
445
452
|
showModelSelector(options?: { temporaryOnly?: boolean }): void {
|
|
446
|
-
const currentContextTokens =
|
|
453
|
+
const currentContextTokens = this.ctx.session.getContextUsage()?.tokens ?? 0;
|
|
447
454
|
this.showSelector(done => {
|
|
448
455
|
const selector = new ModelSelectorComponent(
|
|
449
456
|
this.ctx.ui,
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
ProcessTerminal,
|
|
31
31
|
Spacer,
|
|
32
32
|
setTerminalTextSizing,
|
|
33
|
+
setTuiTight,
|
|
33
34
|
TERMINAL,
|
|
34
35
|
Text,
|
|
35
36
|
TUI,
|
|
@@ -566,6 +567,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
566
567
|
);
|
|
567
568
|
}
|
|
568
569
|
|
|
570
|
+
setTuiTight(settings.get("tui.tight"));
|
|
569
571
|
this.ui = new TUI(new ProcessTerminal(), settings.get("showHardwareCursor"));
|
|
570
572
|
this.ui.setMaxInlineImages(settings.get("tui.maxInlineImages"));
|
|
571
573
|
// OSC 66 text-sizing is Kitty-only; resolve the setting against the terminal's
|
|
@@ -852,14 +854,16 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
852
854
|
}),
|
|
853
855
|
);
|
|
854
856
|
// Set up theme file watcher
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
857
|
+
this.#eventBusUnsubscribers.push(
|
|
858
|
+
onThemeChange(() => {
|
|
859
|
+
this.#clearWorkingMessageAccentCache();
|
|
860
|
+
clearRenderCache();
|
|
861
|
+
clearMermaidCache();
|
|
862
|
+
this.ui.invalidate();
|
|
863
|
+
this.updateEditorBorderColor();
|
|
864
|
+
this.ui.requestRender();
|
|
865
|
+
}),
|
|
866
|
+
);
|
|
863
867
|
|
|
864
868
|
// Subscribe to terminal dark/light appearance changes.
|
|
865
869
|
// The terminal queries background color via OSC 11 at startup and on
|
|
@@ -2179,7 +2183,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2179
2183
|
}
|
|
2180
2184
|
|
|
2181
2185
|
#formatKeepContextLabel(contextUsage: ContextUsage | undefined): string {
|
|
2182
|
-
if (contextUsage
|
|
2186
|
+
if (!contextUsage) {
|
|
2183
2187
|
return "Approve and keep context";
|
|
2184
2188
|
}
|
|
2185
2189
|
const tokens = formatContextTokenCount(contextUsage.tokens);
|
|
@@ -2188,7 +2192,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
2188
2192
|
}
|
|
2189
2193
|
|
|
2190
2194
|
#isKeepContextDisabled(contextUsage: ContextUsage | undefined): boolean {
|
|
2191
|
-
return contextUsage
|
|
2195
|
+
return contextUsage !== undefined && contextUsage.percent > PLAN_KEEP_CONTEXT_DISABLE_THRESHOLD_PERCENT;
|
|
2192
2196
|
}
|
|
2193
2197
|
|
|
2194
2198
|
async #openPlanInExternalEditor(planFilePath: string): Promise<void> {
|
|
@@ -3720,6 +3724,34 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
3720
3724
|
return this.#btwController.handleEscape();
|
|
3721
3725
|
}
|
|
3722
3726
|
|
|
3727
|
+
canBranchBtw(): boolean {
|
|
3728
|
+
return this.#btwController.canBranch();
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
handleBtwBranchKey(): Promise<boolean> {
|
|
3732
|
+
return this.#btwController.handleBranch();
|
|
3733
|
+
}
|
|
3734
|
+
|
|
3735
|
+
async handleBtwBranch(question: string, assistantMessage: AssistantMessage): Promise<void> {
|
|
3736
|
+
try {
|
|
3737
|
+
const result = await this.session.branchFromBtw(question, assistantMessage);
|
|
3738
|
+
if (result.cancelled) {
|
|
3739
|
+
this.showStatus("/btw branch cancelled", { dim: true });
|
|
3740
|
+
return;
|
|
3741
|
+
}
|
|
3742
|
+
this.#btwController.dispose();
|
|
3743
|
+
this.#omfgController.dispose();
|
|
3744
|
+
this.chatContainer.clear();
|
|
3745
|
+
this.renderInitialMessages({ clearTerminalHistory: true });
|
|
3746
|
+
this.updateEditorBorderColor();
|
|
3747
|
+
this.showStatus(
|
|
3748
|
+
result.sessionFile ? `Branched /btw to ${path.basename(result.sessionFile)}` : "Branched /btw",
|
|
3749
|
+
);
|
|
3750
|
+
} catch (error) {
|
|
3751
|
+
this.showError(`Cannot branch /btw: ${error instanceof Error ? error.message : String(error)}`);
|
|
3752
|
+
}
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3723
3755
|
handleOmfgCommand(complaint: string): Promise<void> {
|
|
3724
3756
|
return this.#omfgController.start(complaint);
|
|
3725
3757
|
}
|
|
@@ -108,7 +108,7 @@ export interface RpcSessionState {
|
|
|
108
108
|
/** For session dump / export (plain-text parity with /dump). */
|
|
109
109
|
systemPrompt?: string[];
|
|
110
110
|
dumpTools?: Array<{ name: string; description: string; parameters: unknown; examples?: readonly ToolExample[] }>;
|
|
111
|
-
/** Current context window usage.
|
|
111
|
+
/** Current context window usage. */
|
|
112
112
|
contextUsage?: ContextUsage;
|
|
113
113
|
}
|
|
114
114
|
|
|
@@ -9,6 +9,7 @@ import { SetupWizardComponent } from "./wizard-overlay";
|
|
|
9
9
|
|
|
10
10
|
export type { SetupScene, SetupSceneController, SetupSceneHost, SetupSceneResult } from "./scenes/types";
|
|
11
11
|
|
|
12
|
+
export { runStartupSplash } from "./startup-splash";
|
|
12
13
|
export { CURRENT_SETUP_VERSION };
|
|
13
14
|
|
|
14
15
|
export const ALL_SCENES = [
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { PASTE_CODE_LOGIN_PROVIDERS } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import type { OAuthProvider } from "@oh-my-pi/pi-ai/oauth/types";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type Component,
|
|
6
|
+
type Focusable,
|
|
7
|
+
Input,
|
|
8
|
+
matchesKey,
|
|
9
|
+
type SgrMouseEvent,
|
|
10
|
+
wrapTextWithAnsi,
|
|
11
|
+
} from "@oh-my-pi/pi-tui";
|
|
5
12
|
import { getAgentDbPath } from "@oh-my-pi/pi-utils";
|
|
13
|
+
import { copyToClipboard } from "../../../utils/clipboard";
|
|
6
14
|
import { OAuthSelectorComponent } from "../../components/oauth-selector";
|
|
7
15
|
import { theme } from "../../theme/theme";
|
|
8
16
|
import type { SetupSceneHost, SetupTab } from "./types";
|
|
@@ -11,10 +19,52 @@ function loginUrlLink(url: string): string {
|
|
|
11
19
|
return `\x1b]8;;${url}\x07Open login URL\x1b]8;;\x07`;
|
|
12
20
|
}
|
|
13
21
|
|
|
22
|
+
function loginCopyHint(): string {
|
|
23
|
+
return theme.fg("dim", "(clipboard copy attempted; Alt+C retries)");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class CopyablePromptInput implements Component, Focusable {
|
|
27
|
+
#input: Input;
|
|
28
|
+
#onCopy: () => void;
|
|
29
|
+
|
|
30
|
+
constructor(input: Input, onCopy: () => void) {
|
|
31
|
+
this.#input = input;
|
|
32
|
+
this.#onCopy = onCopy;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get focused(): boolean {
|
|
36
|
+
return this.#input.focused;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
set focused(value: boolean) {
|
|
40
|
+
this.#input.focused = value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setUseTerminalCursor(useTerminalCursor: boolean): void {
|
|
44
|
+
this.#input.setUseTerminalCursor(useTerminalCursor);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
render(width: number): readonly string[] {
|
|
48
|
+
return this.#input.render(width);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
handleInput(data: string): void {
|
|
52
|
+
if (matchesKey(data, "alt+c")) {
|
|
53
|
+
this.#onCopy();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.#input.handleInput(data);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
invalidate(): void {
|
|
60
|
+
this.#input.invalidate();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
14
64
|
interface PromptState {
|
|
15
65
|
message: string;
|
|
16
66
|
placeholder?: string;
|
|
17
|
-
input:
|
|
67
|
+
input: CopyablePromptInput;
|
|
18
68
|
}
|
|
19
69
|
|
|
20
70
|
/**
|
|
@@ -62,6 +112,10 @@ export class SignInTab implements SetupTab {
|
|
|
62
112
|
|
|
63
113
|
handleInput(data: string): void {
|
|
64
114
|
if (this.#loggingInProvider) {
|
|
115
|
+
if (this.#authUrl && (matchesKey(data, "alt+c") || (data === "c" && !this.#prompt))) {
|
|
116
|
+
void this.#copyAuthUrl();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
65
119
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
|
|
66
120
|
this.#loginAbort?.abort();
|
|
67
121
|
}
|
|
@@ -88,7 +142,10 @@ export class SignInTab implements SetupTab {
|
|
|
88
142
|
|
|
89
143
|
const urlLines = this.#authUrl ? wrapTextWithAnsi(theme.fg("dim", this.#authUrl), width) : [];
|
|
90
144
|
if (this.#authUrl) {
|
|
91
|
-
lines.push(
|
|
145
|
+
lines.push(
|
|
146
|
+
theme.fg("accent", `Browser login: ${loginUrlLink(this.#authUrl)} ${loginCopyHint()}`),
|
|
147
|
+
...urlLines.slice(0, 2),
|
|
148
|
+
);
|
|
92
149
|
}
|
|
93
150
|
if (this.#prompt) {
|
|
94
151
|
lines.push(theme.fg("warning", this.#prompt.message));
|
|
@@ -140,6 +197,7 @@ export class SignInTab implements SetupTab {
|
|
|
140
197
|
if (useManualInput) {
|
|
141
198
|
this.#statusLines.push(theme.fg("dim", "Paste the returned code or redirect URL when prompted."));
|
|
142
199
|
}
|
|
200
|
+
void this.#copyAuthUrl();
|
|
143
201
|
this.host.ctx.openInBrowser(info.url);
|
|
144
202
|
this.host.requestRender();
|
|
145
203
|
},
|
|
@@ -184,12 +242,26 @@ export class SignInTab implements SetupTab {
|
|
|
184
242
|
}
|
|
185
243
|
}
|
|
186
244
|
|
|
245
|
+
async #copyAuthUrl(): Promise<void> {
|
|
246
|
+
const url = this.#authUrl;
|
|
247
|
+
if (!url) return;
|
|
248
|
+
try {
|
|
249
|
+
await copyToClipboard(url);
|
|
250
|
+
} catch {
|
|
251
|
+
// Clipboard integration is best-effort; the full URL remains rendered below.
|
|
252
|
+
}
|
|
253
|
+
this.host.requestRender();
|
|
254
|
+
}
|
|
255
|
+
|
|
187
256
|
#showPrompt(prompt: { message: string; placeholder?: string }): Promise<string> {
|
|
188
257
|
this.#resolvePrompt("");
|
|
189
258
|
const input = new Input();
|
|
259
|
+
const focusInput = new CopyablePromptInput(input, () => {
|
|
260
|
+
void this.#copyAuthUrl();
|
|
261
|
+
});
|
|
190
262
|
const pending = Promise.withResolvers<string>();
|
|
191
263
|
this.#promptResolve = pending.resolve;
|
|
192
|
-
this.#prompt = { message: prompt.message, placeholder: prompt.placeholder, input };
|
|
264
|
+
this.#prompt = { message: prompt.message, placeholder: prompt.placeholder, input: focusInput };
|
|
193
265
|
input.onSubmit = value => {
|
|
194
266
|
this.#resolvePrompt(value);
|
|
195
267
|
};
|
|
@@ -197,7 +269,7 @@ export class SignInTab implements SetupTab {
|
|
|
197
269
|
this.#loginAbort?.abort();
|
|
198
270
|
this.#resolvePrompt("");
|
|
199
271
|
};
|
|
200
|
-
this.host.setFocus(
|
|
272
|
+
this.host.setFocus(focusInput);
|
|
201
273
|
this.host.requestRender();
|
|
202
274
|
return pending.promise;
|
|
203
275
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { type Component, matchesKey, type OverlayFocusOwner } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import type { InteractiveModeContext } from "../types";
|
|
3
|
+
import { renderSetupSplash, SETUP_SPLASH_MS, SETUP_TICK_MS } from "./scenes/splash";
|
|
4
|
+
|
|
5
|
+
export interface RunStartupSplashOptions {
|
|
6
|
+
readonly durationMs?: number;
|
|
7
|
+
readonly tickMs?: number;
|
|
8
|
+
readonly now?: () => number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class StartupSplashComponent implements Component, OverlayFocusOwner {
|
|
12
|
+
#phaseStartedAt = 0;
|
|
13
|
+
#timer: NodeJS.Timeout | undefined;
|
|
14
|
+
#done = Promise.withResolvers<void>();
|
|
15
|
+
#disposed = false;
|
|
16
|
+
readonly #durationMs: number;
|
|
17
|
+
readonly #tickMs: number;
|
|
18
|
+
readonly #now: () => number;
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
readonly ctx: InteractiveModeContext,
|
|
22
|
+
options: RunStartupSplashOptions = {},
|
|
23
|
+
) {
|
|
24
|
+
this.#durationMs = options.durationMs ?? SETUP_SPLASH_MS;
|
|
25
|
+
this.#tickMs = options.tickMs ?? SETUP_TICK_MS;
|
|
26
|
+
this.#now = options.now ?? (() => performance.now());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
run(): Promise<void> {
|
|
30
|
+
this.#phaseStartedAt = this.#now();
|
|
31
|
+
this.#startTimer();
|
|
32
|
+
this.ctx.ui.requestRender();
|
|
33
|
+
return this.#done.promise;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
dispose(): void {
|
|
37
|
+
this.#disposed = true;
|
|
38
|
+
this.#stopTimer();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
ownsOverlayFocusTarget(component: Component): boolean {
|
|
42
|
+
return component === this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
handleInput(data: string): void {
|
|
46
|
+
if (
|
|
47
|
+
matchesKey(data, "enter") ||
|
|
48
|
+
matchesKey(data, "return") ||
|
|
49
|
+
matchesKey(data, "space") ||
|
|
50
|
+
matchesKey(data, "escape")
|
|
51
|
+
) {
|
|
52
|
+
this.#complete();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
render(width: number): readonly string[] {
|
|
57
|
+
const elapsedMs = Math.min(this.#durationMs, Math.max(0, this.#now() - this.#phaseStartedAt));
|
|
58
|
+
return renderSetupSplash(Math.max(1, width), Math.max(1, this.ctx.ui.terminal.rows), elapsedMs);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#startTimer(): void {
|
|
62
|
+
if (this.#timer) return;
|
|
63
|
+
this.#timer = setInterval(() => {
|
|
64
|
+
if (this.#disposed) return;
|
|
65
|
+
const elapsed = this.#now() - this.#phaseStartedAt;
|
|
66
|
+
if (elapsed >= this.#durationMs) {
|
|
67
|
+
this.#complete();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.ctx.ui.requestRender();
|
|
71
|
+
}, this.#tickMs);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#stopTimer(): void {
|
|
75
|
+
if (!this.#timer) return;
|
|
76
|
+
clearInterval(this.#timer);
|
|
77
|
+
this.#timer = undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#complete(): void {
|
|
81
|
+
if (this.#disposed) return;
|
|
82
|
+
this.#stopTimer();
|
|
83
|
+
this.#done.resolve();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function runStartupSplash(
|
|
88
|
+
ctx: InteractiveModeContext,
|
|
89
|
+
options: RunStartupSplashOptions = {},
|
|
90
|
+
): Promise<void> {
|
|
91
|
+
const component = new StartupSplashComponent(ctx, options);
|
|
92
|
+
const overlay = ctx.ui.showOverlay(component, {
|
|
93
|
+
width: "100%",
|
|
94
|
+
maxHeight: "100%",
|
|
95
|
+
anchor: "top-left",
|
|
96
|
+
margin: 0,
|
|
97
|
+
fullscreen: true,
|
|
98
|
+
});
|
|
99
|
+
try {
|
|
100
|
+
ctx.ui.setFocus(component);
|
|
101
|
+
await component.run();
|
|
102
|
+
} finally {
|
|
103
|
+
component.dispose();
|
|
104
|
+
ctx.ui.setFocus(component);
|
|
105
|
+
overlay.hide();
|
|
106
|
+
}
|
|
107
|
+
}
|