- 新增日志图标、分隔符和格式化工具函数 - 统一所有安装脚本的输出格式与视觉风格 - 使用统一的图标和配色方案优化交互体验 - 重构命令行输入提示样式,添加视觉标识
117 lines
3.0 KiB
JavaScript
117 lines
3.0 KiB
JavaScript
const readline = require('readline');
|
|
const { colors, ICONS, log } = require('./logger');
|
|
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout
|
|
});
|
|
|
|
rl.on('close', () => {
|
|
process.exit(0);
|
|
});
|
|
|
|
rl.on('error', (err) => {
|
|
log.error(`输入错误: ${err.message}`);
|
|
process.exit(1);
|
|
});
|
|
|
|
function ask(question, defaultValue = '') {
|
|
return new Promise((resolve) => {
|
|
const defaultHint = defaultValue ? `${colors.dim}(${defaultValue})${colors.reset}` : '';
|
|
const prompt = ` ${colors.cyan}${ICONS.pointer}${colors.reset} ${colors.bright}${question}${colors.reset} ${defaultHint}${colors.dim}:${colors.reset} `;
|
|
rl.question(prompt, (answer) => {
|
|
resolve(answer.trim() || defaultValue);
|
|
});
|
|
});
|
|
}
|
|
|
|
function askPassword(question) {
|
|
const prompt = ` ${colors.cyan}${ICONS.pointer}${colors.reset} ${colors.bright}${question}${colors.reset} ${colors.dim}:${colors.reset} `;
|
|
process.stdout.write(prompt);
|
|
|
|
return new Promise((resolve) => {
|
|
let password = '';
|
|
const stdin = process.stdin;
|
|
const stdout = process.stdout;
|
|
|
|
const cleanup = () => {
|
|
if (stdin.isTTY) {
|
|
try {
|
|
stdin.setRawMode(false);
|
|
} catch {}
|
|
}
|
|
stdin.removeListener('data', onData);
|
|
};
|
|
|
|
const onData = (data) => {
|
|
const char = data.toString();
|
|
const code = char.charCodeAt(0);
|
|
|
|
if (code === 10 || code === 13) {
|
|
cleanup();
|
|
stdout.write('\n');
|
|
resolve(password);
|
|
} else if (code === 3) {
|
|
cleanup();
|
|
stdout.write('\n');
|
|
log.warning('已取消');
|
|
process.exit(0);
|
|
} else if (code === 127 || code === 8) {
|
|
if (password.length > 0) {
|
|
password = password.slice(0, -1);
|
|
stdout.write('\b \b');
|
|
}
|
|
} else if (code >= 32) {
|
|
password += char;
|
|
stdout.write('*');
|
|
}
|
|
};
|
|
|
|
if (stdin.isTTY) {
|
|
stdin.setRawMode(true);
|
|
stdin.resume();
|
|
stdin.on('data', onData);
|
|
} else {
|
|
rl.question('', (answer) => {
|
|
resolve(answer.trim());
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
async function select(question, options, defaultIndex = 1) {
|
|
console.log('');
|
|
console.log(` ${colors.bright}${question}${colors.reset}`);
|
|
log.divider();
|
|
|
|
options.forEach((opt, idx) => {
|
|
const isDefault = idx + 1 === defaultIndex;
|
|
const marker = isDefault
|
|
? `${colors.cyan}${ICONS.pointer}${colors.reset}`
|
|
: ` `;
|
|
const label = isDefault
|
|
? `${colors.bright}${opt.label}${colors.reset}`
|
|
: `${colors.dim}${opt.label}${colors.reset}`;
|
|
const num = isDefault
|
|
? `${colors.cyan}${idx + 1}${colors.reset}`
|
|
: `${colors.dim}${idx + 1}${colors.reset}`;
|
|
console.log(` ${marker} ${num}. ${label}`);
|
|
});
|
|
|
|
const answer = await ask('请选择', String(defaultIndex));
|
|
const index = parseInt(answer) - 1;
|
|
|
|
return options[index]?.value || options[0].value;
|
|
}
|
|
|
|
function closeReadline() {
|
|
rl.close();
|
|
}
|
|
|
|
module.exports = {
|
|
ask,
|
|
askPassword,
|
|
select,
|
|
closeReadline,
|
|
};
|