mergestorm 0.3.9 → 0.3.15
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 +67 -24
- package/dist/api.d.ts +106 -40
- package/dist/api.js +245 -9
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +5 -0
- package/dist/commands/branches.d.ts +6 -2
- package/dist/commands/branches.js +29 -14
- package/dist/commands/browse.d.ts +39 -0
- package/dist/commands/browse.js +259 -0
- package/dist/commands/credits.js +30 -12
- package/dist/commands/help.d.ts +5 -0
- package/dist/commands/help.js +93 -0
- package/dist/commands/jobs.d.ts +11 -2
- package/dist/commands/jobs.js +64 -15
- package/dist/commands/login.js +32 -10
- package/dist/commands/logout.js +2 -1
- package/dist/commands/pr.d.ts +22 -0
- package/dist/commands/pr.js +127 -0
- package/dist/commands/queue.d.ts +12 -0
- package/dist/commands/queue.js +160 -0
- package/dist/commands/registry.d.ts +1 -1
- package/dist/commands/registry.js +115 -33
- package/dist/commands/review-client.d.ts +106 -0
- package/dist/commands/review-client.js +399 -0
- package/dist/commands/review.d.ts +36 -6
- package/dist/commands/review.js +395 -145
- package/dist/commands/settings.d.ts +17 -0
- package/dist/commands/settings.js +89 -0
- package/dist/commands/skill.d.ts +20 -0
- package/dist/commands/skill.js +145 -0
- package/dist/commands/stack.d.ts +36 -2
- package/dist/commands/stack.js +341 -113
- package/dist/commands/status.d.ts +18 -2
- package/dist/commands/status.js +236 -21
- package/dist/commands/whoami.d.ts +3 -1
- package/dist/commands/whoami.js +21 -7
- package/dist/errors.d.ts +18 -2
- package/dist/errors.js +22 -1
- package/dist/git-stack.d.ts +2 -0
- package/dist/git-stack.js +4 -0
- package/dist/git.d.ts +12 -1
- package/dist/git.js +52 -7
- package/dist/shell.d.ts +1 -1
- package/dist/shell.js +39 -28
- package/dist/stack-dto.d.ts +266 -0
- package/dist/stack-dto.js +7 -0
- package/dist/submit-pr-base.d.ts +31 -0
- package/dist/submit-pr-base.js +69 -0
- package/dist/ui/ansi.d.ts +2 -0
- package/dist/ui/ansi.js +2 -0
- package/dist/ui/banner.d.ts +34 -2
- package/dist/ui/banner.js +170 -79
- package/dist/ui/box.d.ts +13 -9
- package/dist/ui/box.js +28 -44
- package/dist/ui/findings.d.ts +4 -1
- package/dist/ui/findings.js +14 -7
- package/dist/ui/job-envelope.d.ts +68 -0
- package/dist/ui/job-envelope.js +35 -0
- package/dist/ui/line-tabs.d.ts +27 -0
- package/dist/ui/line-tabs.js +170 -0
- package/dist/ui/logo.d.ts +10 -2
- package/dist/ui/logo.js +13 -9
- package/dist/ui/present.d.ts +10 -0
- package/dist/ui/present.js +13 -0
- package/dist/ui/prompt.d.ts +148 -0
- package/dist/ui/prompt.js +433 -71
- package/dist/ui/select.js +1 -1
- package/dist/ui/tabs.d.ts +125 -0
- package/dist/ui/tabs.js +480 -0
- package/dist/ui/usage.d.ts +29 -1
- package/dist/ui/usage.js +71 -2
- package/dist/ui/width.d.ts +26 -0
- package/dist/ui/width.js +262 -0
- package/package.json +6 -1
- package/skill/SKILL.md +98 -0
- package/skill/mergestorm-pr-loop/SKILL.md +76 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import * as readline from "node:readline";
|
|
2
|
+
import { stdin, stdout } from "node:process";
|
|
3
|
+
import { ansi } from "./ansi.js";
|
|
4
|
+
import { frameWidth, preferBoxedUi, roundedBox, terminalColumns, } from "./box.js";
|
|
5
|
+
import { padVisible, visibleWidth } from "./width.js";
|
|
6
|
+
import { tabsBodyRows, tabsHintLine } from "./tabs.js";
|
|
7
|
+
export function initialLineTabsState(tabCount, start = 0) {
|
|
8
|
+
const n = Math.max(1, tabCount);
|
|
9
|
+
return {
|
|
10
|
+
index: Math.max(0, Math.min(start, n - 1)),
|
|
11
|
+
scroll: Array.from({ length: n }, () => 0),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function reduceLineTabsKey(state, key, tabCount, maxScroll) {
|
|
15
|
+
if (key === "q" || key === "escape")
|
|
16
|
+
return { kind: "close" };
|
|
17
|
+
const last = Math.max(0, tabCount - 1);
|
|
18
|
+
if (key === "left") {
|
|
19
|
+
return { kind: "state", state: { ...state, index: Math.max(0, state.index - 1) } };
|
|
20
|
+
}
|
|
21
|
+
if (key === "right") {
|
|
22
|
+
return { kind: "state", state: { ...state, index: Math.min(last, state.index + 1) } };
|
|
23
|
+
}
|
|
24
|
+
const scroll = state.scroll.slice();
|
|
25
|
+
const at = state.index;
|
|
26
|
+
if (key === "up")
|
|
27
|
+
scroll[at] = Math.max(0, (scroll[at] ?? 0) - 1);
|
|
28
|
+
if (key === "down")
|
|
29
|
+
scroll[at] = Math.min(maxScroll, (scroll[at] ?? 0) + 1);
|
|
30
|
+
return { kind: "state", state: { ...state, scroll } };
|
|
31
|
+
}
|
|
32
|
+
function titleOf(tabs, index, color) {
|
|
33
|
+
const parts = tabs.map((tab, i) => {
|
|
34
|
+
if (!color)
|
|
35
|
+
return i === index ? `[${tab.label}]` : ` ${tab.label} `;
|
|
36
|
+
return i === index ? ansi.boldBrightGreen(tab.label) : ansi.dim(tab.label);
|
|
37
|
+
});
|
|
38
|
+
return parts.join(color ? ansi.dim(" · ") : " · ");
|
|
39
|
+
}
|
|
40
|
+
export function layoutLineTabs(tabs, state, columns, rows, color = false) {
|
|
41
|
+
const boxed = preferBoxedUi(columns);
|
|
42
|
+
const cols = terminalColumns(80, columns);
|
|
43
|
+
const outer = frameWidth(80, columns);
|
|
44
|
+
const contentCells = boxed ? Math.max(20, outer - 4) : Math.max(20, cols - 1);
|
|
45
|
+
const bodyRows = tabsBodyRows(rows);
|
|
46
|
+
const tab = tabs[state.index] ?? tabs[0];
|
|
47
|
+
const content = tab ? tab.lines : [];
|
|
48
|
+
const start = Math.min(Math.max(0, state.scroll[state.index] ?? 0), Math.max(0, content.length - bodyRows));
|
|
49
|
+
const clip = (line) => visibleWidth(line) > contentCells ? padVisible(line, contentCells) : line;
|
|
50
|
+
const body = content.slice(start, start + bodyRows).map(clip);
|
|
51
|
+
while (body.length < bodyRows)
|
|
52
|
+
body.push("");
|
|
53
|
+
const hint = tabsHintLine("←→ tabs · ↑↓ scroll · esc close", undefined, cols, color);
|
|
54
|
+
const title = titleOf(tabs, state.index, color);
|
|
55
|
+
if (!boxed) {
|
|
56
|
+
const plainTitle = visibleWidth(title) > cols - 1 ? padVisible(title, cols - 1) : title;
|
|
57
|
+
return { lines: [plainTitle, ...body, hint], bodyRows, contentRows: content.length };
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
lines: [...roundedBox(body, { title, width: outer, padding: 1 }), hint],
|
|
61
|
+
bodyRows,
|
|
62
|
+
contentRows: content.length,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const up = (n) => `\u001b[${n}A`;
|
|
66
|
+
const clearDown = "\u001b[0J";
|
|
67
|
+
const homeClear = `\u001b[H${clearDown}`;
|
|
68
|
+
/** Hold-to-read panel. TTY: tabbed frame. Pipe: print the lines. */
|
|
69
|
+
export async function showLinePanel(label, lines) {
|
|
70
|
+
const input = stdin;
|
|
71
|
+
const output = stdout;
|
|
72
|
+
if (!input.isTTY || !output.isTTY) {
|
|
73
|
+
for (const line of lines)
|
|
74
|
+
console.log(line);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const id = label.toLowerCase().replace(/[^a-z0-9]+/g, "-") || "page";
|
|
78
|
+
await runLineTabsBrowser({ tabs: [{ id, label, lines }] });
|
|
79
|
+
}
|
|
80
|
+
export async function runLineTabsBrowser(opts) {
|
|
81
|
+
const input = opts.input ?? stdin;
|
|
82
|
+
const output = opts.output ?? stdout;
|
|
83
|
+
const tabs = opts.tabs;
|
|
84
|
+
const color = opts.color ?? ansi.enabled;
|
|
85
|
+
if (!input.isTTY || !output.isTTY) {
|
|
86
|
+
for (const tab of tabs) {
|
|
87
|
+
console.log(` ${tab.label}`);
|
|
88
|
+
for (const line of tab.lines)
|
|
89
|
+
console.log(line);
|
|
90
|
+
console.log("");
|
|
91
|
+
}
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
return new Promise((resolve) => {
|
|
95
|
+
let state = initialLineTabsState(tabs.length, opts.initial ?? 0);
|
|
96
|
+
let lastLayout = null;
|
|
97
|
+
let linesDrawn = 0;
|
|
98
|
+
let finished = false;
|
|
99
|
+
const wasRaw = Boolean(input.isRaw);
|
|
100
|
+
readline.emitKeypressEvents(input);
|
|
101
|
+
input.setRawMode(true);
|
|
102
|
+
input.resume();
|
|
103
|
+
function render(prefix) {
|
|
104
|
+
if (prefix)
|
|
105
|
+
output.write(prefix);
|
|
106
|
+
else if (linesDrawn > 0)
|
|
107
|
+
output.write(`${up(linesDrawn)}\r${clearDown}`);
|
|
108
|
+
lastLayout = layoutLineTabs(tabs, state, output.columns, output.rows, color);
|
|
109
|
+
output.write(lastLayout.lines.join("\r\n") + "\r\n");
|
|
110
|
+
linesDrawn = lastLayout.lines.length;
|
|
111
|
+
}
|
|
112
|
+
function finish() {
|
|
113
|
+
if (finished)
|
|
114
|
+
return;
|
|
115
|
+
finished = true;
|
|
116
|
+
input.removeListener("keypress", onKeypress);
|
|
117
|
+
output.removeListener("resize", onResize);
|
|
118
|
+
input.setRawMode(wasRaw);
|
|
119
|
+
input.pause();
|
|
120
|
+
if (linesDrawn > 0)
|
|
121
|
+
output.write(`${up(linesDrawn)}\r${clearDown}`);
|
|
122
|
+
resolve();
|
|
123
|
+
}
|
|
124
|
+
function onResize() {
|
|
125
|
+
if (!finished)
|
|
126
|
+
render(homeClear);
|
|
127
|
+
}
|
|
128
|
+
function onKeypress(str, key) {
|
|
129
|
+
if (finished || !key)
|
|
130
|
+
return;
|
|
131
|
+
if (key.ctrl && key.name === "c") {
|
|
132
|
+
finish();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let name = null;
|
|
136
|
+
if (key.name === "left" || key.name === "right" || key.name === "up" || key.name === "down") {
|
|
137
|
+
name = key.name;
|
|
138
|
+
}
|
|
139
|
+
else if (key.name === "escape")
|
|
140
|
+
name = "escape";
|
|
141
|
+
else if (key.name === "q" || str === "q")
|
|
142
|
+
name = "q";
|
|
143
|
+
if (!name)
|
|
144
|
+
return;
|
|
145
|
+
const maxScroll = lastLayout
|
|
146
|
+
? Math.max(0, lastLayout.contentRows - lastLayout.bodyRows)
|
|
147
|
+
: 0;
|
|
148
|
+
const step = reduceLineTabsKey(state, name, tabs.length, maxScroll);
|
|
149
|
+
if (step.kind === "close") {
|
|
150
|
+
finish();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
state = step.state;
|
|
154
|
+
try {
|
|
155
|
+
render();
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
finish();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
input.on("keypress", onKeypress);
|
|
162
|
+
output.on("resize", onResize);
|
|
163
|
+
try {
|
|
164
|
+
render(homeClear);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
finish();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
package/dist/ui/logo.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Compact two-row storm mark (Fable / 0.3.14 home). Colored by the caller.
|
|
3
|
+
* Block elements (U+2580..259F) are one cell each; no emoji, no gradients.
|
|
4
4
|
*/
|
|
5
|
+
export declare const STORM_MARK: string[];
|
|
6
|
+
/** Widest row of the mark, in cells (every mark glyph is single-cell). */
|
|
7
|
+
export declare const STORM_MARK_WIDTH: number;
|
|
8
|
+
/** @deprecated Alias — the home panel uses {@link STORM_MARK}. */
|
|
5
9
|
export declare const TORNADO_LOGO: string[];
|
|
10
|
+
/** @deprecated Alias. */
|
|
11
|
+
export declare const TORNADO_LOGO_WIDTH: number;
|
|
12
|
+
/** @deprecated Compact home is the full two-row mark. */
|
|
13
|
+
export declare const TORNADO_LOGO_COMPACT: string[];
|
package/dist/ui/logo.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Compact two-row storm mark (Fable / 0.3.14 home). Colored by the caller.
|
|
3
|
+
* Block elements (U+2580..259F) are one cell each; no emoji, no gradients.
|
|
4
4
|
*/
|
|
5
|
-
export const
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
" ▝▜████▛▘",
|
|
9
|
-
" ▜██▛",
|
|
10
|
-
" ██",
|
|
11
|
-
" ▝▘",
|
|
5
|
+
export const STORM_MARK = [
|
|
6
|
+
"▟██▛",
|
|
7
|
+
" ▜▙",
|
|
12
8
|
];
|
|
9
|
+
/** Widest row of the mark, in cells (every mark glyph is single-cell). */
|
|
10
|
+
export const STORM_MARK_WIDTH = Math.max(...STORM_MARK.map((line) => line.length));
|
|
11
|
+
/** @deprecated Alias — the home panel uses {@link STORM_MARK}. */
|
|
12
|
+
export const TORNADO_LOGO = STORM_MARK;
|
|
13
|
+
/** @deprecated Alias. */
|
|
14
|
+
export const TORNADO_LOGO_WIDTH = STORM_MARK_WIDTH;
|
|
15
|
+
/** @deprecated Compact home is the full two-row mark. */
|
|
16
|
+
export const TORNADO_LOGO_COMPACT = STORM_MARK;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The only way a shell command may show a result and return to home.
|
|
3
|
+
* TTY: hold-to-read panel (q). Pipe: print the lines.
|
|
4
|
+
*
|
|
5
|
+
* Do not `console.log` then return from a shell command — the next
|
|
6
|
+
* askLine homes + ED 0 and wipes it. Live waits (login poll, review
|
|
7
|
+
* progress) may still write; the *final* result goes through here
|
|
8
|
+
* or through `openTabsBrowser`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function present(title: string, lines: string[]): Promise<void>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { showLinePanel } from "./line-tabs.js";
|
|
2
|
+
/**
|
|
3
|
+
* The only way a shell command may show a result and return to home.
|
|
4
|
+
* TTY: hold-to-read panel (q). Pipe: print the lines.
|
|
5
|
+
*
|
|
6
|
+
* Do not `console.log` then return from a shell command — the next
|
|
7
|
+
* askLine homes + ED 0 and wipes it. Live waits (login poll, review
|
|
8
|
+
* progress) may still write; the *final* result goes through here
|
|
9
|
+
* or through `openTabsBrowser`.
|
|
10
|
+
*/
|
|
11
|
+
export async function present(title, lines) {
|
|
12
|
+
await showLinePanel(title, lines);
|
|
13
|
+
}
|
package/dist/ui/prompt.d.ts
CHANGED
|
@@ -1,16 +1,164 @@
|
|
|
1
1
|
export interface CommandSpec {
|
|
2
2
|
name: string;
|
|
3
|
+
aliases?: string[];
|
|
3
4
|
summary: string;
|
|
4
5
|
}
|
|
5
6
|
export interface AskLineOptions {
|
|
6
7
|
prompt?: string;
|
|
7
8
|
history?: string[];
|
|
8
9
|
commands?: CommandSpec[];
|
|
10
|
+
/** Terminal rows; defaults to output.rows. Caps the slash viewport. */
|
|
11
|
+
rows?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Terminal columns; defaults to output.columns, re-read on every render so
|
|
14
|
+
* a resize mid-prompt reflows the frame instead of smearing it.
|
|
15
|
+
*/
|
|
16
|
+
columns?: number;
|
|
17
|
+
/** Streams (defaults: process.stdin / process.stdout). Injectable for tests. */
|
|
18
|
+
input?: NodeJS.ReadStream;
|
|
19
|
+
output?: NodeJS.WriteStream;
|
|
20
|
+
/**
|
|
21
|
+
* Welcome-panel rows sitting directly above this prompt, reflowed per
|
|
22
|
+
* width. Painted on every frame from the pane origin (`ESC[H`) so a
|
|
23
|
+
* resize cannot desync CUU counts. Pass `true` for compact, `"mini"` /
|
|
24
|
+
* `"nano"` for a short pane that still shows the mark.
|
|
25
|
+
*/
|
|
26
|
+
header?: (columns?: number, variant?: boolean | "mini" | "nano") => string[];
|
|
9
27
|
}
|
|
10
28
|
/** Thrown when the user asks to close the prompt (Ctrl+D empty, or Ctrl+C twice). */
|
|
11
29
|
export declare class PromptClosedError extends Error {
|
|
12
30
|
constructor();
|
|
13
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Redraw prefix from the parked content-row cursor.
|
|
34
|
+
* CUU keeps the column, so CR must come before ED 0 or leftover
|
|
35
|
+
* prompt cells survive (`merg` + `mergestorm` -> `mergmmmmmergestorm`).
|
|
36
|
+
*/
|
|
37
|
+
export declare function promptRedrawPrefix(inputRow: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* From the content row, move to the first row after the input chrome
|
|
40
|
+
* and erase the slash overlay. Boxed chrome is top + content + bottom
|
|
41
|
+
* (`down(2)`). Plain chrome is the content line (`down(1)`).
|
|
42
|
+
*/
|
|
43
|
+
export declare function promptClearOverlay(boxed: boolean): string;
|
|
44
|
+
/**
|
|
45
|
+
* Cursor home + ED 0: clears the whole screen from row 1 and leaves the
|
|
46
|
+
* cursor there. Used to redraw the welcome panel on resize, where the old
|
|
47
|
+
* frame's row count is unknowable after the terminal rewraps.
|
|
48
|
+
*/
|
|
49
|
+
export declare const HEADER_REDRAW_PREFIX = "\u001B[H\u001B[0J";
|
|
50
|
+
export declare const DROPDOWN_MAX_COMMAND_ROWS = 8;
|
|
51
|
+
export declare function formatSlashLabel(spec: CommandSpec): string;
|
|
52
|
+
export declare function specMatchesTerm(spec: CommandSpec, term: string): boolean;
|
|
53
|
+
export declare function slashToken(buffer: string): string | null;
|
|
54
|
+
export declare function matchSlashCommands(buffer: string, specs: CommandSpec[], dismissed?: boolean): CommandSpec[];
|
|
55
|
+
export declare function isExactSlashCommand(buffer: string, specs: CommandSpec[]): boolean;
|
|
56
|
+
export declare function dropdownMaxRows(terminalRows: number | undefined, inputRow: number): number;
|
|
57
|
+
export declare function dropdownViewport(count: number, index: number, maxRows: number): {
|
|
58
|
+
start: number;
|
|
59
|
+
moreAbove: number;
|
|
60
|
+
moreBelow: number;
|
|
61
|
+
};
|
|
62
|
+
export type OverlayRow = {
|
|
63
|
+
text: string;
|
|
64
|
+
selected?: boolean;
|
|
65
|
+
};
|
|
66
|
+
/** Overlay rows (no invert). At most `maxRows` commands plus edge markers. */
|
|
67
|
+
/** Prompt label + input chrome + footer hint. */
|
|
68
|
+
export declare function promptChromeRows(boxed: boolean, hasPrompt: boolean, hasHint?: boolean): number;
|
|
69
|
+
/**
|
|
70
|
+
* Rows available above the input bar for the slash list. Idle home
|
|
71
|
+
* passes 0 — do not pad a blank gap between header and prompt.
|
|
72
|
+
*/
|
|
73
|
+
export declare function overlayReserve(terminalRows: number | undefined, headerRows: number, boxed: boolean, hasPrompt: boolean): number;
|
|
74
|
+
/** Pad so the slash list sits just above the input bar (Claude-style). */
|
|
75
|
+
export declare function padOverlayRows(rows: OverlayRow[], reserve: number): OverlayRow[];
|
|
76
|
+
export interface HomeFrameFit {
|
|
77
|
+
header: string[];
|
|
78
|
+
reserve: number;
|
|
79
|
+
boxed: boolean;
|
|
80
|
+
hasPrompt: boolean;
|
|
81
|
+
hasHint: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Pick a header density and input chrome that fit the pane.
|
|
85
|
+
* Never drops the mark when a mini/nano header is provided — a short
|
|
86
|
+
* pane unboxes the input instead of painting an overflowing frame.
|
|
87
|
+
* `dockBottom` uses leftover rows so the prompt sits on the last lines.
|
|
88
|
+
*/
|
|
89
|
+
export declare function fitHomeFrame(params: {
|
|
90
|
+
fullHeader: string[];
|
|
91
|
+
compactHeader: string[];
|
|
92
|
+
miniHeader?: string[];
|
|
93
|
+
nanoHeader?: string[];
|
|
94
|
+
terminalRows: number | undefined;
|
|
95
|
+
boxed: boolean;
|
|
96
|
+
hasPrompt: boolean;
|
|
97
|
+
dropdownCount: number;
|
|
98
|
+
dockBottom?: boolean;
|
|
99
|
+
}): HomeFrameFit;
|
|
100
|
+
export declare function dropdownOverlayRows(dropdown: CommandSpec[], index: number, maxRows: number): OverlayRow[];
|
|
101
|
+
/** Input prefix glyph (U+203A), kept as an escape so editors can't mangle it. */
|
|
102
|
+
export declare const INPUT_PREFIX_GLYPH = "\u203A";
|
|
103
|
+
/** Cells the `> ` prefix occupies before the buffer. */
|
|
104
|
+
export declare const INPUT_PREFIX_CELLS = 2;
|
|
105
|
+
/** Padding inside the boxed input frame. */
|
|
106
|
+
export declare const INPUT_BOX_PADDING = 1;
|
|
107
|
+
export interface InputWindow {
|
|
108
|
+
/** First buffer cell shown. */
|
|
109
|
+
start: number;
|
|
110
|
+
/** The visible slice of the buffer (ANSI-free, at most `cells` wide). */
|
|
111
|
+
text: string;
|
|
112
|
+
/** Cursor column within the window, 0-based. */
|
|
113
|
+
cursorCol: number;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Horizontal scroll for a one-row editor: pick the `cells`-wide window of
|
|
117
|
+
* `buffer` that keeps the cursor on screen, moving the previous window only
|
|
118
|
+
* when the cursor leaves it. Never wraps onto a second physical row.
|
|
119
|
+
*/
|
|
120
|
+
export declare function inputWindow(buffer: string, cursor: number, cells: number, prevStart?: number): InputWindow;
|
|
121
|
+
/** Cells available to the buffer (after the prefix) on a content row. */
|
|
122
|
+
export declare function inputCells(columns: number | undefined, boxedOverride?: boolean): {
|
|
123
|
+
boxed: boolean;
|
|
124
|
+
cells: number;
|
|
125
|
+
};
|
|
126
|
+
export interface PromptFrameInput {
|
|
127
|
+
prompt?: string;
|
|
128
|
+
buffer: string;
|
|
129
|
+
cursor: number;
|
|
130
|
+
/** Terminal columns at draw time; undefined = unknown (boxed at 80). */
|
|
131
|
+
columns?: number;
|
|
132
|
+
/** Previous scroll origin, so left/right only move the window at the edges. */
|
|
133
|
+
scrollStart?: number;
|
|
134
|
+
overlay?: OverlayRow[];
|
|
135
|
+
/**
|
|
136
|
+
* When set, the overlay is padded to this many rows and drawn **above**
|
|
137
|
+
* the input bar. Height stays constant so `/` cannot scroll the header.
|
|
138
|
+
*/
|
|
139
|
+
overlayReserve?: number;
|
|
140
|
+
/** Trailing footer row (empty or the Ctrl+C hint). */
|
|
141
|
+
hint?: string;
|
|
142
|
+
/** Welcome-panel rows painted with this frame (idle home screen). */
|
|
143
|
+
header?: string[];
|
|
144
|
+
/** Force boxed / plain input. Default follows {@link preferBoxedUi}. */
|
|
145
|
+
boxed?: boolean;
|
|
146
|
+
}
|
|
147
|
+
export interface PromptFrame {
|
|
148
|
+
rows: string[];
|
|
149
|
+
boxed: boolean;
|
|
150
|
+
/** Row (0-based) of the content line within `rows`. */
|
|
151
|
+
inputRow: number;
|
|
152
|
+
/** 1-based column to park the cursor on the content row. */
|
|
153
|
+
targetCol: number;
|
|
154
|
+
scrollStart: number;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Everything one prompt redraw writes, computed from the current terminal
|
|
158
|
+
* width. Every row fits in `columns - 1` cells so nothing wraps and the
|
|
159
|
+
* CUU count on the next redraw stays honest.
|
|
160
|
+
*/
|
|
161
|
+
export declare function buildPromptFrame(params: PromptFrameInput): PromptFrame;
|
|
14
162
|
/**
|
|
15
163
|
* Hand-rolled raw-mode line editor: bordered input box, history, and a live
|
|
16
164
|
* slash-command dropdown. Falls back to plain readline on a non-TTY stream.
|