node-automator 1.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.
Files changed (143) hide show
  1. package/LICENSE +15 -0
  2. package/assets/alert.png +0 -0
  3. package/assets/error.png +0 -0
  4. package/assets/success.png +0 -0
  5. package/commands/abort.js +11 -0
  6. package/commands/alert.js +19 -0
  7. package/commands/assemble.js +56 -0
  8. package/commands/assert.js +18 -0
  9. package/commands/base.js +363 -0
  10. package/commands/break.js +11 -0
  11. package/commands/change_filter.js +34 -0
  12. package/commands/cheerio.js +15 -0
  13. package/commands/clear_screen.js +14 -0
  14. package/commands/commit.js +31 -0
  15. package/commands/compare.js +23 -0
  16. package/commands/confirm.js +16 -0
  17. package/commands/continue.js +11 -0
  18. package/commands/copy.js +14 -0
  19. package/commands/cursor_up.js +12 -0
  20. package/commands/date.js +25 -0
  21. package/commands/debugger.js +11 -0
  22. package/commands/dingtalk.js +110 -0
  23. package/commands/disassemble.js +36 -0
  24. package/commands/divination.js +15 -0
  25. package/commands/download.js +12 -0
  26. package/commands/edit.js +83 -0
  27. package/commands/edit_excel.js +57 -0
  28. package/commands/env_optional.js +57 -0
  29. package/commands/eval.js +27 -0
  30. package/commands/exec_from_file.js +31 -0
  31. package/commands/export_diff_head.js +31 -0
  32. package/commands/file_info.js +41 -0
  33. package/commands/file_size.js +12 -0
  34. package/commands/files.js +14 -0
  35. package/commands/first_exist.js +18 -0
  36. package/commands/flatten.js +18 -0
  37. package/commands/ftp.js +161 -0
  38. package/commands/git_commit.js +31 -0
  39. package/commands/git_export_diff_head.js +13 -0
  40. package/commands/git_is_dirty.js +13 -0
  41. package/commands/hash.js +21 -0
  42. package/commands/http_request.js +12 -0
  43. package/commands/image_crop.js +19 -0
  44. package/commands/input.js +37 -0
  45. package/commands/interval.js +60 -0
  46. package/commands/is_dirty.js +31 -0
  47. package/commands/jwt_decode.js +22 -0
  48. package/commands/list2dict.js +32 -0
  49. package/commands/log.js +72 -0
  50. package/commands/loop.js +115 -0
  51. package/commands/loop_forever.js +66 -0
  52. package/commands/merge_files.js +39 -0
  53. package/commands/mgr.js +476 -0
  54. package/commands/modify.js +44 -0
  55. package/commands/move.js +14 -0
  56. package/commands/move_local.js +14 -0
  57. package/commands/multi_select.js +16 -0
  58. package/commands/mysql_connect.js +29 -0
  59. package/commands/mysql_end.js +13 -0
  60. package/commands/mysql_query.js +19 -0
  61. package/commands/notify.js +13 -0
  62. package/commands/open.js +30 -0
  63. package/commands/parse.js +18 -0
  64. package/commands/pause.js +15 -0
  65. package/commands/print.js +47 -0
  66. package/commands/read_cache.js +12 -0
  67. package/commands/read_cfg.js +13 -0
  68. package/commands/read_clipboard.js +19 -0
  69. package/commands/read_excel.js +17 -0
  70. package/commands/read_only_excel.js +40 -0
  71. package/commands/read_plain.js +14 -0
  72. package/commands/remove.js +14 -0
  73. package/commands/retrieve.js +17 -0
  74. package/commands/reveal.js +33 -0
  75. package/commands/revision_info.js +31 -0
  76. package/commands/schedule.js +80 -0
  77. package/commands/select.js +17 -0
  78. package/commands/send_email.js +25 -0
  79. package/commands/share_data.js +745 -0
  80. package/commands/shell.js +13 -0
  81. package/commands/sleep.js +17 -0
  82. package/commands/store.js +13 -0
  83. package/commands/store_optional.js +33 -0
  84. package/commands/stream.js +16 -0
  85. package/commands/stringify.js +24 -0
  86. package/commands/svn_add.js +26 -0
  87. package/commands/svn_commit.js +49 -0
  88. package/commands/svn_export_diff_head.js +13 -0
  89. package/commands/svn_is_dirty.js +13 -0
  90. package/commands/svn_update.js +27 -0
  91. package/commands/switch.js +18 -0
  92. package/commands/temp.js +12 -0
  93. package/commands/tencent_cos.js +323 -0
  94. package/commands/test.js +14 -0
  95. package/commands/title.js +18 -0
  96. package/commands/tmpdir.js +21 -0
  97. package/commands/translate.js +58 -0
  98. package/commands/traversal.js +38 -0
  99. package/commands/truncate.js +31 -0
  100. package/commands/truncate_all.js +62 -0
  101. package/commands/ts_sort.js +18 -0
  102. package/commands/unzip.js +50 -0
  103. package/commands/updating_revision.js +28 -0
  104. package/commands/valid_required.js +31 -0
  105. package/commands/void.js +10 -0
  106. package/commands/watch_mail.js +94 -0
  107. package/commands/web_socket.js +233 -0
  108. package/commands/which.js +60 -0
  109. package/commands/write_cache.js +16 -0
  110. package/commands/write_cfg.js +18 -0
  111. package/commands/write_clipboard.js +23 -0
  112. package/commands/write_excel.js +17 -0
  113. package/commands/write_plain.js +18 -0
  114. package/commands/zip.js +78 -0
  115. package/commands/zip_data.js +64 -0
  116. package/examples/print_env.yml +3 -0
  117. package/index.js +109 -0
  118. package/package.json +63 -0
  119. package/scratch/.gitkeep +0 -0
  120. package/utils/alert_tool.js +133 -0
  121. package/utils/base64_tool.js +13 -0
  122. package/utils/cache/local_cache.js +79 -0
  123. package/utils/cache/raw_cache.js +47 -0
  124. package/utils/cache/simple_local_cache.js +33 -0
  125. package/utils/cache/simple_sqlite_cache.js +43 -0
  126. package/utils/cache/simple_svn_cache.js +49 -0
  127. package/utils/cache/svn_cache.js +40 -0
  128. package/utils/cache_tool.js +53 -0
  129. package/utils/display_tool.js +85 -0
  130. package/utils/divination_util.js +555 -0
  131. package/utils/file_tool.js +529 -0
  132. package/utils/hash_tool.js +8 -0
  133. package/utils/interaction_tool.js +228 -0
  134. package/utils/log_tool.js +190 -0
  135. package/utils/parse_directive.js +194 -0
  136. package/utils/parse_tool.js +236 -0
  137. package/utils/request_tool.js +156 -0
  138. package/utils/scm_tool.js +108 -0
  139. package/utils/selection_tool.js +593 -0
  140. package/utils/shell_tool.js +162 -0
  141. package/utils/svn_diff/diff_file.js +44 -0
  142. package/utils/svn_diff/diff_parser.js +55 -0
  143. package/utils/transform_tool.js +520 -0
@@ -0,0 +1,593 @@
1
+ const { OPTION_PAGE_SIZE, shareData, eval_code } = require("../commands/share_data");
2
+ const {
3
+ toggleRawMode,
4
+ showOptions,
5
+ clearLine,
6
+ resetOptions,
7
+ } = require("./interaction_tool");
8
+ const { info, whisper, log, success, getPrint, warn, debug } = require("./log_tool");
9
+ const { toArray } = require('./transform_tool');
10
+
11
+ async function select(content, data) {
12
+ // let data = this.selfData;
13
+ // let content = this.content;
14
+ info("");
15
+ // 支持数组以及普通对象
16
+ let optionKeys = Object.keys(content);
17
+ // 过滤
18
+ let keyFilterRegExp =
19
+ data.key_filter_pattern && new RegExp(data.key_filter_pattern);
20
+ let valueFilterRegExp =
21
+ data.value_filter_pattern && new RegExp(data.value_filter_pattern);
22
+ optionKeys = optionKeys.filter((key) => {
23
+ let value = content[key];
24
+ return (
25
+ true &&
26
+ (!keyFilterRegExp || keyFilterRegExp.test(key)) &&
27
+ (!valueFilterRegExp || valueFilterRegExp.test(JSON.stringify(value)))
28
+ );
29
+ });
30
+ let optionValues = optionKeys.map((key) => content[key]);
31
+ /**
32
+ * @type {any[]}
33
+ */
34
+ const options = content;
35
+ const viewOptions = optionKeys.map((optionKey, index) => {
36
+ let optionValue = options[optionKey];
37
+ if (data.preview_handler) {
38
+ return {
39
+ key: optionKey,
40
+ value: eval_code(data.preview_handler)(
41
+ optionValue,
42
+ index,
43
+ optionValues
44
+ ),
45
+ };
46
+ }
47
+ return {
48
+ key: optionKey,
49
+ value: data.preview
50
+ ? toArray(data.preview)
51
+ .map((k, i) =>
52
+ i == -1 ? `${k}:${optionValue[k]}` : optionValue[k]
53
+ )
54
+ .join(" | ")
55
+ : typeof optionValue === "object"
56
+ ? JSON.stringify(optionValue)
57
+ : optionValue,
58
+ };
59
+ });
60
+ let filteredViewOptions = viewOptions;
61
+ let searchMaxChars = 20;
62
+ let inputs = [];
63
+ let focusedIndex = 0;
64
+ let automatic = data.automatic;
65
+ // 推荐值
66
+ if (data.recommend_eval) {
67
+ try {
68
+ focusedIndex = Math.max(
69
+ 0,
70
+ Math.min(
71
+ optionKeys.length - 1,
72
+ parseInt(eval_code(data.recommend_eval))
73
+ )
74
+ );
75
+ } catch (err) {
76
+ warn(data.recommend_eval);
77
+ warn(`推荐失败: ${err}`);
78
+ focusedIndex = 0;
79
+ }
80
+ }
81
+ if (data.recommend) {
82
+ switch (data.recommend) {
83
+ case "first": {
84
+ focusedIndex = 0;
85
+ break;
86
+ }
87
+ case "last": {
88
+ focusedIndex = optionKeys.length - 1;
89
+ break;
90
+ }
91
+ case "middle": {
92
+ focusedIndex = Math.floor((optionKeys.length - 1) / 2);
93
+ break;
94
+ }
95
+ case "random": {
96
+ focusedIndex = Math.round((optionKeys.length - 1) * Math.random());
97
+ break;
98
+ }
99
+ default: {
100
+ warn(`不支持的 recommend: ${data.recommend}`);
101
+ break;
102
+ }
103
+ }
104
+ }
105
+ if (data.recommend_context) {
106
+ try {
107
+ let text = data.recommend_context;
108
+ let match_ret = data.recommend_pattern
109
+ ? new RegExp(data.recommend_pattern).exec(text)
110
+ : [text];
111
+ if (match_ret) {
112
+ for (let i = 0; i < viewOptions.length; i++) {
113
+ const viewOption = JSON.stringify(viewOptions[i]);
114
+ if (
115
+ (data.recommend_only_key &&
116
+ viewOptions[i].key == match_ret[match_ret.length - 1]) ||
117
+ (!data.recommend_only_key &&
118
+ viewOption.indexOf(match_ret[match_ret.length - 1]) != -1)
119
+ ) {
120
+ focusedIndex = i;
121
+ success(`从推荐上下文中获取 ${match_ret[match_ret.length - 1]}`);
122
+ break;
123
+ }
124
+ }
125
+ }
126
+ } catch (err) {
127
+ warn(err);
128
+ }
129
+ }
130
+ let numPerPage = OPTION_PAGE_SIZE;
131
+ function restrictFocusedIndex() {
132
+ if (focusedIndex < 0) {
133
+ focusedIndex = 0;
134
+ }
135
+ if (focusedIndex >= viewOptions.length) {
136
+ focusedIndex = viewOptions.length - 1;
137
+ }
138
+ }
139
+ function refreshOptions() {
140
+ showOptions(
141
+ viewOptions,
142
+ filteredViewOptions,
143
+ focusedIndex,
144
+ focusedIndex,
145
+ numPerPage,
146
+ undefined,
147
+ inputs.join("")
148
+ );
149
+ whisper(`单选: [↑↓方向键]切换 [ENTER回车键]确认`, undefined, true);
150
+ if (inputs.length) {
151
+ clearLine();
152
+ debug(inputs.join(""), "", true);
153
+ } else {
154
+ whisper("可输入使用搜索", "", true);
155
+ }
156
+ }
157
+ let selectedOption = await new Promise((resolve, reject) => {
158
+ // listen for the "keypress" event
159
+ /**
160
+ *
161
+ * @param {string} ch
162
+ * @param {{ctrl: boolean, alt: boolean, shift: boolean, name: string }} key
163
+ */
164
+ let onKeyPress = (ch, key) => {
165
+ let isKey = false;
166
+ let inputsChg = false;
167
+ if (key) {
168
+ if (key.name == "return" || key.name == "tab") {
169
+ isKey = true;
170
+ if (focusedIndex != -1) {
171
+ process.stdin.off("keypress", onKeyPress);
172
+ process.stdin.pause();
173
+ toggleRawMode(false);
174
+ log("");
175
+ let ret;
176
+ switch (data.select_mode) {
177
+ case "key": {
178
+ ret = optionKeys[focusedIndex];
179
+ break;
180
+ }
181
+ case "index": {
182
+ ret = focusedIndex;
183
+ break;
184
+ }
185
+ case "all": {
186
+ ret = {
187
+ index: focusedIndex,
188
+ key: optionKeys[focusedIndex],
189
+ value: optionValues[focusedIndex],
190
+ };
191
+ break;
192
+ }
193
+ case "value":
194
+ default: {
195
+ ret = optionValues[focusedIndex];
196
+ break;
197
+ }
198
+ }
199
+ resetOptions();
200
+ resolve(ret);
201
+ } else {
202
+ clearLine();
203
+ warn("\r" + `请选择有效项`, "", true);
204
+ }
205
+ return;
206
+ } else if (key.name == "backspace" || key.name == "delete") {
207
+ isKey = true;
208
+ inputsChg = true;
209
+ inputs.pop();
210
+ } else if (key.name == "left") {
211
+ isKey = true;
212
+ focusedIndex -= 1;
213
+ restrictFocusedIndex();
214
+ } else if (key.name == "right") {
215
+ isKey = true;
216
+ focusedIndex += 1;
217
+ restrictFocusedIndex();
218
+ } else if (key.name == "up") {
219
+ isKey = true;
220
+ let filterIdx = filteredViewOptions.indexOf(
221
+ viewOptions[focusedIndex]
222
+ );
223
+ focusedIndex = viewOptions.indexOf(
224
+ filteredViewOptions[Math.max(0, filterIdx - 1)]
225
+ );
226
+ restrictFocusedIndex();
227
+ } else if (key.name == "down") {
228
+ isKey = true;
229
+ let filterIdx = filteredViewOptions.indexOf(
230
+ viewOptions[focusedIndex]
231
+ );
232
+ focusedIndex = viewOptions.indexOf(
233
+ filteredViewOptions[
234
+ Math.min(filterIdx + 1, filteredViewOptions.length - 1)
235
+ ]
236
+ );
237
+ restrictFocusedIndex();
238
+ } else if (key.name == "pageup") {
239
+ isKey = true;
240
+ let filterIdx = filteredViewOptions.indexOf(
241
+ viewOptions[focusedIndex]
242
+ );
243
+ filterIdx = Math.max(0, filterIdx - numPerPage);
244
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[filterIdx]);
245
+ restrictFocusedIndex();
246
+ } else if (key.name == "pagedown") {
247
+ isKey = true;
248
+ let filterIdx = filteredViewOptions.indexOf(
249
+ viewOptions[focusedIndex]
250
+ );
251
+ filterIdx = Math.min(
252
+ filteredViewOptions.length - 1,
253
+ filterIdx + numPerPage
254
+ );
255
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[filterIdx]);
256
+ restrictFocusedIndex();
257
+ } else if (key.name == "home") {
258
+ isKey = true;
259
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
260
+ } else if (key.name == "end") {
261
+ isKey = true;
262
+ focusedIndex = viewOptions.indexOf(
263
+ filteredViewOptions[filteredViewOptions.length - 1]
264
+ );
265
+ } else if (key.ctrl && key.name == "c") {
266
+ throw "用户中止";
267
+ return;
268
+ } else if (key.name == "escape") {
269
+ isKey = true;
270
+ inputsChg = true;
271
+ inputs.length = 0;
272
+ } else if (key.ctrl) {
273
+ isKey = true;
274
+ }
275
+ }
276
+ if (!isKey && ch) {
277
+ inputs.push(ch);
278
+ inputsChg = true;
279
+ }
280
+ if (inputsChg) {
281
+ focusedIndex = -1;
282
+ let inputStr = inputs.join("");
283
+ let inputParts = inputStr && inputStr.split(/[, ]+/).filter((v) => v);
284
+ filteredViewOptions = !inputParts
285
+ ? viewOptions
286
+ : viewOptions.filter((option, i) => {
287
+ let valueStr = JSON.stringify(option.value);
288
+ return inputParts.some((part) => {
289
+ return (
290
+ part == i ||
291
+ part == option.key ||
292
+ valueStr.indexOf(part) != -1
293
+ );
294
+ });
295
+ });
296
+ if (filteredViewOptions.length) {
297
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
298
+ }
299
+ }
300
+ refreshOptions();
301
+ };
302
+ process.stdin.on("keypress", onKeyPress);
303
+ toggleRawMode(true);
304
+ process.stdin.resume();
305
+ refreshOptions();
306
+ // 全自动返回推荐选项
307
+ if (shareData.FULL_AUTOMATIC || automatic) {
308
+ onKeyPress(undefined, { name: "return" });
309
+ }
310
+ });
311
+ !data.quiet && success(`已选择:${getPrint(selectedOption, true)}`);
312
+ return selectedOption;
313
+ }
314
+
315
+ async function multiSelect(content, data) {
316
+ info("");
317
+ // 支持数组以及普通对象
318
+ let optionKeys = Object.keys(content);
319
+ // 过滤
320
+ let keyFilterRegExp =
321
+ data.key_filter_pattern && new RegExp(data.key_filter_pattern);
322
+ let valueFilterRegExp =
323
+ data.value_filter_pattern && new RegExp(data.value_filter_pattern);
324
+ optionKeys = optionKeys.filter((key) => {
325
+ let value = content[key];
326
+ return (
327
+ true &&
328
+ (!keyFilterRegExp || keyFilterRegExp.test(key)) &&
329
+ (!valueFilterRegExp || valueFilterRegExp.test(JSON.stringify(value)))
330
+ );
331
+ });
332
+ let optionValues = optionKeys.map((key) => content[key]);
333
+ /**
334
+ * @type {any[]}
335
+ */
336
+ const options = content;
337
+ const viewOptions = optionKeys.map((optionKey, index) => {
338
+ let optionValue = options[optionKey];
339
+ if (data.preview_handler) {
340
+ return {
341
+ key: optionKey,
342
+ value: eval_code(data.preview_handler)(
343
+ optionValue,
344
+ index,
345
+ optionValues
346
+ ),
347
+ };
348
+ }
349
+ return {
350
+ key: optionKey,
351
+ value: data.preview
352
+ ? toArray(data.preview)
353
+ .map((k, i) =>
354
+ i == -1 ? `${k}:${optionValue[k]}` : optionValue[k]
355
+ )
356
+ .join(" | ")
357
+ : typeof optionValue === "object"
358
+ ? JSON.stringify(optionValue)
359
+ : optionValue,
360
+ };
361
+ });
362
+ let filteredViewOptions = viewOptions;
363
+ let searchMaxChars = 20;
364
+ let inputs = [];
365
+ let focusedIndex = 0;
366
+ let confirmEmpty = false;
367
+ let selectedIndices = [];
368
+ let numPerPage = OPTION_PAGE_SIZE;
369
+ function restrictFocusedIndex() {
370
+ if (focusedIndex < 0) {
371
+ focusedIndex = 0;
372
+ }
373
+ if (focusedIndex >= viewOptions.length) {
374
+ focusedIndex = viewOptions.length - 1;
375
+ }
376
+ }
377
+ function refreshOptions() {
378
+ showOptions(
379
+ viewOptions,
380
+ filteredViewOptions,
381
+ selectedIndices,
382
+ focusedIndex,
383
+ numPerPage,
384
+ undefined,
385
+ inputs.join("")
386
+ );
387
+ whisper(
388
+ `多选: [↑↓方向键]切换 [TAB]选择/取消 [CTRL+A]选择全部/取消全部 [ENTER]确认`,
389
+ undefined,
390
+ true
391
+ );
392
+ if (inputs.length) {
393
+ clearLine();
394
+ debug(inputs.join(""), "", true);
395
+ } else {
396
+ whisper("可输入使用搜索", "", true);
397
+ }
398
+ }
399
+ let selectedOptions = await new Promise((resolve, reject) => {
400
+ // listen for the "keypress" event
401
+ /**
402
+ *
403
+ * @param {string} ch
404
+ * @param {{ctrl: boolean, alt: boolean, shift: boolean, name: string }} key
405
+ */
406
+ let onKeyPress = (ch, key) => {
407
+ let isKey = false;
408
+ let inputsChg = false;
409
+ if (key) {
410
+ if (key.name == "return") {
411
+ isKey = true;
412
+ if (!selectedIndices.length && !confirmEmpty) {
413
+ clearLine();
414
+ confirmEmpty = true;
415
+ warn("\r当前未选择任何一项,回车再次确认", "");
416
+ } else {
417
+ process.stdin.off("keypress", onKeyPress);
418
+ process.stdin.pause();
419
+ toggleRawMode(false);
420
+ log("");
421
+ let ret;
422
+ switch (data.select_mode) {
423
+ case "key": {
424
+ ret = selectedIndices.map((index) => optionKeys[index]);
425
+ break;
426
+ }
427
+ case "index": {
428
+ ret = selectedIndices;
429
+ break;
430
+ }
431
+ case "all": {
432
+ ret = selectedIndices.map((focusedIndex) => {
433
+ return {
434
+ index: focusedIndex,
435
+ key: optionKeys[focusedIndex],
436
+ value: optionValues[focusedIndex],
437
+ };
438
+ });
439
+ break;
440
+ }
441
+ case "value":
442
+ default: {
443
+ ret = selectedIndices.map((index) => optionValues[index]);
444
+ break;
445
+ }
446
+ }
447
+ resetOptions();
448
+ resolve(ret);
449
+ }
450
+ return;
451
+ } else if (key.name == "backspace" || key.name == "delete") {
452
+ isKey = true;
453
+ inputsChg = true;
454
+ inputs.pop();
455
+ } else if (key.name == "left") {
456
+ isKey = true;
457
+ focusedIndex -= 1;
458
+ restrictFocusedIndex();
459
+ } else if (key.name == "right") {
460
+ isKey = true;
461
+ focusedIndex += 1;
462
+ restrictFocusedIndex();
463
+ } else if (key.name == "up") {
464
+ isKey = true;
465
+ let filterIdx = filteredViewOptions.indexOf(
466
+ viewOptions[focusedIndex]
467
+ );
468
+ focusedIndex = viewOptions.indexOf(
469
+ filteredViewOptions[Math.max(0, filterIdx - 1)]
470
+ );
471
+ restrictFocusedIndex();
472
+ } else if (key.name == "down") {
473
+ isKey = true;
474
+ let filterIdx = filteredViewOptions.indexOf(
475
+ viewOptions[focusedIndex]
476
+ );
477
+ focusedIndex = viewOptions.indexOf(
478
+ filteredViewOptions[
479
+ Math.min(filterIdx + 1, filteredViewOptions.length - 1)
480
+ ]
481
+ );
482
+ restrictFocusedIndex();
483
+ } else if (key.name == "pageup") {
484
+ isKey = true;
485
+ let filterIdx = filteredViewOptions.indexOf(
486
+ viewOptions[focusedIndex]
487
+ );
488
+ filterIdx = Math.max(0, filterIdx - numPerPage);
489
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[filterIdx]);
490
+ restrictFocusedIndex();
491
+ } else if (key.name == "pagedown") {
492
+ isKey = true;
493
+ let filterIdx = filteredViewOptions.indexOf(
494
+ viewOptions[focusedIndex]
495
+ );
496
+ filterIdx = Math.min(
497
+ filteredViewOptions.length - 1,
498
+ filterIdx + numPerPage
499
+ );
500
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[filterIdx]);
501
+ restrictFocusedIndex();
502
+ } else if (key.name == "home") {
503
+ isKey = true;
504
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
505
+ } else if (key.name == "end") {
506
+ isKey = true;
507
+ focusedIndex = viewOptions.indexOf(
508
+ filteredViewOptions[filteredViewOptions.length - 1]
509
+ );
510
+ } else if (key.name == "tab") {
511
+ isKey = true;
512
+ if (focusedIndex != -1) {
513
+ let idx = selectedIndices.indexOf(focusedIndex);
514
+ if (idx != -1) {
515
+ selectedIndices.splice(idx, 1);
516
+ } else {
517
+ selectedIndices.push(focusedIndex);
518
+ }
519
+ }
520
+ } else if (key.ctrl && key.name == "a") {
521
+ isKey = true;
522
+ // 当前是否已经全部选中
523
+ let all_selected = filteredViewOptions.every((v) => {
524
+ return selectedIndices.includes(viewOptions.indexOf(v));
525
+ });
526
+ let will_select = !all_selected;
527
+ for (let i = 0; i < filteredViewOptions.length; i++) {
528
+ let idx = viewOptions.indexOf(filteredViewOptions[i]);
529
+ let selected_idx = selectedIndices.indexOf(idx);
530
+ let selected = selected_idx != -1;
531
+ if (will_select && !selected) {
532
+ selectedIndices.push(idx);
533
+ } else if (!will_select && selected) {
534
+ selectedIndices.splice(selected_idx, 1);
535
+ }
536
+ }
537
+ } else if (key.ctrl && key.name == "c") {
538
+ throw "用户中止";
539
+ return;
540
+ } else if (key.name == "escape") {
541
+ isKey = true;
542
+ inputsChg = true;
543
+ inputs.length = 0;
544
+ } else if (key.ctrl) {
545
+ isKey = true;
546
+ }
547
+ }
548
+ confirmEmpty = false;
549
+ if (!isKey && ch) {
550
+ inputs.push(ch);
551
+ inputsChg = true;
552
+ }
553
+ if (inputsChg) {
554
+ focusedIndex = 0;
555
+ let inputStr = inputs.join("");
556
+ let inputParts = inputStr && inputStr.split(/[, ]+/).filter((v) => v);
557
+ filteredViewOptions = !inputParts
558
+ ? viewOptions
559
+ : viewOptions.filter((option, i) => {
560
+ let valueStr = JSON.stringify(option.value);
561
+ return inputParts.some((part) => {
562
+ return (
563
+ part == i ||
564
+ part == option.key ||
565
+ valueStr.indexOf(part) != -1
566
+ );
567
+ });
568
+ });
569
+ if (inputStr && filteredViewOptions.length) {
570
+ focusedIndex = viewOptions.indexOf(filteredViewOptions[0]);
571
+ }
572
+ }
573
+ refreshOptions();
574
+ };
575
+ process.stdin.on("keypress", onKeyPress);
576
+ toggleRawMode(true);
577
+ process.stdin.resume();
578
+ refreshOptions();
579
+
580
+ // 全自动返回所有选项
581
+ if (shareData.FULL_AUTOMATIC || data.automatic) {
582
+ onKeyPress(undefined, { ctrl: true, name: "a" });
583
+ onKeyPress(undefined, { name: "return" });
584
+ }
585
+ });
586
+ success(`已选择:${getPrint(selectedOptions, true)}`);
587
+ return selectedOptions;
588
+ }
589
+
590
+ module.exports = {
591
+ select,
592
+ multiSelect,
593
+ };