@nan0web/ui-cli 1.1.0 → 2.0.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 +114 -207
- package/package.json +22 -12
- package/src/CLI.js +22 -29
- package/src/CLiMessage.js +2 -3
- package/src/Command.js +26 -24
- package/src/CommandError.js +3 -5
- package/src/CommandHelp.js +40 -36
- package/src/CommandMessage.js +56 -40
- package/src/CommandParser.js +27 -25
- package/src/InputAdapter.js +630 -90
- package/src/OutputAdapter.js +7 -8
- package/src/README.md.js +190 -316
- package/src/components/Alert.js +3 -6
- package/src/components/prompt/Autocomplete.js +12 -0
- package/src/components/prompt/Confirm.js +29 -0
- package/src/components/prompt/DateTime.js +26 -0
- package/src/components/prompt/Input.js +15 -0
- package/src/components/prompt/Mask.js +12 -0
- package/src/components/prompt/Multiselect.js +26 -0
- package/src/components/prompt/Next.js +8 -0
- package/src/components/prompt/Password.js +13 -0
- package/src/components/prompt/Pause.js +9 -0
- package/src/components/prompt/ProgressBar.js +16 -0
- package/src/components/prompt/Select.js +29 -0
- package/src/components/prompt/Slider.js +16 -0
- package/src/components/prompt/Spinner.js +29 -0
- package/src/components/prompt/Toggle.js +13 -0
- package/src/components/prompt/Tree.js +17 -0
- package/src/components/view/Alert.js +78 -0
- package/src/components/view/Badge.js +11 -0
- package/src/components/view/Nav.js +23 -0
- package/src/components/view/Table.js +12 -0
- package/src/components/view/Toast.js +9 -0
- package/src/core/Component.js +79 -0
- package/src/core/PropValidation.js +138 -0
- package/src/core/render.js +37 -0
- package/src/index.js +80 -41
- package/src/test/PlaygroundTest.js +37 -25
- package/src/test/index.js +2 -4
- package/src/ui/alert.js +58 -0
- package/src/ui/autocomplete.js +86 -0
- package/src/ui/badge.js +35 -0
- package/src/ui/confirm.js +49 -0
- package/src/ui/date-time.js +45 -0
- package/src/ui/form.js +120 -55
- package/src/ui/index.js +18 -4
- package/src/ui/input.js +79 -152
- package/src/ui/mask.js +132 -0
- package/src/ui/multiselect.js +59 -0
- package/src/ui/nav.js +74 -0
- package/src/ui/next.js +18 -13
- package/src/ui/progress.js +88 -0
- package/src/ui/select.js +49 -72
- package/src/ui/slider.js +154 -0
- package/src/ui/spinner.js +65 -0
- package/src/ui/table.js +163 -0
- package/src/ui/toast.js +34 -0
- package/src/ui/toggle.js +34 -0
- package/src/ui/tree.js +393 -0
- package/src/utils/parse.js +1 -1
- package/types/CLI.d.ts +5 -5
- package/types/CLiMessage.d.ts +1 -1
- package/types/Command.d.ts +2 -2
- package/types/CommandHelp.d.ts +3 -3
- package/types/CommandMessage.d.ts +8 -8
- package/types/CommandParser.d.ts +3 -3
- package/types/InputAdapter.d.ts +149 -15
- package/types/OutputAdapter.d.ts +1 -1
- package/types/README.md.d.ts +1 -1
- package/types/UiMessage.d.ts +31 -29
- package/types/components/prompt/Autocomplete.d.ts +6 -0
- package/types/components/prompt/Confirm.d.ts +6 -0
- package/types/components/prompt/DateTime.d.ts +6 -0
- package/types/components/prompt/Input.d.ts +6 -0
- package/types/components/prompt/Mask.d.ts +6 -0
- package/types/components/prompt/Multiselect.d.ts +6 -0
- package/types/components/prompt/Next.d.ts +6 -0
- package/types/components/prompt/Password.d.ts +6 -0
- package/types/components/prompt/Pause.d.ts +6 -0
- package/types/components/prompt/ProgressBar.d.ts +12 -0
- package/types/components/prompt/Select.d.ts +18 -0
- package/types/components/prompt/Slider.d.ts +6 -0
- package/types/components/prompt/Spinner.d.ts +21 -0
- package/types/components/prompt/Toggle.d.ts +6 -0
- package/types/components/prompt/Tree.d.ts +6 -0
- package/types/components/view/Alert.d.ts +21 -0
- package/types/components/view/Badge.d.ts +5 -0
- package/types/components/view/Nav.d.ts +15 -0
- package/types/components/view/Table.d.ts +10 -0
- package/types/components/view/Toast.d.ts +5 -0
- package/types/core/Component.d.ts +34 -0
- package/types/core/PropValidation.d.ts +48 -0
- package/types/core/render.d.ts +6 -0
- package/types/index.d.ts +47 -15
- package/types/test/PlaygroundTest.d.ts +12 -8
- package/types/test/index.d.ts +1 -1
- package/types/ui/alert.d.ts +14 -0
- package/types/ui/autocomplete.d.ts +20 -0
- package/types/ui/badge.d.ts +8 -0
- package/types/ui/confirm.d.ts +21 -0
- package/types/ui/date-time.d.ts +19 -0
- package/types/ui/form.d.ts +43 -12
- package/types/ui/index.d.ts +17 -2
- package/types/ui/input.d.ts +31 -74
- package/types/ui/mask.d.ts +29 -0
- package/types/ui/multiselect.d.ts +25 -0
- package/types/ui/nav.d.ts +27 -0
- package/types/ui/progress.d.ts +43 -0
- package/types/ui/select.d.ts +25 -64
- package/types/ui/slider.d.ts +23 -0
- package/types/ui/spinner.d.ts +28 -0
- package/types/ui/table.d.ts +28 -0
- package/types/ui/toast.d.ts +8 -0
- package/types/ui/toggle.d.ts +17 -0
- package/types/ui/tree.d.ts +48 -0
package/src/ui/tree.js
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree View Component - Interactive File/Directory Tree.
|
|
3
|
+
*
|
|
4
|
+
* @module ui/tree
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import process from 'node:process'
|
|
8
|
+
import readline from 'node:readline'
|
|
9
|
+
import Logger from '@nan0web/log'
|
|
10
|
+
import prompts from 'prompts'
|
|
11
|
+
import { CancelError } from '@nan0web/ui/core'
|
|
12
|
+
import { beep } from './input.js'
|
|
13
|
+
import {
|
|
14
|
+
validateString,
|
|
15
|
+
validateFunction,
|
|
16
|
+
validateNumber,
|
|
17
|
+
validateBoolean,
|
|
18
|
+
} from '../core/PropValidation.js'
|
|
19
|
+
|
|
20
|
+
// --- ANSI Utilities ---
|
|
21
|
+
const ESC = '\x1B['
|
|
22
|
+
const HIDE_CURSOR = `${ESC}?25l`
|
|
23
|
+
const SHOW_CURSOR = `${ESC}?25h`
|
|
24
|
+
const UP = (n = 1) => `${ESC}${n}A`
|
|
25
|
+
const ERASE_DOWN = `${ESC}J`
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Object} TreeNode
|
|
29
|
+
* @property {string} name
|
|
30
|
+
* @property {'file'|'dir'} type
|
|
31
|
+
* @property {TreeNode[]} [children] -- If undefined, might be loaded async
|
|
32
|
+
* @property {any} [payload] -- Custom data
|
|
33
|
+
* @property {any} [value] -- Result value (usually same as path or name)
|
|
34
|
+
* @property {string} [path] -- File path
|
|
35
|
+
* @property {boolean} [expanded] -- Internal state
|
|
36
|
+
* @property {boolean} [checked] -- Internal state
|
|
37
|
+
* @property {number} [depth] -- Calculated
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Tree Prompt
|
|
42
|
+
*/
|
|
43
|
+
export async function tree(config) {
|
|
44
|
+
const {
|
|
45
|
+
message = 'Select:',
|
|
46
|
+
mode = 'file', // 'file', 'dir', 'multi'
|
|
47
|
+
tree = null, // Root node or array of nodes
|
|
48
|
+
loader = null, // async (node) => children[]
|
|
49
|
+
limit = 10,
|
|
50
|
+
initialExpanded = [], // keys or paths
|
|
51
|
+
multiselect = mode === 'multi',
|
|
52
|
+
t = (k) => k,
|
|
53
|
+
} = config
|
|
54
|
+
|
|
55
|
+
// Prop Validation
|
|
56
|
+
validateString(message, 'message', 'Tree')
|
|
57
|
+
validateString(mode, 'mode', 'Tree')
|
|
58
|
+
validateNumber(limit, 'limit', 'Tree')
|
|
59
|
+
validateFunction(loader, 'loader', 'Tree')
|
|
60
|
+
validateFunction(t, 't', 'Tree')
|
|
61
|
+
validateBoolean(multiselect, 'multiselect', 'Tree')
|
|
62
|
+
|
|
63
|
+
// Initialize State
|
|
64
|
+
let roots = Array.isArray(tree) ? tree : tree ? [tree] : []
|
|
65
|
+
// If tree is empty, try to load if loader exists
|
|
66
|
+
if (roots.length === 0 && loader) {
|
|
67
|
+
const res = await loader(null) // Load roots
|
|
68
|
+
roots = res || []
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Automated environments support via PLAY_DEMO_SEQUENCE
|
|
72
|
+
if (process.env.PLAY_DEMO_SEQUENCE) {
|
|
73
|
+
const response = await prompts({
|
|
74
|
+
type: 'text',
|
|
75
|
+
name: 'value',
|
|
76
|
+
message: t(message),
|
|
77
|
+
initial: config.initial || roots[0]?.name || 'unknown',
|
|
78
|
+
})
|
|
79
|
+
return { value: response.value, cancelled: response.value === undefined }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Internal State
|
|
83
|
+
const state = {
|
|
84
|
+
roots,
|
|
85
|
+
cursor: 0, // Index in flattened list
|
|
86
|
+
offset: 0, // Scroll offset
|
|
87
|
+
expanded: new Set(), // Set of objects (nodes)
|
|
88
|
+
checked: new Set(), // Set of objects (nodes)
|
|
89
|
+
/** @type {TreeNode[]} */
|
|
90
|
+
flat: [], // Flattened visible nodes
|
|
91
|
+
loading: false,
|
|
92
|
+
message,
|
|
93
|
+
done: false,
|
|
94
|
+
aborted: false,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Helper: Flatten visible nodes
|
|
98
|
+
function flatten() {
|
|
99
|
+
/** @type {TreeNode[]} */
|
|
100
|
+
const list = []
|
|
101
|
+
const traverse = (nodes, depth) => {
|
|
102
|
+
for (const node of nodes) {
|
|
103
|
+
node.depth = depth
|
|
104
|
+
list.push(node)
|
|
105
|
+
if (state.expanded.has(node) && node.children) {
|
|
106
|
+
traverse(node.children, depth + 1)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
traverse(state.roots, 0)
|
|
111
|
+
return list
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Load children if needed
|
|
115
|
+
async function toggleExpand(node) {
|
|
116
|
+
if (node.type !== 'dir') return
|
|
117
|
+
|
|
118
|
+
if (state.expanded.has(node)) {
|
|
119
|
+
state.expanded.delete(node)
|
|
120
|
+
} else {
|
|
121
|
+
state.expanded.add(node)
|
|
122
|
+
if (!node.children && loader) {
|
|
123
|
+
state.loading = true
|
|
124
|
+
render() // Show loading
|
|
125
|
+
try {
|
|
126
|
+
const children = await loader(node)
|
|
127
|
+
node.children = children || []
|
|
128
|
+
} catch (e) {
|
|
129
|
+
state.expanded.delete(node) // Rollback
|
|
130
|
+
}
|
|
131
|
+
state.loading = false
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
state.flat = flatten()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function up() {
|
|
138
|
+
if (state.cursor > 0) {
|
|
139
|
+
state.cursor--
|
|
140
|
+
if (state.cursor < state.offset) {
|
|
141
|
+
state.offset--
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function down() {
|
|
147
|
+
if (state.cursor < state.flat.length - 1) {
|
|
148
|
+
state.cursor++
|
|
149
|
+
if (state.cursor >= state.offset + limit) {
|
|
150
|
+
state.offset++
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function left() {
|
|
156
|
+
const node = state.flat[state.cursor]
|
|
157
|
+
if (state.expanded.has(node)) {
|
|
158
|
+
// Collapse
|
|
159
|
+
toggleExpand(node)
|
|
160
|
+
} else {
|
|
161
|
+
// Jump to parent
|
|
162
|
+
if (node.depth && node.depth > 0) {
|
|
163
|
+
// Find parent index: look backwards for node with depth - 1
|
|
164
|
+
for (let i = state.cursor - 1; i >= 0; i--) {
|
|
165
|
+
if (state.flat[i].depth === node.depth - 1) {
|
|
166
|
+
state.cursor = i
|
|
167
|
+
// Update scroll
|
|
168
|
+
if (state.cursor < state.offset) state.offset = state.cursor
|
|
169
|
+
break
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function right() {
|
|
177
|
+
const node = state.flat[state.cursor]
|
|
178
|
+
if (node.type === 'dir') {
|
|
179
|
+
if (!state.expanded.has(node)) {
|
|
180
|
+
await toggleExpand(node)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function check() {
|
|
186
|
+
if (!multiselect) return
|
|
187
|
+
const node = state.flat[state.cursor]
|
|
188
|
+
if (state.checked.has(node)) state.checked.delete(node)
|
|
189
|
+
else state.checked.add(node)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Initial Flatten
|
|
193
|
+
state.flat = flatten()
|
|
194
|
+
|
|
195
|
+
// Setup Input
|
|
196
|
+
const { stdin, stdout } = process
|
|
197
|
+
if (stdin.isTTY && typeof stdin.setRawMode === 'function') {
|
|
198
|
+
stdin.setRawMode(true)
|
|
199
|
+
}
|
|
200
|
+
stdin.resume()
|
|
201
|
+
readline.emitKeypressEvents(stdin)
|
|
202
|
+
|
|
203
|
+
// Render Loop
|
|
204
|
+
let linesRendered = 0
|
|
205
|
+
let firstRender = true
|
|
206
|
+
|
|
207
|
+
function render() {
|
|
208
|
+
if (!firstRender) {
|
|
209
|
+
// Clear previous output
|
|
210
|
+
stdout.write(UP(linesRendered) + ERASE_DOWN)
|
|
211
|
+
}
|
|
212
|
+
firstRender = false
|
|
213
|
+
|
|
214
|
+
let out = ''
|
|
215
|
+
// Title
|
|
216
|
+
out += Logger.style(`? ${t(state.message)}\n`, { color: Logger.CYAN })
|
|
217
|
+
|
|
218
|
+
// Tree View
|
|
219
|
+
const visible = state.flat.slice(state.offset, state.offset + limit)
|
|
220
|
+
|
|
221
|
+
if (visible.length === 0) {
|
|
222
|
+
out += Logger.style(` ${t('tree.empty')}\n`, { color: Logger.DIM })
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (let i = 0; i < visible.length; i++) {
|
|
226
|
+
const node = visible[i]
|
|
227
|
+
const absIndex = state.offset + i
|
|
228
|
+
const isFocused = absIndex === state.cursor
|
|
229
|
+
|
|
230
|
+
// Indent
|
|
231
|
+
let line = ' '.repeat(node.depth || 0)
|
|
232
|
+
|
|
233
|
+
// Prefix (Expandable)
|
|
234
|
+
if (node.type === 'dir') {
|
|
235
|
+
line += state.expanded.has(node) ? '▼ ' : '▶ '
|
|
236
|
+
} else {
|
|
237
|
+
line += ' ' // Align with arrows
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Checkbox
|
|
241
|
+
if (multiselect) {
|
|
242
|
+
const isChecked = state.checked.has(node)
|
|
243
|
+
line += isChecked
|
|
244
|
+
? Logger.style('◉ ', { color: Logger.GREEN })
|
|
245
|
+
: Logger.style('◯ ', { color: Logger.DIM })
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Icon + Name
|
|
249
|
+
const icon = node.type === 'dir' ? '📁' : '📄'
|
|
250
|
+
const nameDisplay = `${icon} ${node.name}`
|
|
251
|
+
|
|
252
|
+
if (isFocused) {
|
|
253
|
+
// Use bold/color for focus
|
|
254
|
+
line += Logger.style('> ' + nameDisplay, { color: Logger.CYAN })
|
|
255
|
+
if (state.loading && state.expanded.has(node))
|
|
256
|
+
line += Logger.style(` ${t('tree.loading')}`, { color: Logger.DIM })
|
|
257
|
+
} else {
|
|
258
|
+
line += ' ' + nameDisplay
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
out += line + '\n'
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Instructions / Footer
|
|
265
|
+
const helpKey = multiselect ? 'tree.help.multi' : 'tree.help.single'
|
|
266
|
+
out += Logger.style(`\n${t(helpKey)}`, { color: Logger.DIM })
|
|
267
|
+
|
|
268
|
+
stdout.write(out)
|
|
269
|
+
linesRendered = out.split('\n').length - 1 // approximate
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Interactive Loop
|
|
273
|
+
return new Promise((resolve, reject) => {
|
|
274
|
+
const onKey = async (str, key) => {
|
|
275
|
+
if (state.done) return
|
|
276
|
+
|
|
277
|
+
// Handle Ctrl+C
|
|
278
|
+
if (key.ctrl && key.name === 'c') {
|
|
279
|
+
cleanup()
|
|
280
|
+
reject(new CancelError())
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (state.loading) return // Block input while loading
|
|
285
|
+
|
|
286
|
+
switch (key.name) {
|
|
287
|
+
case 'up':
|
|
288
|
+
up()
|
|
289
|
+
break
|
|
290
|
+
case 'down':
|
|
291
|
+
down()
|
|
292
|
+
break
|
|
293
|
+
case 'left':
|
|
294
|
+
left()
|
|
295
|
+
break // Sync handled?
|
|
296
|
+
case 'right':
|
|
297
|
+
await right()
|
|
298
|
+
break
|
|
299
|
+
case 'space':
|
|
300
|
+
check()
|
|
301
|
+
break
|
|
302
|
+
case 'return':
|
|
303
|
+
case 'enter':
|
|
304
|
+
submit()
|
|
305
|
+
return
|
|
306
|
+
default:
|
|
307
|
+
if (!key.ctrl && !key.meta && str && str.length === 1) {
|
|
308
|
+
const s = str.toLowerCase()
|
|
309
|
+
const idx = state.flat.findIndex(
|
|
310
|
+
(n, i) => i > state.cursor && n.name.toLowerCase().startsWith(s)
|
|
311
|
+
)
|
|
312
|
+
if (idx !== -1) {
|
|
313
|
+
state.cursor = idx
|
|
314
|
+
} else {
|
|
315
|
+
const idx2 = state.flat.findIndex((n) => n.name.toLowerCase().startsWith(s))
|
|
316
|
+
if (idx2 !== -1) state.cursor = idx2
|
|
317
|
+
}
|
|
318
|
+
// Adjust scroll offset
|
|
319
|
+
if (state.cursor < state.offset) state.offset = state.cursor
|
|
320
|
+
if (state.cursor >= state.offset + limit)
|
|
321
|
+
state.offset = Math.max(0, state.cursor - limit + 1)
|
|
322
|
+
}
|
|
323
|
+
break
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
state.flat = flatten() // Re-flatten (redundant if only selection moved, but safe)
|
|
327
|
+
render()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function submit() {
|
|
331
|
+
if (multiselect) {
|
|
332
|
+
state.done = true
|
|
333
|
+
cleanup()
|
|
334
|
+
resolve({
|
|
335
|
+
value: Array.from(state.checked).map((n) => n.value || n.path || n.name),
|
|
336
|
+
cancelled: false,
|
|
337
|
+
})
|
|
338
|
+
} else {
|
|
339
|
+
const node = state.flat[state.cursor]
|
|
340
|
+
if (!node) return // Nothing to submit
|
|
341
|
+
// Validation
|
|
342
|
+
if (mode === 'file' && node.type !== 'file') {
|
|
343
|
+
// If Enter on dir in 'file' mode -> toggle expand
|
|
344
|
+
if (node.type === 'dir') {
|
|
345
|
+
toggleExpand(node).then(() => {
|
|
346
|
+
state.flat = flatten()
|
|
347
|
+
render()
|
|
348
|
+
})
|
|
349
|
+
return
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (mode === 'dir' && node.type !== 'dir') {
|
|
353
|
+
beep()
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
state.done = true
|
|
357
|
+
cleanup()
|
|
358
|
+
resolve({ value: node.value || node.path || node.name, cancelled: false, node })
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function cleanup() {
|
|
363
|
+
stdin.removeListener('keypress', onKey)
|
|
364
|
+
if (process.stdin.isTTY && typeof process.stdin.setRawMode === 'function') {
|
|
365
|
+
process.stdin.setRawMode(false)
|
|
366
|
+
}
|
|
367
|
+
stdin.pause() // Crucial for automated tests to release event loop
|
|
368
|
+
|
|
369
|
+
// Final cleanup message or checkmark?
|
|
370
|
+
if (state.done) {
|
|
371
|
+
stdout.write(UP(linesRendered) + ERASE_DOWN)
|
|
372
|
+
stdout.write(Logger.style(`✔ ${t(state.message)} `, { color: Logger.GREEN }))
|
|
373
|
+
// Show result summary
|
|
374
|
+
if (multiselect) {
|
|
375
|
+
stdout.write(
|
|
376
|
+
Logger.style(`${state.checked.size} ${t('tree.selected')}\n`, { color: Logger.WHITE })
|
|
377
|
+
)
|
|
378
|
+
} else {
|
|
379
|
+
const node = state.flat[state.cursor]
|
|
380
|
+
stdout.write(Logger.style(`${node.name}\n`, { color: Logger.WHITE }))
|
|
381
|
+
}
|
|
382
|
+
} else {
|
|
383
|
+
// Cancelled
|
|
384
|
+
stdout.write('\n')
|
|
385
|
+
}
|
|
386
|
+
stdout.write(SHOW_CURSOR)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
stdin.on('keypress', onKey)
|
|
390
|
+
stdout.write(HIDE_CURSOR)
|
|
391
|
+
render()
|
|
392
|
+
})
|
|
393
|
+
}
|
package/src/utils/parse.js
CHANGED
package/types/CLI.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export default class CLi {
|
|
|
22
22
|
commands?: any;
|
|
23
23
|
logger?: Logger | undefined;
|
|
24
24
|
Messages?: Function[] | undefined;
|
|
25
|
-
}
|
|
25
|
+
});
|
|
26
26
|
/** @type {string[]} */
|
|
27
27
|
argv: string[];
|
|
28
28
|
/** @type {Logger} */
|
|
@@ -37,9 +37,9 @@ export default class CLi {
|
|
|
37
37
|
* @param {Message} [msg] - Optional pre‑built message.
|
|
38
38
|
* @returns {AsyncGenerator<OutputMessage>}
|
|
39
39
|
*/
|
|
40
|
-
run(msg?: Message
|
|
40
|
+
run(msg?: Message): AsyncGenerator<OutputMessage>;
|
|
41
41
|
#private;
|
|
42
42
|
}
|
|
43
|
-
import Logger from
|
|
44
|
-
import { Message } from
|
|
45
|
-
import { OutputMessage } from
|
|
43
|
+
import Logger from '@nan0web/log';
|
|
44
|
+
import { Message } from '@nan0web/co';
|
|
45
|
+
import { OutputMessage } from '@nan0web/co';
|
package/types/CLiMessage.d.ts
CHANGED
package/types/Command.d.ts
CHANGED
package/types/CommandHelp.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export default class CommandHelp {
|
|
|
22
22
|
* @param {typeof Message} MessageClass - Message class with a schema.
|
|
23
23
|
* @param {Logger} [logger=new Logger()] - Optional logger.
|
|
24
24
|
*/
|
|
25
|
-
constructor(MessageClass: typeof Message, logger?: Logger
|
|
25
|
+
constructor(MessageClass: typeof Message, logger?: Logger);
|
|
26
26
|
/** @type {typeof Message} Message class the help is built for */
|
|
27
27
|
MessageClass: typeof Message;
|
|
28
28
|
/** @type {Logger} Logger used for printing */
|
|
@@ -81,5 +81,5 @@ export type CommandHelpField = {
|
|
|
81
81
|
*/
|
|
82
82
|
pattern?: RegExp | undefined;
|
|
83
83
|
};
|
|
84
|
-
import { Message } from
|
|
85
|
-
import Logger from
|
|
84
|
+
import { Message } from '@nan0web/co';
|
|
85
|
+
import Logger from '@nan0web/log';
|
|
@@ -12,7 +12,7 @@ export default class CommandMessage extends Message {
|
|
|
12
12
|
* @returns {CommandMessage}
|
|
13
13
|
* @throws {CommandError} If no input is supplied.
|
|
14
14
|
*/
|
|
15
|
-
static parse(argv: string | string[], BodyClass?:
|
|
15
|
+
static parse(argv: string | string[], BodyClass?: typeof Object): CommandMessage;
|
|
16
16
|
/**
|
|
17
17
|
* Convert a raw input into a {@link CommandMessage} instance.
|
|
18
18
|
*
|
|
@@ -34,23 +34,23 @@ export default class CommandMessage extends Message {
|
|
|
34
34
|
opts?: any;
|
|
35
35
|
children?: CommandMessage[] | undefined;
|
|
36
36
|
body?: any;
|
|
37
|
-
}
|
|
37
|
+
});
|
|
38
38
|
/** @param {string} v */
|
|
39
|
-
set name(
|
|
39
|
+
set name(v: string);
|
|
40
40
|
/** @returns {string} */
|
|
41
41
|
get name(): string;
|
|
42
42
|
/** @param {string[]} v */
|
|
43
|
-
set argv(
|
|
43
|
+
set argv(v: string[]);
|
|
44
44
|
/** @returns {string[]} */
|
|
45
45
|
get argv(): string[];
|
|
46
46
|
/** @param {Object} v */
|
|
47
|
-
set opts(
|
|
47
|
+
set opts(v: any);
|
|
48
48
|
/** @returns {Object} */
|
|
49
49
|
get opts(): any;
|
|
50
50
|
/** @returns {Array<CommandMessage>} */
|
|
51
|
-
get children(): CommandMessage
|
|
51
|
+
get children(): Array<CommandMessage>;
|
|
52
52
|
/** @returns {Array<string>} Full command line (name + args). */
|
|
53
|
-
get args(): string
|
|
53
|
+
get args(): Array<string>;
|
|
54
54
|
/** @returns {string} Sub‑command name of the first child, or empty string. */
|
|
55
55
|
get subCommand(): string;
|
|
56
56
|
/** @returns {CommandMessage|null} First child message, or null. */
|
|
@@ -63,4 +63,4 @@ export default class CommandMessage extends Message {
|
|
|
63
63
|
add(msg: CommandMessage | any): void;
|
|
64
64
|
#private;
|
|
65
65
|
}
|
|
66
|
-
import { Message } from
|
|
66
|
+
import { Message } from '@nan0web/co';
|
package/types/CommandParser.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export default class CommandParser {
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {Array<Function>} [Messages=[]] - Root message classes.
|
|
7
7
|
*/
|
|
8
|
-
constructor(Messages?: Function
|
|
8
|
+
constructor(Messages?: Array<Function>);
|
|
9
9
|
/** @type {Array<Function>} */
|
|
10
10
|
Messages: Array<Function>;
|
|
11
11
|
/**
|
|
@@ -15,7 +15,7 @@ export default class CommandParser {
|
|
|
15
15
|
* @returns {Message}
|
|
16
16
|
* @throws {Error} If no command is supplied or unknown root command.
|
|
17
17
|
*/
|
|
18
|
-
parse(input?: string | string[]
|
|
18
|
+
parse(input?: string | string[]): Message;
|
|
19
19
|
/**
|
|
20
20
|
* Generate help text for a given message class.
|
|
21
21
|
*
|
|
@@ -25,4 +25,4 @@ export default class CommandParser {
|
|
|
25
25
|
generateHelp(MessageClass: typeof Message): string;
|
|
26
26
|
#private;
|
|
27
27
|
}
|
|
28
|
-
import { Message } from
|
|
28
|
+
import { Message } from '@nan0web/co';
|