更新功能实现
This commit is contained in:
25
index.js
25
index.js
@ -1,14 +1,35 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
import color from "picocolors";
|
import color from "picocolors";
|
||||||
import { showMainMenu } from "./lib/menu.js";
|
import { execSync } from "child_process";
|
||||||
|
import { createRequire } from "module";
|
||||||
|
import { showMainMenu, setUpdateInfo } from "./lib/menu.js";
|
||||||
import { checkUpdate } from "./lib/update.js";
|
import { checkUpdate } from "./lib/update.js";
|
||||||
|
|
||||||
await checkUpdate();
|
const require = createRequire(import.meta.url);
|
||||||
|
const pkg = require("./package.json");
|
||||||
|
|
||||||
|
const updateResult = await checkUpdate();
|
||||||
|
if (updateResult) {
|
||||||
|
setUpdateInfo(updateResult);
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const selected = await showMainMenu();
|
const selected = await showMainMenu();
|
||||||
|
|
||||||
console.clear();
|
console.clear();
|
||||||
|
|
||||||
|
if (selected.isUpdate) {
|
||||||
|
console.log(color.cyan("\n正在更新...\n"));
|
||||||
|
try {
|
||||||
|
execSync(`npm i -g ${pkg.name}@latest`, { stdio: "inherit" });
|
||||||
|
console.log(color.green("\n✅ 更新完成!请重新运行 yinx\n"));
|
||||||
|
process.exit(0);
|
||||||
|
} catch {
|
||||||
|
console.log(color.red("\n❌ 更新失败,请手动运行: npm i -g " + pkg.name + "@latest\n"));
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log(color.cyan("\n正在启动: " + selected.name + "...\n"));
|
console.log(color.cyan("\n正在启动: " + selected.name + "...\n"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
42
lib/grid.js
42
lib/grid.js
@ -23,10 +23,15 @@ export async function gridSelect(options) {
|
|||||||
cols = 3,
|
cols = 3,
|
||||||
colWidth = 24,
|
colWidth = 24,
|
||||||
title = "",
|
title = "",
|
||||||
renderHeader = null
|
renderHeader = null,
|
||||||
|
updateInfo = null,
|
||||||
|
headerGap = 2, // header后的空行数
|
||||||
|
menuGap = 2, // 菜单和提示文字的间隔
|
||||||
|
rowGap = 1 // 每行菜单后的空行数
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
let current = 0;
|
let current = 0;
|
||||||
|
let onUpdate = updateInfo ? true : false; // 如果有更新,默认选中更新按钮
|
||||||
let resolved = false;
|
let resolved = false;
|
||||||
const rows = Math.ceil(items.length / cols);
|
const rows = Math.ceil(items.length / cols);
|
||||||
const termWidth = process.stdout.columns || 80;
|
const termWidth = process.stdout.columns || 80;
|
||||||
@ -37,19 +42,18 @@ export async function gridSelect(options) {
|
|||||||
clearScreen();
|
clearScreen();
|
||||||
|
|
||||||
if (renderHeader) {
|
if (renderHeader) {
|
||||||
console.log(renderHeader());
|
console.log(renderHeader(onUpdate));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("");
|
for (let i = 0; i < headerGap; i++) console.log("");
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
const titlePad = " ".repeat(Math.max(0, Math.floor((termWidth - title.length - 4) / 2)));
|
const titlePad = " ".repeat(Math.max(0, Math.floor((termWidth - title.length - 4) / 2)));
|
||||||
console.log(titlePad + color.bgMagenta(color.white(` ${title} `)));
|
console.log(titlePad + color.bgMagenta(color.white(` ${title} `)));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("");
|
|
||||||
console.log(pad + color.dim("↑ ↓ ← → 选择 | Enter 确认 | Esc 退出"));
|
console.log(pad + color.dim("↑ ↓ ← → 选择 | Enter 确认 | Esc 退出"));
|
||||||
console.log("\n");
|
for (let i = 0; i < menuGap; i++) console.log("");
|
||||||
|
|
||||||
for (let row = 0; row < rows; row++) {
|
for (let row = 0; row < rows; row++) {
|
||||||
let line = "";
|
let line = "";
|
||||||
@ -59,7 +63,7 @@ export async function gridSelect(options) {
|
|||||||
const idx = row * cols + col;
|
const idx = row * cols + col;
|
||||||
if (idx < items.length) {
|
if (idx < items.length) {
|
||||||
const item = items[idx];
|
const item = items[idx];
|
||||||
if (idx === current) {
|
if (!onUpdate && idx === current) {
|
||||||
line += color.cyan("[" + item.name + "]");
|
line += color.cyan("[" + item.name + "]");
|
||||||
} else {
|
} else {
|
||||||
line += " " + item.name + " ";
|
line += " " + item.name + " ";
|
||||||
@ -71,7 +75,7 @@ export async function gridSelect(options) {
|
|||||||
|
|
||||||
console.log(pad + line);
|
console.log(pad + line);
|
||||||
console.log(pad + color.dim(descLine));
|
console.log(pad + color.dim(descLine));
|
||||||
console.log("\n");
|
for (let i = 0; i < rowGap; i++) console.log("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,21 +91,35 @@ export async function gridSelect(options) {
|
|||||||
|
|
||||||
switch (key.name) {
|
switch (key.name) {
|
||||||
case "up":
|
case "up":
|
||||||
if (row > 0) { current -= cols; render(); }
|
if (onUpdate) {
|
||||||
|
// 已在更新按钮,不能再上
|
||||||
|
} else if (row === 0 && updateInfo) {
|
||||||
|
onUpdate = true; render();
|
||||||
|
} else if (row > 0) {
|
||||||
|
current -= cols; render();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "down":
|
case "down":
|
||||||
if (row < rows - 1 && current + cols < items.length) { current += cols; render(); }
|
if (onUpdate) {
|
||||||
|
onUpdate = false; render();
|
||||||
|
} else if (row < rows - 1 && current + cols < items.length) {
|
||||||
|
current += cols; render();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "left":
|
case "left":
|
||||||
if (col > 0) { current--; render(); }
|
if (!onUpdate && col > 0) { current--; render(); }
|
||||||
break;
|
break;
|
||||||
case "right":
|
case "right":
|
||||||
if (col < cols - 1 && current < items.length - 1) { current++; render(); }
|
if (!onUpdate && col < cols - 1 && current < items.length - 1) { current++; render(); }
|
||||||
break;
|
break;
|
||||||
case "return":
|
case "return":
|
||||||
resolved = true;
|
resolved = true;
|
||||||
stopKeypress();
|
stopKeypress();
|
||||||
setImmediate(() => resolve(items[current]));
|
if (onUpdate) {
|
||||||
|
setImmediate(() => resolve({ isUpdate: true }));
|
||||||
|
} else {
|
||||||
|
setImmediate(() => resolve(items[current]));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "escape":
|
case "escape":
|
||||||
resolved = true;
|
resolved = true;
|
||||||
|
|||||||
38
lib/menu.js
38
lib/menu.js
@ -1,6 +1,5 @@
|
|||||||
import color from "picocolors";
|
import color from "picocolors";
|
||||||
import boxen from "boxen";
|
import boxen from "boxen";
|
||||||
import figlet from "figlet";
|
|
||||||
import { gridSelect } from "./grid.js";
|
import { gridSelect } from "./grid.js";
|
||||||
import * as convertTool from "./convert/index.js";
|
import * as convertTool from "./convert/index.js";
|
||||||
import * as ktx2Tool from "./ktx2/index.js";
|
import * as ktx2Tool from "./ktx2/index.js";
|
||||||
@ -20,11 +19,7 @@ let poemConfig = {
|
|||||||
borderColor: "cyan",
|
borderColor: "cyan",
|
||||||
};
|
};
|
||||||
|
|
||||||
let titleConfig = {
|
let updateInfo = null;
|
||||||
text: "Zguiy Tool Box",
|
|
||||||
font: "Standard",
|
|
||||||
color: "magenta",
|
|
||||||
};
|
|
||||||
|
|
||||||
const tools = [
|
const tools = [
|
||||||
{ name: "格式转换", desc: "支持多种模型格式转换", tool: convertTool },
|
{ name: "格式转换", desc: "支持多种模型格式转换", tool: convertTool },
|
||||||
@ -47,10 +42,8 @@ export function setPoemStyle(style) {
|
|||||||
Object.assign(poemConfig, style);
|
Object.assign(poemConfig, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setTitle(text, font = "Standard", titleColor = "magenta") {
|
export function setUpdateInfo(info) {
|
||||||
titleConfig.text = text;
|
updateInfo = info;
|
||||||
titleConfig.font = font;
|
|
||||||
titleConfig.color = titleColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPoem() {
|
function renderPoem() {
|
||||||
@ -67,17 +60,21 @@ function renderPoem() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderTitle() {
|
function renderUpdateInfo(selected) {
|
||||||
const art = figlet.textSync(titleConfig.text, { font: titleConfig.font });
|
if (!updateInfo) return "";
|
||||||
const termWidth = process.stdout.columns || 80;
|
const btn = selected ? color.cyan("[ 立即更新 ]") :color.white(" 立即更新 ") ;
|
||||||
return art.split("\n").map(line => {
|
const msg = `📦 发现新版本: ${color.red(updateInfo.current)} → ${color.green(updateInfo.latest)} ${btn}`;
|
||||||
const pad = Math.max(0, Math.floor((termWidth - line.length) / 2));
|
return "\n" + boxen(color.yellow(msg), {
|
||||||
return " ".repeat(pad) + color[titleConfig.color](line);
|
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
||||||
}).join("\n");
|
borderStyle: "round",
|
||||||
|
borderColor: "green",
|
||||||
|
textAlignment: "center",
|
||||||
|
float: "center",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderHeader() {
|
function renderHeader(onUpdate) {
|
||||||
return renderPoem() + "\n\n" + renderTitle();
|
return renderPoem() + renderUpdateInfo(onUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function showMainMenu() {
|
export async function showMainMenu() {
|
||||||
@ -86,7 +83,8 @@ export async function showMainMenu() {
|
|||||||
cols: 3,
|
cols: 3,
|
||||||
colWidth: 24,
|
colWidth: 24,
|
||||||
renderHeader: renderHeader,
|
renderHeader: renderHeader,
|
||||||
|
updateInfo: updateInfo,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { tools, poemConfig, titleConfig };
|
export { tools, poemConfig };
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import color from "picocolors";
|
|
||||||
import { createRequire } from "module";
|
import { createRequire } from "module";
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
@ -6,17 +5,17 @@ const pkg = require("../package.json");
|
|||||||
|
|
||||||
export async function checkUpdate() {
|
export async function checkUpdate() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`https://registry.npmmirror.com/${pkg.name}/latest`, {
|
const res = await fetch(`https://registry.npmjs.org/${pkg.name}/latest`, {
|
||||||
signal: AbortSignal.timeout(3000)
|
signal: AbortSignal.timeout(3000)
|
||||||
});
|
});
|
||||||
if (!res.ok) return;
|
if (!res.ok) return null;
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const latest = data.version;
|
const latest = data.version;
|
||||||
if (latest && latest !== pkg.version && isNewer(latest, pkg.version)) {
|
if (latest && latest !== pkg.version && isNewer(latest, pkg.version)) {
|
||||||
console.log(color.yellow(`\n📦 发现新版本: ${color.red(pkg.version)} → ${color.green(latest)}`));
|
return { current: pkg.version, latest };
|
||||||
console.log(color.cyan(` 运行 ${color.bold("yinx upgrade")} 进行更新\n`));
|
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNewer(latest, current) {
|
function isNewer(latest, current) {
|
||||||
|
|||||||
29
package-lock.json
generated
29
package-lock.json
generated
@ -1,19 +1,18 @@
|
|||||||
{
|
{
|
||||||
"name": "@yinshuangxi/yinx-cli",
|
"name": "@yinshuangxi/yinx-cli",
|
||||||
"version": "1.0.7",
|
"version": "1.1.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@yinshuangxi/yinx-cli",
|
"name": "@yinshuangxi/yinx-cli",
|
||||||
"version": "1.0.7",
|
"version": "1.1.3",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gltf-transform/core": "^3.10.1",
|
"@gltf-transform/core": "^3.10.1",
|
||||||
"@gltf-transform/extensions": "^3.10.1",
|
"@gltf-transform/extensions": "^3.10.1",
|
||||||
"@gltf-transform/functions": "^3.10.1",
|
"@gltf-transform/functions": "^3.10.1",
|
||||||
"boxen": "^8.0.1",
|
"boxen": "^8.0.1",
|
||||||
"figlet": "^1.9.4",
|
|
||||||
"picocolors": "^1.1.1"
|
"picocolors": "^1.1.1"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -741,15 +740,6 @@
|
|||||||
"simple-swizzle": "^0.2.2"
|
"simple-swizzle": "^0.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/commander": {
|
|
||||||
"version": "14.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz",
|
|
||||||
"integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
@ -1132,21 +1122,6 @@
|
|||||||
],
|
],
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
"node_modules/figlet": {
|
|
||||||
"version": "1.9.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/figlet/-/figlet-1.9.4.tgz",
|
|
||||||
"integrity": "sha512-uN6QE+TrzTAHC1IWTyrc4FfGo2KH/82J8Jl1tyKB7+z5DBit/m3D++Iu5lg91qJMnQQ3vpJrj5gxcK/pk4R9tQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"commander": "^14.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"figlet": "bin/index.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 17.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yinshuangxi/yinx-cli",
|
"name": "@yinshuangxi/yinx-cli",
|
||||||
"version": "1.1.3",
|
"version": "1.1.5",
|
||||||
"description": "子归云工具箱-你想要的都在这",
|
"description": "子归云工具箱-你想要的都在这",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@ -37,7 +37,6 @@
|
|||||||
"@gltf-transform/extensions": "^3.10.1",
|
"@gltf-transform/extensions": "^3.10.1",
|
||||||
"@gltf-transform/functions": "^3.10.1",
|
"@gltf-transform/functions": "^3.10.1",
|
||||||
"boxen": "^8.0.1",
|
"boxen": "^8.0.1",
|
||||||
"figlet": "^1.9.4",
|
|
||||||
"picocolors": "^1.1.1"
|
"picocolors": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 78 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 82 KiB |
Reference in New Issue
Block a user