toolcraft 0.0.163 → 0.0.164
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/composition.json +2 -2
- package/dist/composition.json +2 -2
- package/node_modules/tiny-stdio-mcp-server/dist/composition.json +1 -1
- package/node_modules/toolcraft-design/dist/prompts/interactive/core.d.ts +3 -1
- package/node_modules/toolcraft-design/dist/prompts/interactive/core.js +51 -24
- package/node_modules/toolcraft-design/dist/prompts/interactive/multiselect.js +3 -2
- package/node_modules/toolcraft-design/dist/prompts/interactive/pagination.js +21 -27
- package/node_modules/toolcraft-design/dist/prompts/interactive/password.js +11 -8
- package/node_modules/toolcraft-design/dist/prompts/interactive/select.js +3 -2
- package/node_modules/toolcraft-design/dist/prompts/interactive/text.js +12 -9
- package/node_modules/toolcraft-schema/package.json +1 -1
- package/package.json +2 -2
package/composition.json
CHANGED
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.164",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.164",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
package/dist/composition.json
CHANGED
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "toolcraft",
|
|
116
|
-
"version": "0.0.
|
|
116
|
+
"version": "0.0.164",
|
|
117
117
|
"license": "MIT"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
125
|
"name": "toolcraft-schema",
|
|
126
|
-
"version": "0.0.
|
|
126
|
+
"version": "0.0.164",
|
|
127
127
|
"license": "MIT"
|
|
128
128
|
},
|
|
129
129
|
{
|
|
@@ -24,6 +24,8 @@ export interface PromptOptions<Value> {
|
|
|
24
24
|
}
|
|
25
25
|
type InputStream = NodeJS.ReadableStream & {
|
|
26
26
|
isTTY?: boolean;
|
|
27
|
+
destroyed?: boolean;
|
|
28
|
+
readableEnded?: boolean;
|
|
27
29
|
setRawMode?: (enabled: boolean) => void;
|
|
28
30
|
unpipe?: () => void;
|
|
29
31
|
};
|
|
@@ -41,7 +43,6 @@ export declare class Prompt<Value> extends EventEmitter {
|
|
|
41
43
|
private readonly trackValue;
|
|
42
44
|
private previousFrame;
|
|
43
45
|
private readlineInterface?;
|
|
44
|
-
private abortListener?;
|
|
45
46
|
private closed;
|
|
46
47
|
constructor(opts: PromptOptions<Value>, trackValue?: boolean);
|
|
47
48
|
get cursor(): number;
|
|
@@ -52,6 +53,7 @@ export declare class Prompt<Value> extends EventEmitter {
|
|
|
52
53
|
protected setError(message: string): void;
|
|
53
54
|
protected setUserInput(value: string): void;
|
|
54
55
|
protected clearUserInput(): void;
|
|
56
|
+
private readonly onCancel;
|
|
55
57
|
private readonly onKeypress;
|
|
56
58
|
private updateTrackedInput;
|
|
57
59
|
protected readonly render: () => void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import * as readline from "node:readline";
|
|
3
|
+
import { graphemes } from "../../dashboard/terminal-width.js";
|
|
3
4
|
import { CANCEL } from "./cancel-symbol.js";
|
|
4
5
|
import { mapKey } from "./keys.js";
|
|
5
6
|
import { wrapFrame } from "./wrap.js";
|
|
@@ -50,7 +51,6 @@ export class Prompt extends EventEmitter {
|
|
|
50
51
|
trackValue;
|
|
51
52
|
previousFrame = "";
|
|
52
53
|
readlineInterface;
|
|
53
|
-
abortListener;
|
|
54
54
|
closed = false;
|
|
55
55
|
constructor(opts, trackValue = true) {
|
|
56
56
|
super();
|
|
@@ -75,18 +75,16 @@ export class Prompt extends EventEmitter {
|
|
|
75
75
|
if (this.input.isTTY !== true) {
|
|
76
76
|
return this.promptNonTty();
|
|
77
77
|
}
|
|
78
|
+
if (this.input.destroyed || this.input.readableEnded) {
|
|
79
|
+
this.state = "cancel";
|
|
80
|
+
return Promise.resolve(CANCEL);
|
|
81
|
+
}
|
|
78
82
|
return new Promise((resolve) => {
|
|
79
83
|
const onSubmit = (value) => resolve(value);
|
|
80
84
|
const onCancel = () => resolve(CANCEL);
|
|
81
85
|
this.once("submit", onSubmit);
|
|
82
86
|
this.once("cancel", onCancel);
|
|
83
|
-
this.
|
|
84
|
-
this.state = "cancel";
|
|
85
|
-
this.emit("finalize");
|
|
86
|
-
this.render();
|
|
87
|
-
this.close();
|
|
88
|
-
};
|
|
89
|
-
this.signal?.addEventListener("abort", this.abortListener, { once: true });
|
|
87
|
+
this.signal?.addEventListener("abort", this.onCancel, { once: true });
|
|
90
88
|
this.readlineInterface = readline.createInterface({
|
|
91
89
|
input: this.input,
|
|
92
90
|
output: undefined,
|
|
@@ -95,6 +93,8 @@ export class Prompt extends EventEmitter {
|
|
|
95
93
|
escapeCodeTimeout: 50,
|
|
96
94
|
terminal: true
|
|
97
95
|
});
|
|
96
|
+
this.readlineInterface.once("close", this.onCancel);
|
|
97
|
+
this.input.once("close", this.onCancel);
|
|
98
98
|
readline.emitKeypressEvents(this.input, this.readlineInterface);
|
|
99
99
|
this.readlineInterface.prompt();
|
|
100
100
|
this.input.on("keypress", this.onKeypress);
|
|
@@ -134,6 +134,15 @@ export class Prompt extends EventEmitter {
|
|
|
134
134
|
setUserInput(value) {
|
|
135
135
|
this.userInput = value;
|
|
136
136
|
this._cursor = Math.min(this._cursor, this.userInput.length);
|
|
137
|
+
if (this.trackValue) {
|
|
138
|
+
let boundary = 0;
|
|
139
|
+
for (const segment of graphemes(value)) {
|
|
140
|
+
if (boundary >= this._cursor)
|
|
141
|
+
break;
|
|
142
|
+
boundary += segment.length;
|
|
143
|
+
}
|
|
144
|
+
this._cursor = boundary;
|
|
145
|
+
}
|
|
137
146
|
this.emit("userInput", this.userInput);
|
|
138
147
|
}
|
|
139
148
|
clearUserInput() {
|
|
@@ -141,7 +150,19 @@ export class Prompt extends EventEmitter {
|
|
|
141
150
|
this._cursor = 0;
|
|
142
151
|
this.emit("userInput", this.userInput);
|
|
143
152
|
}
|
|
153
|
+
onCancel = () => {
|
|
154
|
+
if (this.closed || this.state === "submit" || this.state === "cancel") {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.state = "cancel";
|
|
158
|
+
this.emit("finalize");
|
|
159
|
+
this.render();
|
|
160
|
+
this.close();
|
|
161
|
+
};
|
|
144
162
|
onKeypress = (char, key = {}) => {
|
|
163
|
+
if (this.closed) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
145
166
|
let action = mapKey(key.name, char);
|
|
146
167
|
if (this.trackValue && char && char >= " " && key.name !== "return" && key.name !== "enter" && key.name !== "escape") {
|
|
147
168
|
action = undefined;
|
|
@@ -187,6 +208,14 @@ export class Prompt extends EventEmitter {
|
|
|
187
208
|
}
|
|
188
209
|
};
|
|
189
210
|
updateTrackedInput(char, key, action) {
|
|
211
|
+
if (key.name === "home") {
|
|
212
|
+
this._cursor = 0;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (key.name === "end") {
|
|
216
|
+
this._cursor = this.userInput.length;
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
190
219
|
if (key.ctrl) {
|
|
191
220
|
if (key.name === "a") {
|
|
192
221
|
this._cursor = 0;
|
|
@@ -197,23 +226,24 @@ export class Prompt extends EventEmitter {
|
|
|
197
226
|
return;
|
|
198
227
|
}
|
|
199
228
|
if (key.name === "u") {
|
|
200
|
-
|
|
229
|
+
const remaining = this.userInput.slice(this._cursor);
|
|
201
230
|
this._cursor = 0;
|
|
202
|
-
this.
|
|
231
|
+
this.setUserInput(remaining);
|
|
203
232
|
return;
|
|
204
233
|
}
|
|
205
234
|
if (key.name === "k") {
|
|
206
|
-
this.
|
|
207
|
-
this.emit("userInput", this.userInput);
|
|
235
|
+
this.setUserInput(this.userInput.slice(0, this._cursor));
|
|
208
236
|
return;
|
|
209
237
|
}
|
|
210
238
|
}
|
|
239
|
+
const before = this.userInput.slice(0, this._cursor);
|
|
240
|
+
const after = this.userInput.slice(this._cursor);
|
|
211
241
|
if (action === "left") {
|
|
212
|
-
this._cursor
|
|
242
|
+
this._cursor -= graphemes(before).at(-1)?.length ?? 0;
|
|
213
243
|
return;
|
|
214
244
|
}
|
|
215
245
|
if (action === "right") {
|
|
216
|
-
this._cursor
|
|
246
|
+
this._cursor += graphemes(after)[0]?.length ?? 0;
|
|
217
247
|
return;
|
|
218
248
|
}
|
|
219
249
|
if (action === "cancel" || action === "up" || action === "down" || action === "space") {
|
|
@@ -221,25 +251,22 @@ export class Prompt extends EventEmitter {
|
|
|
221
251
|
}
|
|
222
252
|
if (key.name === "backspace" || char === "\b" || char === "\x7f") {
|
|
223
253
|
if (this._cursor > 0) {
|
|
224
|
-
this.
|
|
225
|
-
this._cursor
|
|
226
|
-
this.emit("userInput", this.userInput);
|
|
254
|
+
this._cursor -= graphemes(before).at(-1)?.length ?? 0;
|
|
255
|
+
this.setUserInput(`${before.slice(0, this._cursor)}${after}`);
|
|
227
256
|
}
|
|
228
257
|
return;
|
|
229
258
|
}
|
|
230
259
|
if (key.name === "delete") {
|
|
231
260
|
if (this._cursor < this.userInput.length) {
|
|
232
|
-
this.
|
|
233
|
-
this.emit("userInput", this.userInput);
|
|
261
|
+
this.setUserInput(`${before}${after.slice(graphemes(after)[0]?.length ?? 0)}`);
|
|
234
262
|
}
|
|
235
263
|
return;
|
|
236
264
|
}
|
|
237
265
|
if (!char || char < " " || key.ctrl) {
|
|
238
266
|
return;
|
|
239
267
|
}
|
|
240
|
-
this.userInput = `${this.userInput.slice(0, this._cursor)}${char}${this.userInput.slice(this._cursor)}`;
|
|
241
268
|
this._cursor += char.length;
|
|
242
|
-
this.
|
|
269
|
+
this.setUserInput(`${before}${char}${after}`);
|
|
243
270
|
}
|
|
244
271
|
render = () => {
|
|
245
272
|
if (this.closed) {
|
|
@@ -267,10 +294,10 @@ export class Prompt extends EventEmitter {
|
|
|
267
294
|
}
|
|
268
295
|
this.closed = true;
|
|
269
296
|
this.input.removeListener("keypress", this.onKeypress);
|
|
297
|
+
this.input.removeListener("close", this.onCancel);
|
|
270
298
|
this.output.removeListener("resize", this.render);
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
}
|
|
299
|
+
this.signal?.removeEventListener("abort", this.onCancel);
|
|
300
|
+
this.readlineInterface?.removeListener("close", this.onCancel);
|
|
274
301
|
this.output.write(`${cursor.show}\n`);
|
|
275
302
|
if (!process.platform.startsWith("win") && this.input.setRawMode) {
|
|
276
303
|
this.input.setRawMode(false);
|
|
@@ -2,6 +2,7 @@ import { color } from "../../components/color.js";
|
|
|
2
2
|
import { GLYPHS, symbol } from "./glyphs.js";
|
|
3
3
|
import { Prompt } from "./core.js";
|
|
4
4
|
import { limitOptions } from "./pagination.js";
|
|
5
|
+
import { wrapTextWithPrefix } from "./wrap.js";
|
|
5
6
|
import { findNonDisabled } from "./select.js";
|
|
6
7
|
class MultiselectPrompt extends Prompt {
|
|
7
8
|
options;
|
|
@@ -111,7 +112,7 @@ function renderMultiselectPrompt(prompt, opts) {
|
|
|
111
112
|
.map((option) => prompt.state === "submit" ? color.dim(option.label) : color.dim.strikethrough(option.label))
|
|
112
113
|
.join(", ");
|
|
113
114
|
const end = prompt.state === "submit" ? color.green(GLYPHS.barEnd) : color.red(GLYPHS.barEnd);
|
|
114
|
-
return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${color.gray(GLYPHS.bar)}
|
|
115
|
+
return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${wrapTextWithPrefix(opts.output ?? process.stdout, labels, `${color.gray(GLYPHS.bar)} `)}\n${end}`;
|
|
115
116
|
}
|
|
116
117
|
const lines = limitOptions({
|
|
117
118
|
cursor: prompt.cursor,
|
|
@@ -120,7 +121,7 @@ function renderMultiselectPrompt(prompt, opts) {
|
|
|
120
121
|
maxItems: opts.maxItems,
|
|
121
122
|
columnPadding: 3,
|
|
122
123
|
style: (option, active) => renderOption(option, prompt.value, active, false, false)
|
|
123
|
-
}).
|
|
124
|
+
}).flatMap((line) => line.split("\n").map((physicalLine) => `${prompt.state === "error" ? color.yellow(GLYPHS.bar) : color.cyan(GLYPHS.bar)} ${physicalLine}`));
|
|
124
125
|
const body = [`${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}`, ...lines];
|
|
125
126
|
if (prompt.state === "error") {
|
|
126
127
|
body.push(`${color.yellow(GLYPHS.barEnd)} ${color.yellow(prompt.error)}`);
|
|
@@ -5,24 +5,6 @@ import { wrapAnsi } from "fast-wrap-ansi";
|
|
|
5
5
|
function countLines(values) {
|
|
6
6
|
return values.reduce((sum, value) => sum + value.split("\n").length, 0);
|
|
7
7
|
}
|
|
8
|
-
function trimToRows(values, cursorOffset, rows, hasTop, hasBottom) {
|
|
9
|
-
const output = [...values];
|
|
10
|
-
while (countLines(output) > rows && output.length > 1) {
|
|
11
|
-
const removeFromTop = hasTop && cursorOffset > 0;
|
|
12
|
-
const removeFromBottom = hasBottom && cursorOffset < output.length - 1;
|
|
13
|
-
if (removeFromTop) {
|
|
14
|
-
output.shift();
|
|
15
|
-
cursorOffset -= 1;
|
|
16
|
-
}
|
|
17
|
-
else if (removeFromBottom) {
|
|
18
|
-
output.pop();
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
output.pop();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return output;
|
|
25
|
-
}
|
|
26
8
|
export function limitOptions(opts) {
|
|
27
9
|
const { cursor, options, style, output, maxItems = Number.POSITIVE_INFINITY, columnPadding = 0, rowPadding = 4 } = opts;
|
|
28
10
|
if (options.length === 0) {
|
|
@@ -36,17 +18,29 @@ export function limitOptions(opts) {
|
|
|
36
18
|
if (cursor >= cappedVisibleCount - 3) {
|
|
37
19
|
start = Math.max(Math.min(cursor - cappedVisibleCount + 3, options.length - cappedVisibleCount), 0);
|
|
38
20
|
}
|
|
39
|
-
|
|
40
|
-
const hasBottomMarker = cappedVisibleCount < options.length && start + cappedVisibleCount < options.length;
|
|
21
|
+
let end = start + cappedVisibleCount;
|
|
41
22
|
const visible = options
|
|
42
|
-
.slice(start,
|
|
23
|
+
.slice(start, end)
|
|
43
24
|
.map((option, index) => wrapAnsi(style(option, start + index === cursor), columns, { hard: true, trim: false }));
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
25
|
+
const marker = wrapAnsi(color.dim(GLYPHS.ellipsis), columns, { hard: true, trim: false });
|
|
26
|
+
const markerRows = marker.split("\n").length;
|
|
27
|
+
while (visible.length > 1 && countLines(visible) + markerRows * (Number(start > 0) + Number(end < options.length)) > rowBudget) {
|
|
28
|
+
if (start < cursor) {
|
|
29
|
+
visible.shift();
|
|
30
|
+
start += 1;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
visible.pop();
|
|
34
|
+
end -= 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
let remainingRows = rowBudget - countLines(visible);
|
|
38
|
+
if (start > 0 && remainingRows >= markerRows) {
|
|
39
|
+
visible.unshift(marker);
|
|
40
|
+
remainingRows -= markerRows;
|
|
47
41
|
}
|
|
48
|
-
if (
|
|
49
|
-
|
|
42
|
+
if (end < options.length && remainingRows >= markerRows) {
|
|
43
|
+
visible.push(marker);
|
|
50
44
|
}
|
|
51
|
-
return
|
|
45
|
+
return visible;
|
|
52
46
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { color } from "../../components/color.js";
|
|
2
|
+
import { graphemes } from "../../dashboard/terminal-width.js";
|
|
2
3
|
import { GLYPHS, symbol, symbolBar } from "./glyphs.js";
|
|
3
4
|
import { Prompt } from "./core.js";
|
|
5
|
+
import { wrapTextWithPrefix } from "./wrap.js";
|
|
4
6
|
class PasswordPrompt extends Prompt {
|
|
5
7
|
mask;
|
|
6
8
|
constructor(opts) {
|
|
@@ -15,16 +17,17 @@ class PasswordPrompt extends Prompt {
|
|
|
15
17
|
this.on("userInput", (value) => this.setValue(value));
|
|
16
18
|
}
|
|
17
19
|
get masked() {
|
|
18
|
-
return this.mask.repeat(this.userInput.length);
|
|
20
|
+
return this.mask.repeat(graphemes(this.userInput).length);
|
|
19
21
|
}
|
|
20
22
|
get userInputWithCursor() {
|
|
21
23
|
if (this.state === "submit") {
|
|
22
24
|
return this.masked;
|
|
23
25
|
}
|
|
24
26
|
const masked = this.masked;
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
27
|
+
const maskCursor = graphemes(this.userInput.slice(0, this.cursor)).length * this.mask.length;
|
|
28
|
+
const before = masked.slice(0, maskCursor);
|
|
29
|
+
const current = masked.slice(maskCursor, maskCursor + this.mask.length);
|
|
30
|
+
const after = masked.slice(maskCursor + this.mask.length);
|
|
28
31
|
if (current) {
|
|
29
32
|
return `${before}${color.inverse(current)}${after}`;
|
|
30
33
|
}
|
|
@@ -40,15 +43,15 @@ function renderHeader(prompt, message) {
|
|
|
40
43
|
function renderPasswordPrompt(prompt, opts) {
|
|
41
44
|
const value = prompt.masked;
|
|
42
45
|
if (prompt.state === "submit") {
|
|
43
|
-
return `${renderHeader(prompt, opts.message)}\n${color.gray(GLYPHS.bar)}
|
|
46
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, color.dim(value), `${color.gray(GLYPHS.bar)} `)}\n${color.green(GLYPHS.barEnd)}`;
|
|
44
47
|
}
|
|
45
48
|
if (prompt.state === "cancel") {
|
|
46
|
-
return `${renderHeader(prompt, opts.message)}\n${color.gray(GLYPHS.bar)}
|
|
49
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, color.dim.strikethrough(value), `${color.gray(GLYPHS.bar)} `)}\n${color.red(GLYPHS.barEnd)}`;
|
|
47
50
|
}
|
|
48
51
|
if (prompt.state === "error") {
|
|
49
|
-
return `${renderHeader(prompt, opts.message)}\n${symbolBar(prompt.state)}
|
|
52
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, prompt.userInputWithCursor, `${symbolBar(prompt.state)} `)}\n${color.yellow(GLYPHS.barEnd)} ${color.yellow(prompt.error)}`;
|
|
50
53
|
}
|
|
51
|
-
return `${renderHeader(prompt, opts.message)}\n${symbolBar(prompt.state)}
|
|
54
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, prompt.userInputWithCursor, `${symbolBar(prompt.state)} `)}\n${color.cyan(GLYPHS.barEnd)}`;
|
|
52
55
|
}
|
|
53
56
|
export function passwordPrompt(opts) {
|
|
54
57
|
return new PasswordPrompt(opts).prompt();
|
|
@@ -2,6 +2,7 @@ import { color } from "../../components/color.js";
|
|
|
2
2
|
import { GLYPHS, symbol } from "./glyphs.js";
|
|
3
3
|
import { Prompt } from "./core.js";
|
|
4
4
|
import { limitOptions } from "./pagination.js";
|
|
5
|
+
import { wrapTextWithPrefix } from "./wrap.js";
|
|
5
6
|
class SelectPrompt extends Prompt {
|
|
6
7
|
options;
|
|
7
8
|
constructor(opts) {
|
|
@@ -72,7 +73,7 @@ function renderSelectPrompt(prompt, opts) {
|
|
|
72
73
|
const option = prompt.visibleOptions[prompt.cursor];
|
|
73
74
|
const rendered = option ? renderOption(option, false, prompt.state === "submit", prompt.state === "cancel") : "";
|
|
74
75
|
const end = prompt.state === "submit" ? color.green(GLYPHS.barEnd) : color.red(GLYPHS.barEnd);
|
|
75
|
-
return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${color.gray(GLYPHS.bar)}
|
|
76
|
+
return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${wrapTextWithPrefix(opts.output ?? process.stdout, rendered, `${color.gray(GLYPHS.bar)} `)}\n${end}`;
|
|
76
77
|
}
|
|
77
78
|
const lines = limitOptions({
|
|
78
79
|
cursor: prompt.cursor,
|
|
@@ -81,7 +82,7 @@ function renderSelectPrompt(prompt, opts) {
|
|
|
81
82
|
maxItems: opts.maxItems,
|
|
82
83
|
columnPadding: 3,
|
|
83
84
|
style: (option, active) => renderOption(option, active, false, false)
|
|
84
|
-
}).
|
|
85
|
+
}).flatMap((line) => line.split("\n").map((physicalLine) => `${color.cyan(GLYPHS.bar)} ${physicalLine}`));
|
|
85
86
|
return `${color.gray(GLYPHS.barStart)} ${symbol(prompt.state)} ${opts.message}\n${lines.join("\n")}\n${color.cyan(GLYPHS.barEnd)}`;
|
|
86
87
|
}
|
|
87
88
|
export function selectPrompt(opts) {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { color } from "../../components/color.js";
|
|
2
|
+
import { graphemes } from "../../dashboard/terminal-width.js";
|
|
2
3
|
import { GLYPHS, symbol, symbolBar } from "./glyphs.js";
|
|
3
4
|
import { Prompt } from "./core.js";
|
|
5
|
+
import { wrapTextWithPrefix } from "./wrap.js";
|
|
4
6
|
class TextPrompt extends Prompt {
|
|
5
7
|
constructor(opts) {
|
|
6
8
|
const initialUserInput = opts.initialValue ?? "";
|
|
@@ -9,7 +11,7 @@ class TextPrompt extends Prompt {
|
|
|
9
11
|
initialValue: initialUserInput,
|
|
10
12
|
initialUserInput,
|
|
11
13
|
render: (prompt) => renderTextPrompt(prompt, opts),
|
|
12
|
-
validate: opts.validate
|
|
14
|
+
validate: (value) => opts.validate?.(value || opts.defaultValue || "")
|
|
13
15
|
});
|
|
14
16
|
this.on("userInput", (value) => this.setValue(value));
|
|
15
17
|
this.on("finalize", () => {
|
|
@@ -23,8 +25,8 @@ class TextPrompt extends Prompt {
|
|
|
23
25
|
return this.userInput;
|
|
24
26
|
}
|
|
25
27
|
const before = this.userInput.slice(0, this.cursor);
|
|
26
|
-
const current = this.userInput
|
|
27
|
-
const after = this.userInput.slice(this.cursor +
|
|
28
|
+
const current = graphemes(this.userInput.slice(this.cursor))[0];
|
|
29
|
+
const after = this.userInput.slice(this.cursor + (current?.length ?? 0));
|
|
28
30
|
if (current) {
|
|
29
31
|
return `${before}${color.inverse(current)}${after}`;
|
|
30
32
|
}
|
|
@@ -40,20 +42,21 @@ function renderHeader(prompt, message) {
|
|
|
40
42
|
function renderTextPrompt(prompt, opts) {
|
|
41
43
|
const value = prompt.value ?? "";
|
|
42
44
|
if (prompt.state === "submit") {
|
|
43
|
-
return `${renderHeader(prompt, opts.message)}\n${color.gray(GLYPHS.bar)}
|
|
45
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, color.dim(value), `${color.gray(GLYPHS.bar)} `)}\n${color.green(GLYPHS.barEnd)}`;
|
|
44
46
|
}
|
|
45
47
|
if (prompt.state === "cancel") {
|
|
46
|
-
return `${renderHeader(prompt, opts.message)}\n${color.gray(GLYPHS.bar)}
|
|
48
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, color.dim.strikethrough(value), `${color.gray(GLYPHS.bar)} `)}\n${color.red(GLYPHS.barEnd)}`;
|
|
47
49
|
}
|
|
50
|
+
const [placeholder, ...placeholderRest] = graphemes(opts.placeholder ?? "");
|
|
48
51
|
const input = prompt.userInput.length > 0
|
|
49
52
|
? prompt.userInputWithCursor
|
|
50
|
-
:
|
|
51
|
-
? `${color.inverse(
|
|
53
|
+
: placeholder
|
|
54
|
+
? `${color.inverse(placeholder)}${color.dim(placeholderRest.join(""))}`
|
|
52
55
|
: color.inverse("_");
|
|
53
56
|
if (prompt.state === "error") {
|
|
54
|
-
return `${renderHeader(prompt, opts.message)}\n${symbolBar(prompt.state)}
|
|
57
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, input, `${symbolBar(prompt.state)} `)}\n${color.yellow(GLYPHS.barEnd)} ${color.yellow(prompt.error)}`;
|
|
55
58
|
}
|
|
56
|
-
return `${renderHeader(prompt, opts.message)}\n${symbolBar(prompt.state)}
|
|
59
|
+
return `${renderHeader(prompt, opts.message)}\n${wrapTextWithPrefix(opts.output ?? process.stdout, input, `${symbolBar(prompt.state)} `)}\n${color.cyan(GLYPHS.barEnd)}`;
|
|
57
60
|
}
|
|
58
61
|
export function textPrompt(opts) {
|
|
59
62
|
return new TextPrompt(opts).prompt();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolcraft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.164",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
"yaml"
|
|
159
159
|
],
|
|
160
160
|
"optionalDependencies": {
|
|
161
|
-
"toolcraft-schema": "0.0.
|
|
161
|
+
"toolcraft-schema": "0.0.164",
|
|
162
162
|
"toolcraft-design": "*",
|
|
163
163
|
"@poe-code/frontmatter": "*",
|
|
164
164
|
"@poe-code/agent-mcp-config": "*",
|