diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..3dc7ddb --- /dev/null +++ b/Readme.md @@ -0,0 +1,45 @@ +# Zguiy Tool Box + +子归云工具箱 - 3D模型与纹理处理工具集 + +## 🚀 快速开始 + +### 安装 + +```bash +npm -g i @yinshuangxi/yinx-cli +``` + +### 使用 + +在控制台输入以下命令启动工具箱: + +```bash +yinx +``` + +启动后,你将看到一个交互式菜单,使用键盘方向键或数字键选择需要的工具,然后按照提示进行操作。 + +## ✨ 功能特性 + +- 🎨 **格式转换**: 支持多种3D模型格式之间的相互转换 +- 📦 **KTX2纹理压缩**: 将图片转换为KTX2格式,优化WebGL/Three.js应用性能 +- 🔧 **glTF扩展**: 为glTF模型添加KHR_texture_basisu扩展,支持高效纹理加载 +- 🗜️ **模型压缩**: 压缩glTF/GLB模型,减小文件体积 +- 🖼️ **图片批量处理**: 批量裁剪、缩放和转换图片文件 +- 📷 **Sprite图集**: 合并多个图片为Sprite图集 +- 📊 **LOD生成器**: 为3D模型生成多级细节 +- 🔊 **音频压缩**: 压缩音频文件,优化资源大小 +- 🛠️ **项目脚手架**: 快速创建项目模板,提高开发效率 + +## 📖 项目简介 + +Zguiy Tool Box 是一个功能强大的命令行工具集,专注于3D模型和纹理的处理、转换与优化。它提供了一系列实用工具,帮助开发者高效地处理游戏和3D应用开发中的资源文件。 + +## 📄 许可证 + +MIT License + +--- + +**你我皆牛马,生在人世间,终日奔波苦,一刻不得闲。** \ No newline at end of file diff --git a/lib/scaffold/config.js b/lib/scaffold/config.js index 83087fb..22e9d62 100644 --- a/lib/scaffold/config.js +++ b/lib/scaffold/config.js @@ -5,154 +5,212 @@ export const projectTypes = [ { name: "全栈", desc: "前后端一体化" }, ]; -// 三级菜单 - 具体框架 -export const frameworks = { +// 框架选项 +export const frameworkOptions = { 前端: [ - { name: "React", desc: "Vite + React", value: "react-vite" }, - { name: "Vue", desc: "Vite + Vue", value: "vue-vite" }, + { value: "vue", label: "Vue(Vite + Vue)" }, + { value: "react", label: "React(Vite + React)" }, ], 后端: [ - { name: "Bun", desc: "Bun运行时", value: "bun" }, - { name: "NestJS", desc: "企业级框架", value: "nestjs" }, - { name: "Express", desc: "Express Generator", value: "express" }, - { name: "Koa", desc: "Koa Generator", value: "koa" }, - { name: "Egg", desc: "阿里Egg.js", value: "egg" }, - { name: "Midway", desc: "阿里Midway", value: "midway" }, - { name: "Fastify", desc: "高性能框架", value: "fastify" }, - { name: "AdonisJS", desc: "全功能MVC", value: "adonisjs" }, + { value: "bun", label: "Bun(Bun运行时)" }, + { value: "node", label: "Node.js(Node运行时)" }, ], 全栈: [ - { name: "Next.js", desc: "React全栈框架", value: "nextjs" }, - { name: "Nuxt", desc: "Vue全栈框架", value: "nuxt" }, + { value: "nextjs", label: "Next.js(React全栈框架)" }, + { value: "nuxt", label: "Nuxt(Vue全栈框架)" }, ], }; -// 前端组件配置步骤 -export const frontendSteps = [ - { - name: "路由", - type: "select", - message: "选择路由方案", - options: [ - { value: "none", label: "不需要路由" }, - { value: "react-router", label: "React Router(React项目)" }, - { value: "vue-router", label: "Vue Router(Vue项目)" }, - ], - default: "none" - }, - { - name: "状态管理", - type: "select", - message: "选择状态管理方案", - options: [ - { value: "none", label: "不需要状态管理" }, - { value: "zustand", label: "Zustand(轻量级,React推荐)" }, - { value: "pinia", label: "Pinia(Vue官方推荐)" }, - { value: "redux", label: "Redux Toolkit(大型项目)" }, - { value: "mobx", label: "MobX(响应式状态管理)" }, - ], - default: "none" - }, - { - name: "HTTP请求", - type: "select", - message: "选择HTTP请求库", - options: [ - { value: "none", label: "不需要HTTP库" }, - { value: "axios", label: "Axios(功能全面,拦截器支持)" }, - { value: "ky", label: "Ky(轻量级,基于Fetch)" }, - { value: "ofetch", label: "ofetch(Nuxt团队出品)" }, - ], - default: "none" - }, - { - name: "UI组件库", - type: "select", - message: "选择UI组件库", - options: [ - { value: "none", label: "不需要UI库" }, - { value: "antd", label: "Ant Design(企业级,React)" }, - { value: "element-plus", label: "Element Plus(饿了么,Vue)" }, - { value: "arco", label: "Arco Design(字节跳动)" }, - { value: "naive-ui", label: "Naive UI(Vue3原生)" }, - { value: "shadcn", label: "shadcn/ui(可定制,React)" }, - ], - default: "none" - }, - { - name: "CSS方案", - type: "select", - message: "选择CSS方案", - options: [ - { value: "none", label: "原生CSS" }, - { value: "tailwind", label: "Tailwind CSS(原子化CSS)" }, - { value: "unocss", label: "UnoCSS(即时原子化)" }, - { value: "sass", label: "Sass/SCSS(预处理器)" }, - { value: "less", label: "Less(预处理器)" }, - { value: "styled", label: "Styled Components(CSS-in-JS)" }, - ], - default: "none" - }, - { - name: "工具库", - type: "multiselect", - message: "选择常用工具库", - options: [ - { value: "lodash", label: "Lodash(工具函数库)" }, - { value: "dayjs", label: "Day.js(日期处理)" }, - { value: "iconify", label: "Iconify(图标库)" }, - { value: "vueuse", label: "VueUse(Vue组合式工具)" }, - { value: "ahooks", label: "ahooks(React Hooks库)" }, - ], - default: [] - }, - { - name: "表单验证", - type: "select", - message: "选择表单验证方案", - options: [ - { value: "none", label: "不需要表单验证" }, - { value: "zod", label: "Zod(TypeScript优先)" }, - { value: "yup", label: "Yup(声明式验证)" }, - { value: "vee-validate", label: "VeeValidate(Vue专用)" }, - { value: "react-hook-form", label: "React Hook Form" }, - ], - default: "none" - }, - { - name: "国际化", - type: "select", - message: "选择国际化方案", - options: [ - { value: "none", label: "不需要国际化" }, - { value: "i18next", label: "i18next(通用方案)" }, - { value: "vue-i18n", label: "Vue I18n(Vue专用)" }, - ], - default: "none" - }, - { - name: "代码规范", - type: "multiselect", - message: "选择代码规范工具", - options: [ - { value: "eslint", label: "ESLint(代码检查)" }, - { value: "prettier", label: "Prettier(代码格式化)" }, - { value: "husky", label: "Husky(Git Hooks)" }, - { value: "lint-staged", label: "lint-staged(暂存区检查)" }, - { value: "commitlint", label: "Commitlint(提交信息规范)" }, - ], - default: [] - }, - { - name: "其他", - type: "multiselect", - message: "选择其他功能", - options: [ - { value: "mock", label: "Mock.js(模拟数据)" }, - { value: "pwa", label: "PWA支持" }, - { value: "storage", label: "持久化存储封装" }, - { value: "env", label: "环境变量配置" }, - ], - default: [] +const bunComponents = { + 框架: [ + { value: "normal", label: "原生" }, + { value: "elysia", label: "Elysia(官方亲儿子)" }, + { value: "hono", label: "Hono(轻量级)" }, + ] +}; + +const nodeComponents = { + 框架: [ + { value: "nestjs", label: "NestJS(企业级框架)" }, + { value: "express", label: "Express(Express Generator)" }, + { value: "koa", label: "Koa(Koa Generator)" }, + { value: "egg", label: "Egg(阿里Egg.js)" }, + { value: "midway", label: "Midway(阿里Midway)" }, + { value: "fastify", label: "Fastify(高性能框架)" }, + { value: "adonisjs", label: "AdonisJS(全功能MVC)" }, + ] +}; +//后端公共框架 +const backendCommonComponents = { + 数据库: [ + { value: "none", label: "不需要数据库" }, + { value: "mongoose", label: "MongoDB + Mongoose" }, + { value: "prisma", label: "Prisma(支持 MySQL / PostgreSQL / SQLite)" }, + { value: "typeorm", label: "TypeORM(支持 MySQL / PostgreSQL 等)" }, + ] +}; + + + +// 组件选项 - 按框架分类 +const reactComponents = { + 路由: [ + { value: "none", label: "不需要路由" }, + { value: "react-router", label: "React Router" }, + { value: "tanstack-router", label: "TanStack Router" }, + ], + 状态管理: [ + { value: "none", label: "不需要状态管理" }, + { value: "zustand", label: "Zustand(轻量级推荐)" }, + { value: "redux", label: "Redux Toolkit" }, + { value: "jotai", label: "Jotai(原子化)" }, + { value: "mobx", label: "MobX" }, + ], + UI组件库: [ + { value: "none", label: "不需要UI库" }, + { value: "antd", label: "Ant Design" }, + { value: "arco", label: "Arco Design" }, + { value: "shadcn", label: "shadcn/ui" }, + { value: "mui", label: "Material UI" }, + ], + 表单验证: [ + { value: "none", label: "不需要表单验证" }, + { value: "react-hook-form", label: "React Hook Form" }, + { value: "formik", label: "Formik" }, + { value: "zod", label: "Zod" }, + ], + 工具库: [ + { value: "ahooks", label: "ahooks(React Hooks库)" }, + { value: "lodash", label: "Lodash" }, + { value: "dayjs", label: "Day.js" }, + { value: "iconify", label: "Iconify图标" }, + ], + 国际化: [ + { value: "none", label: "不需要国际化" }, + { value: "i18next", label: "i18next" }, + ], +}; + +const vueComponents = { + 路由: [ + { value: "none", label: "不需要路由" }, + { value: "vue-router", label: "Vue Router" }, + ], + 状态管理: [ + { value: "none", label: "不需要状态管理" }, + { value: "pinia", label: "Pinia(官方推荐)" }, + ], + UI组件库: [ + { value: "none", label: "不需要UI库" }, + { value: "element-plus", label: "Element Plus" }, + { value: "naive-ui", label: "Naive UI" }, + { value: "arco-vue", label: "Arco Design Vue" }, + { value: "ant-design-vue", label: "Ant Design Vue" }, + ], + 表单验证: [ + { value: "none", label: "不需要表单验证" }, + { value: "vee-validate", label: "VeeValidate" }, + { value: "vuelidate", label: "Vuelidate" }, + { value: "zod", label: "Zod" }, + ], + 工具库: [ + { value: "vueuse", label: "VueUse(组合式工具)" }, + { value: "lodash", label: "Lodash" }, + { value: "dayjs", label: "Day.js" }, + { value: "iconify", label: "Iconify图标" }, + ], 国际化: [ + { value: "none", label: "不需要国际化" }, + { value: "vue-i18n", label: "Vue-i18n" }, + ], +}; + +// 通用组件(React和Vue共用) +const commonComponents = { + HTTP请求: [ + { value: "none", label: "不需要HTTP库" }, + { value: "axios", label: "Axios" }, + { value: "ky", label: "Ky(轻量级)" }, + { value: "ofetch", label: "ofetch" }, + ], + CSS方案: [ + { value: "none", label: "原生CSS" }, + { value: "tailwind", label: "Tailwind CSS" }, + { value: "unocss", label: "UnoCSS" }, + { value: "sass", label: "Sass/SCSS" }, + { value: "less", label: "Less" }, + ], + 代码规范: [ + { value: "eslint", label: "ESLint" }, + { value: "prettier", label: "Prettier" }, + { value: "husky", label: "Husky" }, + { value: "lint-staged", label: "lint-staged" }, + ], + 其他: [ + { value: "mock", label: "Mock.js" }, + { value: "pwa", label: "PWA支持" }, + { value: "storage", label: "持久化存储" }, + { value: "threejs", label: "3D集成(Three.js)" }, + { value: "babylonjs", label: "3D集成(Babylon.js)" }, + ], +}; + +// 根据框架获取组件配置 +export function getComponentsByFramework(framework) { + if (!framework || framework === "none") return {}; + + const isReact = ["react", "nextjs"].includes(framework); + const isVue = ["vue", "nuxt"].includes(framework); + const isBun = framework === "bun"; + const isNode = framework === "node"; + + if (isReact) { + return { ...reactComponents, ...commonComponents }; + } else if (isVue) { + return { ...vueComponents, ...commonComponents }; + } else if (isBun) { + return { ...bunComponents, ...backendCommonComponents }; + } else if (isNode) { + return { ...nodeComponents, ...backendCommonComponents }; } -]; + + return {}; +} + +// 获取默认框架 +function getDefaultFramework(projectType) { + const opts = frameworkOptions[projectType]; + return opts?.[0]?.value || "none"; +} + +// 生成前端/全栈项目的步骤配置 +export function generateSteps(projectType, selectedFramework) { + const defaultFramework = getDefaultFramework(projectType); + const framework = selectedFramework || defaultFramework; + + const steps = [ + { + name: "运行时", + type: "select", + message: "选择项目框架", + options: frameworkOptions[projectType] || [], + default: defaultFramework + } + ]; + + const components = getComponentsByFramework(framework); + const componentNames = Object.keys(components); + + componentNames.forEach(name => { + const isMulti = ["工具库", "代码规范", "其他"].includes(name); + const opts = components[name] || []; + steps.push({ + name, + type: isMulti ? "multiselect" : "select", + message: `选择${name}`, + options: opts, + default: isMulti ? [] : opts[0]?.value + }); + }); + + return steps; +} diff --git a/lib/scaffold/index.js b/lib/scaffold/index.js index 4bcb0ac..16e26ed 100644 --- a/lib/scaffold/index.js +++ b/lib/scaffold/index.js @@ -1,6 +1,6 @@ import color from "picocolors"; -import { projectTypes, frameworks } from "./config.js"; -import { gridSelect, createComponentUI, formatResults, waitKey, showPlaceholder } from "./ui.js"; +import { projectTypes } from "./config.js"; +import { gridSelect, createScaffoldUI, formatResults, waitKey } from "./ui.js"; export async function run() { while (true) { @@ -9,31 +9,16 @@ export async function run() { if (typeResult.action === "back") return "back"; const projectType = typeResult.item.name; - const frameworkList = frameworks[projectType]; - while (true) { - // 三级菜单 - 框架选择 - const frameworkResult = await gridSelect(frameworkList, `${projectType}项目 - 选择框架`); - if (frameworkResult.action === "back") break; + // 三级页面 - 框架+组件配置(合并) + const ui = createScaffoldUI(projectType); + const result = await ui.runInteractive(); - const framework = frameworkResult.item; - - if (projectType === "前端" || projectType === "全栈") { - // 组件配置 - const ui = createComponentUI(framework.name); - const result = await ui.runInteractive(); - - if (result) { - const summary = formatResults(result.results); - ui.showSummary(summary); - console.log(color.yellow("功能开发中,敬请期待...")); - await waitKey(); - } - } else { - // 后端暂无组件配置 - showPlaceholder(framework); - await waitKey(); - } + if (result) { + const summary = formatResults(result.steps, result.results); + ui.showSummary(summary); + console.log(color.yellow("功能开发中,敬请期待...")); + await waitKey(); } } } diff --git a/lib/scaffold/ui.js b/lib/scaffold/ui.js index 3c63809..65bdf27 100644 --- a/lib/scaffold/ui.js +++ b/lib/scaffold/ui.js @@ -1,7 +1,7 @@ import color from "picocolors"; import { initKeypress, onKey, stopKeypress } from "../keyboard.js"; import { createStepUI } from "../utils/stepui.js"; -import { frontendSteps } from "./config.js"; +import { frameworkOptions, generateSteps } from "./config.js"; function clearScreen() { process.stdout.write('\x1Bc'); @@ -103,24 +103,24 @@ export async function gridSelect(items, title) { }); } -// 创建组件配置UI -export function createComponentUI(frameworkName) { +// 创建框架+组件配置UI +export function createScaffoldUI(projectType) { return createStepUI({ - title: `${frameworkName} - 组件配置`, - getSteps: () => frontendSteps + title: `${projectType} - 项目配置`, + getSteps: () => generateSteps(projectType, null), + onStepChange: (framework) => generateSteps(projectType, framework) }); } // 解析配置结果 -export function formatResults(results) { - const stepNames = frontendSteps.map(s => s.name); +export function formatResults(steps, results) { const summary = []; results.forEach((val, i) => { if (Array.isArray(val) && val.length > 0) { - summary.push(`${stepNames[i]}: ${val.join(", ")}`); + summary.push(`${steps[i].name}: ${val.join(", ")}`); } else if (val && val !== "none") { - summary.push(`${stepNames[i]}: ${val}`); + summary.push(`${steps[i].name}: ${val}`); } }); @@ -139,14 +139,3 @@ export async function waitKey(message = "按任意键返回") { }); }); } - -// 显示占位信息 -export function showPlaceholder(framework) { - clearScreen(); - console.log(""); - console.log(color.bgGreen(color.black(" 配置完成 "))); - console.log(""); - console.log(color.cyan("框架: ") + framework.name); - console.log(""); - console.log(color.yellow("功能开发中,敬请期待...")); -} diff --git a/lib/utils/stepui.js b/lib/utils/stepui.js index a0779b5..61dade9 100644 --- a/lib/utils/stepui.js +++ b/lib/utils/stepui.js @@ -6,7 +6,7 @@ function clearScreen() { } export function createStepUI(options) { - const { title, getSteps } = options; + const { title, getSteps, onStepChange } = options; let steps = []; let results = []; @@ -19,16 +19,33 @@ export function createStepUI(options) { steps = typeof getSteps === "function" ? getSteps() : getSteps; results = steps.map(s => s.type === "multiselect" ? [...(s.default || [])] : s.default); completed = new Set(); + // 有默认值的步骤自动标记为已完成 + steps.forEach((s, i) => { + if (s.default && s.default !== "none" && (!Array.isArray(s.default) || s.default.length > 0)) { + completed.add(i); + } + }); currentStep = 0; currentOption = 0; resolved = false; } + function updateSteps(newSteps) { + steps = newSteps; + // 保留第一个步骤的结果,重置其他步骤为默认值 + const firstResult = results[0]; + results = steps.map((s, i) => { + if (i === 0) return firstResult; + return s.type === "multiselect" ? [...(s.default || [])] : s.default; + }); + if (currentStep >= steps.length) currentStep = steps.length - 1; + } + function renderNav() { const nav = steps.map((step, i) => { - if (completed.has(i)) return color.green("☑ " + step.name); + if (step.disabled) return color.dim("□ " + step.name); if (i === currentStep) return color.bgCyan(color.black(" " + step.name + " ")); - return color.dim("□ " + step.name); + return color.dim(step.name); }); return "← " + nav.join(" ") + " " + color.green("✓Submit") + " →"; } @@ -81,6 +98,20 @@ export function createStepUI(options) { console.log(renderOptions()); } + function findPrevStep(from) { + for (let i = from - 1; i >= 0; i--) { + if (!steps[i].disabled) return i; + } + return from; + } + + function findNextStep(from) { + for (let i = from + 1; i < steps.length; i++) { + if (!steps[i].disabled) return i; + } + return from; + } + function handleKey(key, resolve) { if (!key || resolved) return; @@ -89,11 +120,13 @@ export function createStepUI(options) { switch (key.name) { case "left": - if (currentStep > 0) { currentStep--; currentOption = 0; } + const prevStep = findPrevStep(currentStep); + if (prevStep !== currentStep) { currentStep = prevStep; currentOption = 0; } render(); break; case "right": - if (currentStep < steps.length - 1) { currentStep++; currentOption = 0; } + const nextStep = findNextStep(currentStep); + if (nextStep !== currentStep) { currentStep = nextStep; currentOption = 0; } render(); break; case "up": @@ -120,6 +153,10 @@ export function createStepUI(options) { results[currentStep] = opt.value; } completed.add(currentStep); + if (onStepChange && currentStep === 0) { + const newSteps = onStepChange(results[0]); + if (newSteps) updateSteps(newSteps); + } render(); } break; @@ -127,7 +164,12 @@ export function createStepUI(options) { if (optCount && step.type === "select") { results[currentStep] = step.options[currentOption].value; completed.add(currentStep); - if (currentStep < steps.length - 1) { currentStep++; currentOption = 0; } + if (onStepChange && currentStep === 0) { + const newSteps = onStepChange(results[0]); + if (newSteps) updateSteps(newSteps); + } + const next = findNextStep(currentStep); + if (next !== currentStep) { currentStep = next; currentOption = 0; } render(); } break; diff --git a/package.json b/package.json index 3253f8a..7589be9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@yinshuangxi/yinx-cli", - "version": "1.0.3", + "version": "1.0.4", "description": "子归云工具箱-你想要的都在这", "main": "dist/index.js", "type": "module", diff --git a/xw_20251220142204.png b/xw_20251220142204.png new file mode 100644 index 0000000..5e0d91a Binary files /dev/null and b/xw_20251220142204.png differ