增加转格式
This commit is contained in:
@ -55,7 +55,8 @@ export const steps = [
|
||||
{ value: "overwrite", label: "覆盖已存在文件(自动替换同名文件)" },
|
||||
{ value: "keepOriginal", label: "保留原文件(压缩后不删除源文件)" },
|
||||
{ value: "report", label: "生成压缩报告(输出详细的压缩统计信息)" },
|
||||
{ value: "silent", label: "静默模式(减少控制台输出信息)" }
|
||||
{ value: "silent", label: "静默模式(减少控制台输出信息)" },
|
||||
{ value: "gltfExtension", label: "修改glTF扩展(添加KHR_texture_basisu)", dynamic: true }
|
||||
],
|
||||
default: ["overwrite", "keepOriginal"]
|
||||
}
|
||||
|
||||
3
lib/ktx2/gltf.js
Normal file
3
lib/ktx2/gltf.js
Normal file
@ -0,0 +1,3 @@
|
||||
// 从 utils 导出,保持兼容
|
||||
export { hasGltfFile, checkRequiredFiles, modifyGltfContent } from "../utils/gltf.js";
|
||||
export { runGltfExtension } from "../gltf/index.js";
|
||||
@ -1,40 +1,49 @@
|
||||
import color from "picocolors";
|
||||
import { checkToktx, scanImages, compressAll } from "./compressor.js";
|
||||
import { runInteractive, showSummary } from "./ui.js";
|
||||
import { runGltfExtension } from "./gltf.js";
|
||||
import { stopKeypress, waitForKey } from "../keyboard.js";
|
||||
|
||||
export async function run() {
|
||||
// 检查 toktx
|
||||
checkToktx();
|
||||
|
||||
// 运行交互界面
|
||||
const result = await runInteractive();
|
||||
|
||||
// ESC 返回主菜单
|
||||
if (!result) return "back";
|
||||
|
||||
const [exts, quality, encoding, mipmap, outputOpts] = result;
|
||||
stopKeypress();
|
||||
|
||||
const { results } = result;
|
||||
const [exts, quality, encoding, mipmap, outputOpts] = results;
|
||||
const config = { exts, quality, encoding, mipmap, outputOpts };
|
||||
showSummary([
|
||||
"文件格式: " + config.exts.join(", "),
|
||||
"压缩程度: " + config.quality,
|
||||
"编码格式: " + config.encoding,
|
||||
"Mipmap: " + config.mipmap,
|
||||
"输出选项: " + config.outputOpts.join(", ")
|
||||
]);
|
||||
|
||||
// 显示配置摘要
|
||||
showSummary(config);
|
||||
|
||||
// 扫描文件
|
||||
const { images, cwd } = scanImages(exts);
|
||||
|
||||
if (images.length === 0) {
|
||||
console.log(color.yellow("当前目录没有匹配的图片"));
|
||||
return;
|
||||
await waitForKey();
|
||||
return "back";
|
||||
}
|
||||
|
||||
console.log(`📁 找到 ${color.cyan(images.length)} 个待转换文件\n`);
|
||||
|
||||
// 执行压缩
|
||||
console.log("📁 找到 " + color.cyan(images.length) + " 个待转换文件\n");
|
||||
const { total, failed } = await compressAll(images, config, cwd);
|
||||
|
||||
// 显示结果
|
||||
if (failed > 0) {
|
||||
console.log(color.yellow(`\n⚠️ 完成,但有 ${failed} 个文件失败`));
|
||||
console.log(color.yellow("\n⚠️ 完成,但有 " + failed + " 个文件失败"));
|
||||
} else {
|
||||
console.log(color.green("\n🎉 全部文件压缩完成!"));
|
||||
}
|
||||
|
||||
if (outputOpts.includes("gltfExtension")) {
|
||||
console.log(color.cyan("\n正在修改 glTF 文件..."));
|
||||
const { success, count } = runGltfExtension();
|
||||
if (success) console.log(color.green("✓ 已修改 " + count + " 个 glTF 文件"));
|
||||
}
|
||||
|
||||
await waitForKey();
|
||||
return "back";
|
||||
}
|
||||
|
||||
159
lib/ktx2/ui.js
159
lib/ktx2/ui.js
@ -1,151 +1,20 @@
|
||||
import color from "picocolors";
|
||||
import { createStepUI } from "../utils/stepui.js";
|
||||
import { steps } from "./config.js";
|
||||
import { initKeypress, onKey } from "../keyboard.js";
|
||||
import { hasGltfFile } from "./gltf.js";
|
||||
|
||||
// 存储结果和状态
|
||||
let results = [];
|
||||
let completed = new Set();
|
||||
let currentStep = 0;
|
||||
let currentOption = 0;
|
||||
|
||||
// 初始化结果
|
||||
export function initResults() {
|
||||
results = steps.map(s => s.type === "multiselect" ? [...s.default] : s.default);
|
||||
completed = new Set();
|
||||
currentStep = 0;
|
||||
currentOption = 0;
|
||||
}
|
||||
|
||||
// 渲染导航栏
|
||||
function renderNav() {
|
||||
const nav = steps.map((step, i) => {
|
||||
if (completed.has(i)) {
|
||||
return color.green(`☑ ${step.name}`);
|
||||
} else if (i === currentStep) {
|
||||
return color.bgCyan(color.black(` ${step.name} `));
|
||||
} else {
|
||||
return color.dim(`□ ${step.name}`);
|
||||
function getFilteredSteps() {
|
||||
const hasGltf = hasGltfFile();
|
||||
return steps.map(step => {
|
||||
if (step.name === "输出选项") {
|
||||
return { ...step, options: step.options.filter(opt => !opt.dynamic || hasGltf) };
|
||||
}
|
||||
});
|
||||
return `← ${nav.join(" ")} ${color.green("✓Submit")} →`;
|
||||
}
|
||||
|
||||
// 渲染选项列表
|
||||
function renderOptions() {
|
||||
const step = steps[currentStep];
|
||||
const lines = [];
|
||||
|
||||
lines.push(color.cyan(step.message));
|
||||
lines.push("");
|
||||
|
||||
step.options.forEach((opt, i) => {
|
||||
const isCurrent = i === currentOption;
|
||||
const isSelected = step.type === "multiselect"
|
||||
? results[currentStep].includes(opt.value)
|
||||
: results[currentStep] === opt.value;
|
||||
|
||||
let prefix;
|
||||
if (step.type === "multiselect") {
|
||||
prefix = isSelected ? color.green("◉ ") : "○ ";
|
||||
} else {
|
||||
prefix = isSelected ? color.green("● ") : "○ ";
|
||||
}
|
||||
|
||||
const cursor = isCurrent ? color.cyan("❯ ") : " ";
|
||||
const label = isCurrent ? color.cyan(opt.label) : opt.label;
|
||||
const check = isSelected ? color.green(" ✓") : "";
|
||||
|
||||
lines.push(`${cursor}${prefix}${label}${check}`);
|
||||
if (opt.hint) {
|
||||
lines.push(` ${color.dim(opt.hint)}`);
|
||||
}
|
||||
});
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
// 渲染整个界面
|
||||
function render() {
|
||||
console.clear();
|
||||
console.log(color.bgCyan(color.black(" KTX2 纹理压缩工具 ")));
|
||||
console.log("\n" + renderNav());
|
||||
console.log(color.dim("\n← → 切换步骤 | ↑ ↓ 选择 | Space 选中 | Tab 提交 | Esc 返回\n"));
|
||||
console.log(renderOptions());
|
||||
}
|
||||
|
||||
// 主交互循环
|
||||
export async function runInteractive() {
|
||||
initResults();
|
||||
initKeypress();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
render();
|
||||
|
||||
onKey((str, key) => {
|
||||
if (!key) return;
|
||||
|
||||
const step = steps[currentStep];
|
||||
const optCount = step.options.length;
|
||||
|
||||
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") {
|
||||
currentOption = (currentOption - 1 + optCount) % optCount;
|
||||
render();
|
||||
} else if (key.name === "down") {
|
||||
currentOption = (currentOption + 1) % optCount;
|
||||
render();
|
||||
} else if (key.name === "space") {
|
||||
const opt = step.options[currentOption];
|
||||
if (step.type === "multiselect") {
|
||||
const idx = results[currentStep].indexOf(opt.value);
|
||||
if (idx >= 0) {
|
||||
results[currentStep].splice(idx, 1);
|
||||
} else {
|
||||
results[currentStep].push(opt.value);
|
||||
}
|
||||
} else {
|
||||
results[currentStep] = opt.value;
|
||||
}
|
||||
completed.add(currentStep);
|
||||
render();
|
||||
} else if (key.name === "return") {
|
||||
if (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(results);
|
||||
} else if (key.name === "escape" || (key.ctrl && key.name === "c")) {
|
||||
resolve(null);
|
||||
}
|
||||
});
|
||||
return step;
|
||||
});
|
||||
}
|
||||
|
||||
// 显示配置摘要
|
||||
export function showSummary(config) {
|
||||
console.clear();
|
||||
console.log(color.bgCyan(color.black(" KTX2 纹理压缩工具 ")));
|
||||
console.log("\n" + color.green("配置完成!当前设置:"));
|
||||
console.log(` 文件格式: ${config.exts.join(", ")}`);
|
||||
console.log(` 压缩程度: ${config.quality}`);
|
||||
console.log(` 编码格式: ${config.encoding}`);
|
||||
console.log(` Mipmap: ${config.mipmap}`);
|
||||
console.log(` 输出选项: ${config.outputOpts.join(", ")}\n`);
|
||||
}
|
||||
const ui = createStepUI({
|
||||
title: "KTX2 纹理压缩工具",
|
||||
getSteps: getFilteredSteps
|
||||
});
|
||||
|
||||
export const { runInteractive, showSummary } = ui;
|
||||
|
||||
Reference in New Issue
Block a user