优化卡死的问题

This commit is contained in:
yinsx
2025-12-20 14:13:59 +08:00
parent 3098796a08
commit ad60f8c3ec
7 changed files with 495 additions and 108 deletions

View File

@ -1,5 +1,9 @@
import color from "picocolors";
import { initKeypress, onKey } from "../keyboard.js";
import { initKeypress, onKey, stopKeypress } from "../keyboard.js";
function clearScreen() {
process.stdout.write('\x1Bc');
}
export function createStepUI(options) {
const { title, getSteps } = options;
@ -9,6 +13,7 @@ export function createStepUI(options) {
let completed = new Set();
let currentStep = 0;
let currentOption = 0;
let resolved = false;
function initResults() {
steps = typeof getSteps === "function" ? getSteps() : getSteps;
@ -16,6 +21,7 @@ export function createStepUI(options) {
completed = new Set();
currentStep = 0;
currentOption = 0;
resolved = false;
}
function renderNav() {
@ -35,13 +41,10 @@ export function createStepUI(options) {
return lines.join("\n");
}
// 计算可显示的行数(终端高度 - 标题/导航/提示占用的行数)
const termHeight = process.stdout.rows || 24;
const reservedLines = 8;
const maxVisible = Math.max(5, termHeight - reservedLines);
const maxVisible = Math.max(5, termHeight - 8);
const total = step.options.length;
// 计算滚动窗口
let start = 0;
if (total > maxVisible) {
start = Math.max(0, Math.min(currentOption - Math.floor(maxVisible / 2), total - maxVisible));
@ -71,38 +74,42 @@ export function createStepUI(options) {
}
function render() {
process.stdout.write('\x1Bc');
clearScreen();
console.log(color.bgCyan(color.black(` ${title} `)));
console.log("\n" + renderNav());
console.log(color.dim("\n← → 切换步骤 | ↑ ↓ 选择 | Space 选中 | Tab 提交 | Esc 返回\n"));
console.log(renderOptions());
}
function runInteractive() {
initResults();
initKeypress();
return new Promise(resolve => {
render();
onKey((str, key) => {
if (!key) return;
const step = steps[currentStep];
const optCount = step.options?.length || 0;
if (key.name === "left") {
if (currentStep > 0) { currentStep--; currentOption = 0; }
render();
} else if (key.name === "right") {
if (currentStep < steps.length - 1) { currentStep++; currentOption = 0; }
render();
} else if (key.name === "up") {
if (!optCount) return;
function handleKey(key, resolve) {
if (!key || resolved) return;
const step = steps[currentStep];
const optCount = step.options?.length || 0;
switch (key.name) {
case "left":
if (currentStep > 0) { currentStep--; currentOption = 0; }
render();
break;
case "right":
if (currentStep < steps.length - 1) { currentStep++; currentOption = 0; }
render();
break;
case "up":
if (optCount) {
currentOption = (currentOption - 1 + optCount) % optCount;
render();
} else if (key.name === "down") {
if (!optCount) return;
}
break;
case "down":
if (optCount) {
currentOption = (currentOption + 1) % optCount;
render();
} else if (key.name === "space") {
if (!optCount) return;
}
break;
case "space":
if (optCount) {
const opt = step.options[currentOption];
if (step.type === "multiselect") {
const list = results[currentStep];
@ -114,25 +121,48 @@ export function createStepUI(options) {
}
completed.add(currentStep);
render();
} else if (key.name === "return") {
if (!optCount) return;
if (step.type === "select") {
results[currentStep] = step.options[currentOption].value;
}
}
break;
case "return":
if (optCount && step.type === "select") {
results[currentStep] = step.options[currentOption].value;
completed.add(currentStep);
if (currentStep < steps.length - 1) { currentStep++; currentOption = 0; }
render();
} else if (key.name === "tab") {
resolve({ steps, results });
} else if (key.name === "escape" || (key.ctrl && key.name === "c")) {
resolve(null);
}
});
break;
case "tab":
resolved = true;
stopKeypress();
setImmediate(() => resolve({ steps, results }));
break;
case "escape":
resolved = true;
stopKeypress();
setImmediate(() => resolve(null));
break;
case "c":
if (key.ctrl) {
resolved = true;
stopKeypress();
setImmediate(() => resolve(null));
}
break;
}
}
function runInteractive() {
initResults();
initKeypress();
return new Promise(resolve => {
render();
onKey((str, key) => handleKey(key, resolve));
});
}
function showSummary(lines) {
console.clear();
clearScreen();
console.log(color.bgCyan(color.black(` ${title} `)));
console.log("\n" + color.green("配置完成!当前设置:"));
lines.forEach(line => console.log(" " + line));