@kikkimo/claude-launcher 2.4.0 → 3.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/CHANGELOG.md +71 -0
- package/README.md +32 -17
- package/claude-launcher +638 -393
- package/docs/README-zh.md +32 -17
- package/lib/api-manager.js +136 -11
- package/lib/auth/password-input.js +8 -4
- package/lib/auth/password-validator.js +83 -48
- package/lib/i18n/index.js +4 -3
- package/lib/i18n/language-manager.js +4 -3
- package/lib/i18n/locales/de.js +93 -9
- package/lib/i18n/locales/en.js +93 -9
- package/lib/i18n/locales/es.js +93 -9
- package/lib/i18n/locales/fr.js +93 -9
- package/lib/i18n/locales/it.js +93 -9
- package/lib/i18n/locales/ja.js +93 -9
- package/lib/i18n/locales/ko.js +93 -9
- package/lib/i18n/locales/pt.js +93 -9
- package/lib/i18n/locales/ru.js +93 -9
- package/lib/i18n/locales/zh-TW.js +93 -9
- package/lib/i18n/locales/zh.js +93 -9
- package/lib/launcher.js +131 -93
- package/lib/presets/providers.js +39 -18
- package/lib/ui/api-editor.js +210 -0
- package/lib/ui/interactive-table.js +216 -99
- package/lib/ui/menu.js +78 -55
- package/lib/ui/prompts.js +168 -139
- package/lib/ui/screen.js +125 -0
- package/lib/utils/stdin-manager.js +11 -9
- package/lib/utils/version-checker.js +63 -3
- package/package.json +2 -2
package/lib/ui/menu.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const colors = require('./colors');
|
|
7
7
|
const i18n = require('../i18n');
|
|
8
|
-
const { padStringToWidth } = require('../utils/string-width');
|
|
8
|
+
const { padStringToWidth, getStringWidth } = require('../utils/string-width');
|
|
9
9
|
const stdinManager = require('../utils/stdin-manager');
|
|
10
|
+
const screen = require('./screen');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Force cleanup stdin state before displaying any menu
|
|
@@ -27,7 +28,7 @@ function forceCleanupBeforeMenu() {
|
|
|
27
28
|
} catch (error) {
|
|
28
29
|
// Ignore cleanup errors but log for debugging
|
|
29
30
|
if (process.env.DEBUG_STDIN) {
|
|
30
|
-
|
|
31
|
+
screen.debug('[DEBUG] forceCleanupBeforeMenu error: ' + error.message);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
}
|
|
@@ -45,54 +46,73 @@ class Menu {
|
|
|
45
46
|
// Force cleanup stdin state before any menu display
|
|
46
47
|
forceCleanupBeforeMenu();
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
const lines = [];
|
|
50
|
+
lines.push('');
|
|
51
|
+
lines.push(colors.orange + ' ┌──────────────────────────────────────────────────────┐' + colors.reset);
|
|
52
|
+
lines.push(colors.orange + ' │' + colors.white + colors.bright + ' Claude Code Launcher ' + colors.orange + '│' + colors.reset);
|
|
53
|
+
lines.push(colors.orange + ' └──────────────────────────────────────────────────────┘' + colors.reset);
|
|
54
|
+
lines.push('');
|
|
55
|
+
lines.push(colors.gray + ' ' + i18n.tSync('navigation.use_arrows') + colors.reset);
|
|
56
|
+
lines.push('');
|
|
57
|
+
screen.render(lines);
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
61
|
* Display menu with current selection
|
|
61
|
-
* @param {boolean} clearScreen - Whether to clear screen before displaying (default: true)
|
|
62
62
|
* @param {string} versionInfo - Optional version info to display between banner and navigation
|
|
63
|
+
* @param {Function|null} hintCallback - Optional sync callback(selectedIndex) returning hint string or null
|
|
63
64
|
*/
|
|
64
|
-
displayMenu(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
console.log(colors.orange + ' │' + colors.white + colors.bright + ' Claude Code Launcher ' + colors.orange + '│' + colors.reset);
|
|
72
|
-
console.log(colors.orange + ' └──────────────────────────────────────────────────────┘' + colors.reset);
|
|
73
|
-
console.log('');
|
|
65
|
+
displayMenu(versionInfo = null, hintCallback = null) {
|
|
66
|
+
const lines = [];
|
|
67
|
+
lines.push('');
|
|
68
|
+
lines.push(colors.orange + ' ┌──────────────────────────────────────────────────────┐' + colors.reset);
|
|
69
|
+
lines.push(colors.orange + ' │' + colors.white + colors.bright + ' Claude Code Launcher ' + colors.orange + '│' + colors.reset);
|
|
70
|
+
lines.push(colors.orange + ' └──────────────────────────────────────────────────────┘' + colors.reset);
|
|
71
|
+
lines.push('');
|
|
74
72
|
|
|
75
|
-
// Display version info if provided (between banner and navigation tips, like Claude Code)
|
|
76
73
|
if (versionInfo) {
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
lines.push(versionInfo);
|
|
75
|
+
lines.push('');
|
|
79
76
|
}
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
lines.push(colors.gray + ' ' + i18n.tSync('navigation.use_arrows') + colors.reset);
|
|
79
|
+
lines.push('');
|
|
83
80
|
|
|
84
|
-
// Display menu options
|
|
85
81
|
this.menuOptions.forEach((option, index) => {
|
|
86
82
|
if (index === this.selectedIndex) {
|
|
87
|
-
|
|
88
|
-
const paddedOption = padStringToWidth(option, Math.max(40,
|
|
89
|
-
|
|
83
|
+
const displayWidth = getStringWidth(option);
|
|
84
|
+
const paddedOption = padStringToWidth(option, Math.max(40, displayWidth + 2));
|
|
85
|
+
lines.push(colors.orange + ' → ' + colors.black + colors.bgAmber + paddedOption + colors.reset);
|
|
90
86
|
} else {
|
|
91
|
-
|
|
87
|
+
lines.push(colors.gray + ' ' + option + colors.reset);
|
|
92
88
|
}
|
|
93
89
|
});
|
|
94
90
|
|
|
95
|
-
|
|
91
|
+
// Fixed 4-line hint area for stable menu height
|
|
92
|
+
const hintText = hintCallback ? hintCallback(this.selectedIndex) : null;
|
|
93
|
+
if (hintText) {
|
|
94
|
+
const hintLines = hintText.split('\n').slice(0, 4);
|
|
95
|
+
while (hintLines.length < 4) hintLines.push('');
|
|
96
|
+
lines.push('');
|
|
97
|
+
hintLines.forEach((line, i) => {
|
|
98
|
+
if (line === '') {
|
|
99
|
+
lines.push('');
|
|
100
|
+
} else if (i === 0) {
|
|
101
|
+
lines.push(colors.green + ' \u2139 ' + line + colors.reset);
|
|
102
|
+
} else {
|
|
103
|
+
lines.push(colors.gray + ' ' + line + colors.reset);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
lines.push('');
|
|
108
|
+
lines.push('');
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push('');
|
|
111
|
+
lines.push('');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
lines.push('');
|
|
115
|
+
screen.render(lines);
|
|
96
116
|
}
|
|
97
117
|
|
|
98
118
|
/**
|
|
@@ -105,20 +125,21 @@ class Menu {
|
|
|
105
125
|
|
|
106
126
|
/**
|
|
107
127
|
* Handle keyboard navigation
|
|
108
|
-
* @param {boolean} clearScreen - Whether to clear screen on initial display (default: true)
|
|
109
128
|
* @param {string} versionInfo - Optional version info to display
|
|
129
|
+
* @param {Function|null} hintCallback - Optional sync callback(selectedIndex) returning hint string or null
|
|
110
130
|
*/
|
|
111
|
-
async navigate(
|
|
131
|
+
async navigate(versionInfo = null, hintCallback = null) {
|
|
112
132
|
// Guard against empty menu to prevent NaN from modulo operations
|
|
113
133
|
if (!this.menuOptions || this.menuOptions.length === 0) {
|
|
114
|
-
|
|
134
|
+
screen.write(colors.yellow + ' Warning: No menu options available' + colors.reset + '\n');
|
|
115
135
|
return -1; // Return cancel/exit code
|
|
116
136
|
}
|
|
117
137
|
|
|
118
138
|
this.versionInfo = versionInfo; // Store for redrawing
|
|
139
|
+
this.hintCallback = hintCallback; // Store for redrawing
|
|
119
140
|
|
|
120
141
|
return new Promise((resolve, reject) => {
|
|
121
|
-
this.displayMenu(
|
|
142
|
+
this.displayMenu(versionInfo, hintCallback);
|
|
122
143
|
|
|
123
144
|
if (process.stdin.isTTY) {
|
|
124
145
|
const scope = stdinManager.acquire('raw', {
|
|
@@ -171,15 +192,16 @@ class Menu {
|
|
|
171
192
|
switch (key) {
|
|
172
193
|
case '\u001b[A': // Up arrow
|
|
173
194
|
this.selectedIndex = (this.selectedIndex - 1 + this.menuOptions.length) % this.menuOptions.length;
|
|
174
|
-
this.displayMenu(
|
|
195
|
+
this.displayMenu(this.versionInfo, this.hintCallback);
|
|
175
196
|
break;
|
|
176
197
|
|
|
177
198
|
case '\u001b[B': // Down arrow
|
|
178
199
|
this.selectedIndex = (this.selectedIndex + 1) % this.menuOptions.length;
|
|
179
|
-
this.displayMenu(
|
|
200
|
+
this.displayMenu(this.versionInfo, this.hintCallback);
|
|
180
201
|
break;
|
|
181
202
|
|
|
182
203
|
case '\r': // Enter
|
|
204
|
+
case ' ': // Space
|
|
183
205
|
safeResolve(this.selectedIndex);
|
|
184
206
|
break;
|
|
185
207
|
|
|
@@ -207,7 +229,7 @@ class Menu {
|
|
|
207
229
|
|
|
208
230
|
const rl = lineScope.createReadline();
|
|
209
231
|
|
|
210
|
-
|
|
232
|
+
screen.write(colors.yellow + ' ' + i18n.tSync('navigation.arrow_keys_not_available', this.menuOptions.length) + colors.reset + '\n');
|
|
211
233
|
|
|
212
234
|
rl.on('line', (input) => {
|
|
213
235
|
const choice = parseInt(input.trim());
|
|
@@ -220,7 +242,7 @@ class Menu {
|
|
|
220
242
|
lineScope.release();
|
|
221
243
|
resolve(-1);
|
|
222
244
|
} else {
|
|
223
|
-
|
|
245
|
+
screen.write(colors.red + ' Invalid selection. Please enter 1-' + this.menuOptions.length + '.' + colors.reset + '\n');
|
|
224
246
|
}
|
|
225
247
|
});
|
|
226
248
|
}
|
|
@@ -230,7 +252,7 @@ class Menu {
|
|
|
230
252
|
async selectFromList(title, items, activeIndex = -1) {
|
|
231
253
|
// Guard against empty items list to prevent NaN from modulo operations
|
|
232
254
|
if (!items || items.length === 0) {
|
|
233
|
-
|
|
255
|
+
screen.write(colors.yellow + ' Warning: No items available to select' + colors.reset + '\n');
|
|
234
256
|
return null; // Return null to indicate no selection
|
|
235
257
|
}
|
|
236
258
|
|
|
@@ -239,12 +261,12 @@ class Menu {
|
|
|
239
261
|
let selectedIndex = activeIndex >= 0 ? activeIndex : 0;
|
|
240
262
|
|
|
241
263
|
const displayList = () => {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
264
|
+
const lines = [];
|
|
265
|
+
lines.push('');
|
|
266
|
+
lines.push(colors.bright + colors.orange + `[*] ${title}` + colors.reset);
|
|
267
|
+
lines.push('');
|
|
268
|
+
lines.push(colors.gray + ' Use ↑↓ arrow keys to navigate, Enter to select, Esc to cancel' + colors.reset);
|
|
269
|
+
lines.push('');
|
|
248
270
|
|
|
249
271
|
items.forEach((item, index) => {
|
|
250
272
|
const isActive = index === activeIndex;
|
|
@@ -253,19 +275,20 @@ class Menu {
|
|
|
253
275
|
|
|
254
276
|
if (index === selectedIndex) {
|
|
255
277
|
const itemText = `${item.name}${activeIndicator}`;
|
|
256
|
-
const paddedItem = padStringToWidth(itemText, Math.max(40, itemText
|
|
257
|
-
|
|
278
|
+
const paddedItem = padStringToWidth(itemText, Math.max(40, getStringWidth(itemText) + 2));
|
|
279
|
+
lines.push(colors.orange + prefix + colors.black + colors.bgAmber + paddedItem + colors.reset);
|
|
258
280
|
if (item.details) {
|
|
259
281
|
item.details.forEach(detail => {
|
|
260
|
-
|
|
282
|
+
lines.push(colors.gray + ' ' + detail + colors.reset);
|
|
261
283
|
});
|
|
262
284
|
}
|
|
263
285
|
} else {
|
|
264
|
-
|
|
286
|
+
lines.push(colors.gray + prefix + `${item.name}${activeIndicator}` + colors.reset);
|
|
265
287
|
}
|
|
266
288
|
});
|
|
267
289
|
|
|
268
|
-
|
|
290
|
+
lines.push('');
|
|
291
|
+
screen.render(lines);
|
|
269
292
|
};
|
|
270
293
|
|
|
271
294
|
return new Promise((resolve, reject) => {
|
|
@@ -358,7 +381,7 @@ class Menu {
|
|
|
358
381
|
|
|
359
382
|
const rl = lineScope.createReadline();
|
|
360
383
|
|
|
361
|
-
|
|
384
|
+
screen.write(colors.yellow + ' Arrow keys not available. Enter item number (1-' + items.length + ') or q to cancel:' + colors.reset + '\n');
|
|
362
385
|
|
|
363
386
|
rl.on('line', (input) => {
|
|
364
387
|
const choice = parseInt(input.trim());
|
|
@@ -371,7 +394,7 @@ class Menu {
|
|
|
371
394
|
lineScope.release();
|
|
372
395
|
resolve(null);
|
|
373
396
|
} else {
|
|
374
|
-
|
|
397
|
+
screen.write(colors.red + ' Invalid selection. Please enter 1-' + items.length + '.' + colors.reset + '\n');
|
|
375
398
|
}
|
|
376
399
|
});
|
|
377
400
|
}
|