mini-coder 0.5.14 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -109
- package/bin/mc.ts +8 -11
- package/bun.lock +79 -269
- package/package.json +17 -22
- package/src/agent.ts +237 -1403
- package/src/args.ts +289 -0
- package/src/headless.ts +43 -358
- package/src/index.ts +29 -1016
- package/src/oauth.ts +117 -0
- package/src/prompt.ts +227 -284
- package/src/session.ts +55 -1306
- package/src/shared.ts +117 -38
- package/src/tool-bash.ts +110 -0
- package/src/tool-edit.ts +133 -0
- package/src/tool-task.ts +114 -0
- package/src/tui-components.ts +150 -0
- package/src/tui-conversation.ts +262 -0
- package/src/tui-editor.ts +29 -0
- package/src/tui-overlay.ts +403 -0
- package/src/tui.ts +236 -0
- package/src/types.ts +160 -0
- package/tsconfig.json +17 -0
- package/BENCHMARK.md +0 -107
- package/LICENSE +0 -9
- package/PROGRESS.md +0 -5
- package/assets/icon-1-minimal.svg +0 -31
- package/assets/icon-2-dark-terminal.svg +0 -48
- package/assets/icon-3-gradient-modern.svg +0 -45
- package/assets/icon-4-filled-bold.svg +0 -54
- package/assets/icon-5-community-badge.svg +0 -63
- package/assets/mc-claude-smart.png +0 -0
- package/assets/mc-gpt-smart.png +0 -0
- package/assets/preview-0-5-0.png +0 -0
- package/assets/preview.gif +0 -0
- package/benchmark-baseline.sh +0 -15
- package/benchmark-loop.sh +0 -19
- package/skills-lock.json +0 -15
- package/src/assistant-output.ts +0 -73
- package/src/cli.ts +0 -134
- package/src/delegation.ts +0 -238
- package/src/errors.ts +0 -15
- package/src/git.ts +0 -247
- package/src/input.ts +0 -168
- package/src/mcp.ts +0 -609
- package/src/paths.ts +0 -37
- package/src/session-message.ts +0 -385
- package/src/settings.ts +0 -449
- package/src/skills.ts +0 -271
- package/src/submit.ts +0 -376
- package/src/text.ts +0 -71
- package/src/theme.ts +0 -330
- package/src/tool-common.ts +0 -93
- package/src/tool-delegate.ts +0 -125
- package/src/tool-grep.ts +0 -606
- package/src/tool-read.ts +0 -313
- package/src/tool-shell.ts +0 -1051
- package/src/tools.ts +0 -1179
- package/src/ui/agent.ts +0 -320
- package/src/ui/commands.test.ts +0 -957
- package/src/ui/commands.ts +0 -848
- package/src/ui/conversation.test.ts +0 -585
- package/src/ui/conversation.ts +0 -1836
- package/src/ui/help.ts +0 -158
- package/src/ui/input.test.ts +0 -64
- package/src/ui/input.ts +0 -138
- package/src/ui/overlay.ts +0 -59
- package/src/ui/runtime.ts +0 -69
- package/src/ui/status.ts +0 -220
- package/src/ui.ts +0 -1190
- package/src/version.ts +0 -48
package/src/ui/help.ts
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `/help` text rendering and command descriptions for the terminal UI.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { AppState } from "../index.ts";
|
|
8
|
-
import { COMMANDS, SKILL_COMMAND } from "../input.ts";
|
|
9
|
-
import { abbreviatePath } from "./status.ts";
|
|
10
|
-
|
|
11
|
-
/** Help text inputs derived from application state. */
|
|
12
|
-
export interface HelpRenderState {
|
|
13
|
-
/** Available provider credentials keyed by provider id. */
|
|
14
|
-
providers: AppState["providers"];
|
|
15
|
-
/** Current model selection. */
|
|
16
|
-
model: AppState["model"];
|
|
17
|
-
/** Loaded AGENTS.md files. */
|
|
18
|
-
agentsMd: AppState["agentsMd"];
|
|
19
|
-
/** Discovered skills. */
|
|
20
|
-
skills: AppState["skills"];
|
|
21
|
-
/** Whether reasoning blocks are shown in the log. */
|
|
22
|
-
showReasoning: AppState["showReasoning"];
|
|
23
|
-
/** Whether verbose tool rendering is enabled in the log. */
|
|
24
|
-
verbose: AppState["verbose"];
|
|
25
|
-
/** Configured MCP servers and their current on/off state. */
|
|
26
|
-
mcpServers: AppState["mcpServers"];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Command descriptions for `/help` and command autocomplete. */
|
|
30
|
-
export const COMMAND_DESCRIPTIONS: Record<string, string> = {
|
|
31
|
-
model: "Select a model",
|
|
32
|
-
effort: "Set reasoning effort",
|
|
33
|
-
session: "Resume a session",
|
|
34
|
-
new: "New session",
|
|
35
|
-
fork: "Fork session",
|
|
36
|
-
undo: "Undo last turn",
|
|
37
|
-
reasoning: "Toggle thinking display",
|
|
38
|
-
verbose: "Toggle verbose tool rendering",
|
|
39
|
-
mcp: "Toggle configured MCP servers",
|
|
40
|
-
todo: "Show the current todo list",
|
|
41
|
-
login: "OAuth login",
|
|
42
|
-
logout: "OAuth logout",
|
|
43
|
-
help: "Show help",
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/** Slash-skill label shown in `/help` and command autocomplete. */
|
|
47
|
-
export const SKILL_REFERENCE_LABEL = `/${SKILL_COMMAND}:name`;
|
|
48
|
-
|
|
49
|
-
/** Description for the slash-skill input helper. */
|
|
50
|
-
export const SKILL_REFERENCE_DESCRIPTION =
|
|
51
|
-
"Insert a discovered skill into the next message";
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Get the `/help` description for a command, including current state when relevant.
|
|
55
|
-
*
|
|
56
|
-
* @param command - Command name.
|
|
57
|
-
* @param state - Help-relevant application state.
|
|
58
|
-
* @returns Markdown-ready command description.
|
|
59
|
-
*/
|
|
60
|
-
function getHelpCommandDescription(
|
|
61
|
-
command: (typeof COMMANDS)[number],
|
|
62
|
-
state: Pick<HelpRenderState, "showReasoning" | "verbose">,
|
|
63
|
-
): string {
|
|
64
|
-
const description = COMMAND_DESCRIPTIONS[command] ?? "";
|
|
65
|
-
if (command === "reasoning") {
|
|
66
|
-
return `${description} _(currently ${state.showReasoning ? "on" : "off"})_`;
|
|
67
|
-
}
|
|
68
|
-
if (command === "verbose") {
|
|
69
|
-
return `${description} _(currently ${state.verbose ? "on" : "off"})_`;
|
|
70
|
-
}
|
|
71
|
-
return description;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function formatInlineCode(text: string): string {
|
|
75
|
-
return `\`${text}\``;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function formatInlineCodeList(items: readonly string[]): string {
|
|
79
|
-
return items.map((item) => formatInlineCode(item)).join(", ");
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function formatMcpServerState(
|
|
83
|
-
server: HelpRenderState["mcpServers"][number],
|
|
84
|
-
): string {
|
|
85
|
-
return `${formatInlineCode(server.name)} (${server.enabled ? "on" : "off"})`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Build the `/help` text shown in the conversation log.
|
|
90
|
-
*
|
|
91
|
-
* @param state - Help-relevant application state.
|
|
92
|
-
* @returns Multi-line markdown help text for display.
|
|
93
|
-
*/
|
|
94
|
-
export function buildHelpText(state: HelpRenderState): string {
|
|
95
|
-
const lines: string[] = ["# Help", "", "## Commands", ""];
|
|
96
|
-
|
|
97
|
-
for (const command of COMMANDS) {
|
|
98
|
-
lines.push(
|
|
99
|
-
`- ${formatInlineCode(`/${command}`)} — ${getHelpCommandDescription(command, state)}`,
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
lines.push(
|
|
103
|
-
`- ${formatInlineCode(SKILL_REFERENCE_LABEL)} — ${SKILL_REFERENCE_DESCRIPTION}; submit ${formatInlineCode(`/${SKILL_COMMAND}`)} to open the skill picker.`,
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
const providerNames = Array.from(state.providers.keys());
|
|
107
|
-
const mcpServerStates = state.mcpServers.map((server) =>
|
|
108
|
-
formatMcpServerState(server),
|
|
109
|
-
);
|
|
110
|
-
lines.push(
|
|
111
|
-
"",
|
|
112
|
-
"## Keyboard",
|
|
113
|
-
"",
|
|
114
|
-
"- `Enter` submits the current draft.",
|
|
115
|
-
"- `Shift+Enter` inserts a newline.",
|
|
116
|
-
"- `Tab` opens command autocomplete when the draft starts with `/`, preserving the current draft.",
|
|
117
|
-
"- Otherwise, `Tab` autocompletes file paths and can open a path picker when there are multiple matches.",
|
|
118
|
-
"- `Ctrl+R` opens global input history search.",
|
|
119
|
-
"- `Escape` closes the current overlay and returns focus to the input.",
|
|
120
|
-
"- With no overlay open, `Escape` interrupts the current turn.",
|
|
121
|
-
"- Otherwise, `Escape` does nothing.",
|
|
122
|
-
"- `Ctrl+C` exits gracefully.",
|
|
123
|
-
"- `Ctrl+D` exits when the input is empty.",
|
|
124
|
-
"- `Ctrl+Z` suspends the app to the background.",
|
|
125
|
-
"",
|
|
126
|
-
"## Current state",
|
|
127
|
-
"",
|
|
128
|
-
providerNames.length > 0
|
|
129
|
-
? `- Providers: ${formatInlineCodeList(providerNames)}`
|
|
130
|
-
: "- Providers: none — use `/login`",
|
|
131
|
-
state.model
|
|
132
|
-
? `- Model: ${formatInlineCode(`${state.model.provider}/${state.model.id}`)}`
|
|
133
|
-
: "- Model: none — use `/model`",
|
|
134
|
-
mcpServerStates.length > 0
|
|
135
|
-
? `- MCP servers: ${mcpServerStates.join(", ")}`
|
|
136
|
-
: "- MCP servers: none — configure them in `settings.json`",
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
if (state.agentsMd.length > 0) {
|
|
140
|
-
lines.push("", "## Loaded `AGENTS.md` files", "");
|
|
141
|
-
for (const agentFile of state.agentsMd) {
|
|
142
|
-
lines.push(`- ${formatInlineCode(abbreviatePath(agentFile.path))}`);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (state.skills.length > 0) {
|
|
147
|
-
lines.push("", "## Skills", "");
|
|
148
|
-
for (const skill of state.skills) {
|
|
149
|
-
lines.push(
|
|
150
|
-
skill.description
|
|
151
|
-
? `- ${formatInlineCode(skill.name)} — ${skill.description}`
|
|
152
|
-
: `- ${formatInlineCode(skill.name)}`,
|
|
153
|
-
);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return lines.join("\n");
|
|
158
|
-
}
|
package/src/ui/input.test.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { autocompleteInputPath, findInputPathMatches } from "./input.ts";
|
|
6
|
-
|
|
7
|
-
const tempDirs: string[] = [];
|
|
8
|
-
|
|
9
|
-
function createTempDir(): string {
|
|
10
|
-
const dir = mkdtempSync(join(tmpdir(), "mini-coder-ui-input-test-"));
|
|
11
|
-
tempDirs.push(dir);
|
|
12
|
-
return dir;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
afterEach(() => {
|
|
16
|
-
for (const dir of tempDirs.splice(0)) {
|
|
17
|
-
rmSync(dir, { recursive: true, force: true });
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
describe("ui/input", () => {
|
|
22
|
-
test("autocompleteInputPath completes the last file path token when exactly one match is available", () => {
|
|
23
|
-
const cwd = createTempDir();
|
|
24
|
-
mkdirSync(join(cwd, "src"), { recursive: true });
|
|
25
|
-
writeFileSync(join(cwd, "src", "ui.ts"), "", "utf-8");
|
|
26
|
-
|
|
27
|
-
expect(autocompleteInputPath("inspect src/u", cwd)).toBe(
|
|
28
|
-
"inspect src/ui.ts",
|
|
29
|
-
);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("autocompleteInputPath returns null when multiple matches are available", () => {
|
|
33
|
-
const cwd = createTempDir();
|
|
34
|
-
mkdirSync(join(cwd, "src"), { recursive: true });
|
|
35
|
-
writeFileSync(join(cwd, "src", "ui.ts"), "", "utf-8");
|
|
36
|
-
writeFileSync(join(cwd, "src", "utils.ts"), "", "utf-8");
|
|
37
|
-
|
|
38
|
-
expect(autocompleteInputPath("inspect src/u", cwd)).toBeNull();
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
test("findInputPathMatches returns sorted selectable matches for the last file path token", () => {
|
|
42
|
-
const cwd = createTempDir();
|
|
43
|
-
mkdirSync(join(cwd, "src"), { recursive: true });
|
|
44
|
-
writeFileSync(join(cwd, "src", "alpine.ts"), "", "utf-8");
|
|
45
|
-
writeFileSync(join(cwd, "src", "alpha.ts"), "", "utf-8");
|
|
46
|
-
|
|
47
|
-
expect(findInputPathMatches("inspect src/al", cwd)).toEqual([
|
|
48
|
-
{
|
|
49
|
-
label: "src/alpha.ts",
|
|
50
|
-
value: "inspect src/alpha.ts",
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
label: "src/alpine.ts",
|
|
54
|
-
value: "inspect src/alpine.ts",
|
|
55
|
-
},
|
|
56
|
-
]);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("autocompleteInputPath returns null when no completion is available", () => {
|
|
60
|
-
const cwd = createTempDir();
|
|
61
|
-
|
|
62
|
-
expect(autocompleteInputPath("inspect src/u", cwd)).toBeNull();
|
|
63
|
-
});
|
|
64
|
-
});
|
package/src/ui/input.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Input-area rendering and path-autocomplete helpers for the terminal UI.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { readdirSync } from "node:fs";
|
|
8
|
-
import { isAbsolute, join } from "node:path";
|
|
9
|
-
import { Text, TextInput } from "@cel-tui/core";
|
|
10
|
-
import type { Node } from "@cel-tui/types";
|
|
11
|
-
import type { Theme } from "../theme.ts";
|
|
12
|
-
|
|
13
|
-
const INPUT_PLACEHOLDER =
|
|
14
|
-
"`Ctrl+R` for input history, `/` + `Tab` for interactive menu, or type a message…";
|
|
15
|
-
|
|
16
|
-
/** Stable callbacks for the controlled TextInput. */
|
|
17
|
-
export interface InputController {
|
|
18
|
-
/** Update the controlled input value and re-render. */
|
|
19
|
-
onChange: (value: string) => void;
|
|
20
|
-
/** Mark the input as focused and re-render. */
|
|
21
|
-
onFocus: () => void;
|
|
22
|
-
/** Mark the input as blurred and re-render. */
|
|
23
|
-
onBlur: () => void;
|
|
24
|
-
/** Intercept submit/autocomplete keys before default editing runs. */
|
|
25
|
-
onKeyPress: (key: string) => boolean | undefined;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** A selectable path-autocomplete match for the current input draft. */
|
|
29
|
-
export interface InputPathMatch {
|
|
30
|
-
/** Path label shown in the overlay. */
|
|
31
|
-
label: string;
|
|
32
|
-
/** Full draft value to apply when this match is selected. */
|
|
33
|
-
value: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function listInputPathMatches(value: string, cwd: string): InputPathMatch[] {
|
|
37
|
-
const tokenMatch = /(^|\s)(\S+)$/.exec(value);
|
|
38
|
-
if (!tokenMatch?.[2]) {
|
|
39
|
-
return [];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const token = tokenMatch[2];
|
|
43
|
-
const tokenStart = tokenMatch.index + tokenMatch[1]!.length;
|
|
44
|
-
const slashIndex = token.lastIndexOf("/");
|
|
45
|
-
const dirToken = slashIndex >= 0 ? token.slice(0, slashIndex + 1) : "";
|
|
46
|
-
const partial = token.slice(slashIndex + 1);
|
|
47
|
-
const searchDir = isAbsolute(token)
|
|
48
|
-
? dirToken || "/"
|
|
49
|
-
: join(cwd, dirToken || ".");
|
|
50
|
-
|
|
51
|
-
const entries = (() => {
|
|
52
|
-
try {
|
|
53
|
-
return readdirSync(searchDir, {
|
|
54
|
-
encoding: "utf8",
|
|
55
|
-
withFileTypes: true,
|
|
56
|
-
});
|
|
57
|
-
} catch {
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
})();
|
|
61
|
-
if (!entries) {
|
|
62
|
-
return [];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const showHidden = partial.startsWith(".");
|
|
66
|
-
return entries
|
|
67
|
-
.filter((entry) => (showHidden ? true : !entry.name.startsWith(".")))
|
|
68
|
-
.filter((entry) => entry.name.startsWith(partial))
|
|
69
|
-
.sort((a, b) => a.name.localeCompare(b.name))
|
|
70
|
-
.map((entry) => {
|
|
71
|
-
const completedPath = `${dirToken}${entry.name}${entry.isDirectory() ? "/" : ""}`;
|
|
72
|
-
return {
|
|
73
|
-
label: completedPath,
|
|
74
|
-
value: `${value.slice(0, tokenStart)}${completedPath}`,
|
|
75
|
-
};
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Find selectable path matches for the final path token in the current draft.
|
|
81
|
-
*
|
|
82
|
-
* @param value - Current input draft.
|
|
83
|
-
* @param cwd - Working directory used to resolve relative paths.
|
|
84
|
-
* @returns Matching path completions with their applied draft values.
|
|
85
|
-
*/
|
|
86
|
-
export function findInputPathMatches(
|
|
87
|
-
value: string,
|
|
88
|
-
cwd: string,
|
|
89
|
-
): InputPathMatch[] {
|
|
90
|
-
return listInputPathMatches(value, cwd);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Attempt to autocomplete the final path token in the current draft.
|
|
95
|
-
*
|
|
96
|
-
* @param value - Current input draft.
|
|
97
|
-
* @param cwd - Working directory used to resolve relative paths.
|
|
98
|
-
* @returns The completed input value, or `null` when direct completion is unavailable.
|
|
99
|
-
*/
|
|
100
|
-
export function autocompleteInputPath(
|
|
101
|
-
value: string,
|
|
102
|
-
cwd: string,
|
|
103
|
-
): string | null {
|
|
104
|
-
const matches = listInputPathMatches(value, cwd);
|
|
105
|
-
return matches.length === 1 ? matches[0]!.value : null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Render the input area.
|
|
110
|
-
*
|
|
111
|
-
* @param theme - Active UI theme.
|
|
112
|
-
* @param controller - Stable TextInput callbacks.
|
|
113
|
-
* @param value - Current input draft.
|
|
114
|
-
* @param focused - Whether the input should be focused.
|
|
115
|
-
* @returns The input area node.
|
|
116
|
-
*/
|
|
117
|
-
export function renderInputArea(
|
|
118
|
-
theme: Theme,
|
|
119
|
-
controller: InputController,
|
|
120
|
-
value: string,
|
|
121
|
-
focused: boolean,
|
|
122
|
-
): Node {
|
|
123
|
-
return TextInput({
|
|
124
|
-
minHeight: 2,
|
|
125
|
-
maxHeight: 10,
|
|
126
|
-
padding: { x: 1 },
|
|
127
|
-
value,
|
|
128
|
-
onChange: controller.onChange,
|
|
129
|
-
placeholder: Text(INPUT_PLACEHOLDER, {
|
|
130
|
-
fgColor: theme.mutedText,
|
|
131
|
-
italic: true,
|
|
132
|
-
}),
|
|
133
|
-
focused,
|
|
134
|
-
onFocus: controller.onFocus,
|
|
135
|
-
onBlur: controller.onBlur,
|
|
136
|
-
onKeyPress: controller.onKeyPress,
|
|
137
|
-
});
|
|
138
|
-
}
|
package/src/ui/overlay.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Overlay rendering primitives for the terminal UI.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { SelectInstance } from "@cel-tui/components";
|
|
8
|
-
import { Text, VStack } from "@cel-tui/core";
|
|
9
|
-
import type { Node } from "@cel-tui/types";
|
|
10
|
-
import type { Theme } from "../theme.ts";
|
|
11
|
-
|
|
12
|
-
/** Max visible items in the overlay Select. */
|
|
13
|
-
export const OVERLAY_MAX_VISIBLE = 15;
|
|
14
|
-
|
|
15
|
-
/** Horizontal padding around the overlay modal. */
|
|
16
|
-
export const OVERLAY_PADDING_X = 4;
|
|
17
|
-
|
|
18
|
-
/** Active overlay for interactive commands such as `/model` and `/session`. */
|
|
19
|
-
export interface ActiveOverlay {
|
|
20
|
-
/** The Select component instance. */
|
|
21
|
-
select: SelectInstance;
|
|
22
|
-
/** Title displayed above the Select. */
|
|
23
|
-
title: string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Render the centered overlay modal for an active Select.
|
|
28
|
-
*
|
|
29
|
-
* @param theme - Active UI theme.
|
|
30
|
-
* @param overlay - Overlay title and Select instance to render.
|
|
31
|
-
* @returns The overlay node.
|
|
32
|
-
*/
|
|
33
|
-
export function renderOverlay(theme: Theme, overlay: ActiveOverlay): Node {
|
|
34
|
-
const modalHeight = OVERLAY_MAX_VISIBLE + 3;
|
|
35
|
-
|
|
36
|
-
return VStack(
|
|
37
|
-
{
|
|
38
|
-
height: "100%",
|
|
39
|
-
justifyContent: "center",
|
|
40
|
-
padding: { x: OVERLAY_PADDING_X },
|
|
41
|
-
},
|
|
42
|
-
[
|
|
43
|
-
VStack(
|
|
44
|
-
{
|
|
45
|
-
height: modalHeight,
|
|
46
|
-
bgColor: theme.overlayBg,
|
|
47
|
-
padding: { x: 1 },
|
|
48
|
-
},
|
|
49
|
-
[
|
|
50
|
-
Text(overlay.title, {
|
|
51
|
-
bold: true,
|
|
52
|
-
fgColor: theme.accentText,
|
|
53
|
-
}),
|
|
54
|
-
overlay.select(),
|
|
55
|
-
],
|
|
56
|
-
),
|
|
57
|
-
],
|
|
58
|
-
);
|
|
59
|
-
}
|
package/src/ui/runtime.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime-only helper bindings for the terminal UI.
|
|
3
|
-
*
|
|
4
|
-
* @module
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { AppState } from "../index.ts";
|
|
8
|
-
import {
|
|
9
|
-
appendConversationMessage,
|
|
10
|
-
appendMessage,
|
|
11
|
-
createUiMessage,
|
|
12
|
-
createUiTodoMessage,
|
|
13
|
-
type UiInfoFormat,
|
|
14
|
-
type UiMessage,
|
|
15
|
-
} from "../session.ts";
|
|
16
|
-
import type { TodoItem } from "../tools.ts";
|
|
17
|
-
|
|
18
|
-
/** Render scheduling priorities used by the terminal UI. */
|
|
19
|
-
export type UiRenderPriority = "immediate" | "normal" | "stream" | "animation";
|
|
20
|
-
|
|
21
|
-
interface UiRuntimeHooks {
|
|
22
|
-
/** Schedule a UI render. */
|
|
23
|
-
requestRender: (priority?: UiRenderPriority) => void;
|
|
24
|
-
/** Re-enable stick-to-bottom behavior for the conversation log. */
|
|
25
|
-
scrollConversationToBottom: () => void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface UiRuntimeHelpers {
|
|
29
|
-
/** Append a UI-only info message to the conversation log. */
|
|
30
|
-
appendInfoMessage: (
|
|
31
|
-
text: string,
|
|
32
|
-
state: AppState,
|
|
33
|
-
format?: UiInfoFormat,
|
|
34
|
-
) => void;
|
|
35
|
-
/** Append a UI-only todo snapshot to the conversation log. */
|
|
36
|
-
appendTodoMessage: (todos: readonly TodoItem[], state: AppState) => void;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function appendUiMessage(
|
|
40
|
-
message: UiMessage,
|
|
41
|
-
state: AppState,
|
|
42
|
-
runtime: UiRuntimeHooks,
|
|
43
|
-
): void {
|
|
44
|
-
if (state.session) {
|
|
45
|
-
appendMessage(state.db, state.session.id, message);
|
|
46
|
-
}
|
|
47
|
-
appendConversationMessage(state, message);
|
|
48
|
-
runtime.scrollConversationToBottom();
|
|
49
|
-
runtime.requestRender("normal");
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Create runtime-bound helpers that append UI-only conversation messages.
|
|
54
|
-
*
|
|
55
|
-
* @param runtime - Render and scroll hooks owned by `ui.ts`.
|
|
56
|
-
* @returns Helpers shared by the command and agent controllers.
|
|
57
|
-
*/
|
|
58
|
-
export function createUiRuntimeHelpers(
|
|
59
|
-
runtime: UiRuntimeHooks,
|
|
60
|
-
): UiRuntimeHelpers {
|
|
61
|
-
return {
|
|
62
|
-
appendInfoMessage: (text, state, format) => {
|
|
63
|
-
appendUiMessage(createUiMessage(text, format), state, runtime);
|
|
64
|
-
},
|
|
65
|
-
appendTodoMessage: (todos, state) => {
|
|
66
|
-
appendUiMessage(createUiTodoMessage(todos), state, runtime);
|
|
67
|
-
},
|
|
68
|
-
};
|
|
69
|
-
}
|