change-image-suffix 2.1.10 → 2.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.js +62 -108
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [2.1.11](https://github.com/GuoSirius/change-image-suffix/compare/v2.1.10...v2.1.11) (2026-05-25)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* bypass cmd.exe entirely — call node.exe directly from context menu ([ced93c4](https://github.com/GuoSirius/change-image-suffix/commit/ced93c498bc83ed68da59b539553d5b04c0ec1eb))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Chores
|
|
14
|
+
|
|
15
|
+
* update local settings permissions ([f97f6f6](https://github.com/GuoSirius/change-image-suffix/commit/f97f6f6a229fc7401291c4a1175f2184bf5e7ec1))
|
|
16
|
+
|
|
5
17
|
### [2.1.10](https://github.com/GuoSirius/change-image-suffix/compare/v2.1.9...v2.1.10) (2026-05-25)
|
|
6
18
|
|
|
7
19
|
|
package/dist/index.js
CHANGED
|
@@ -56,52 +56,9 @@ function installContextMenu() {
|
|
|
56
56
|
fs.copyFileSync(icoSource, icoTarget);
|
|
57
57
|
}
|
|
58
58
|
const iconPath = fs.existsSync(icoTarget) ? icoTarget : cisCmd;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const batContent = `
|
|
63
|
-
@echo off
|
|
64
|
-
chcp 65001 >nul
|
|
65
|
-
setlocal
|
|
66
|
-
|
|
67
|
-
REM Find cis command in PATH (take first match via goto)
|
|
68
|
-
set "CIS_CMD="
|
|
69
|
-
for /f "delims=" %%c in ('where cis.cmd 2^>nul') do set "CIS_CMD=%%c" & goto :cis_found
|
|
70
|
-
:cis_found
|
|
71
|
-
if "%CIS_CMD%"=="" for /f "delims=" %%c in ('where cis 2^>nul') do set "CIS_CMD=%%c" & goto :cis_found
|
|
72
|
-
if "%CIS_CMD%"=="" (
|
|
73
|
-
echo [change-image-suffix] cis command not found. Run: npm install -g change-image-suffix
|
|
74
|
-
pause
|
|
75
|
-
exit /b 1
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
if "%~1"=="" (
|
|
79
|
-
echo [change-image-suffix] Error: No format specified.
|
|
80
|
-
pause
|
|
81
|
-
exit /b 1
|
|
82
|
-
)
|
|
83
|
-
|
|
84
|
-
set "format=%~1"
|
|
85
|
-
shift
|
|
86
|
-
|
|
87
|
-
if "%~1"=="" (
|
|
88
|
-
echo [change-image-suffix] No files or directories to process.
|
|
89
|
-
pause
|
|
90
|
-
exit /b 1
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
:process
|
|
94
|
-
if "%~1"=="" goto :done
|
|
95
|
-
"%CIS_CMD%" -t %format% "%~1"
|
|
96
|
-
shift
|
|
97
|
-
goto :process
|
|
98
|
-
|
|
99
|
-
:done
|
|
100
|
-
pause
|
|
101
|
-
endlocal
|
|
102
|
-
goto :eof
|
|
103
|
-
`;
|
|
104
|
-
fs.writeFileSync(batPath, batContent, 'utf8');
|
|
59
|
+
// 绕过 cmd.exe 编码问题:直接用 node.exe 调用,避免 bat/cmd 的 GBK/Unicode 转换
|
|
60
|
+
const nodeExe = process.execPath;
|
|
61
|
+
const scriptPath = path.join(__dirname, 'index.js');
|
|
105
62
|
// ── 格式列表(webp 排第一,其他按常见程度排序)──
|
|
106
63
|
const formats = [
|
|
107
64
|
{ verb: 'webp', label: '🌀 WebP' },
|
|
@@ -110,43 +67,28 @@ goto :eof
|
|
|
110
67
|
{ verb: 'avif', label: '📺 AVIF' },
|
|
111
68
|
{ verb: 'tiff', label: '📋 TIFF' },
|
|
112
69
|
];
|
|
113
|
-
// ── 使用 ExtendedSubCommandsKey
|
|
114
|
-
// 文件右键和目录右键均使用 bat 脚本,混合选择时自动分类文件和目录
|
|
70
|
+
// ── 使用 ExtendedSubCommandsKey,直接调用 node.exe(无 bat 中转)──
|
|
115
71
|
const menuBases = [
|
|
116
72
|
{ base: 'HKCU\\Software\\Classes\\Directory\\Background\\shell\\cis', subMenu: 'Directory\\ContextMenus\\cis', arg: '-p "%V"' },
|
|
117
|
-
{ base: 'HKCU\\Software\\Classes\\Directory\\shell\\cis', subMenu: 'Directory\\ContextMenus\\cis_dir',
|
|
118
|
-
{ base: 'HKCU\\Software\\Classes\\*\\shell\\cis', subMenu: 'Directory\\ContextMenus\\cis_file',
|
|
73
|
+
{ base: 'HKCU\\Software\\Classes\\Directory\\shell\\cis', subMenu: 'Directory\\ContextMenus\\cis_dir', arg: '-p "%1"' },
|
|
74
|
+
{ base: 'HKCU\\Software\\Classes\\*\\shell\\cis', subMenu: 'Directory\\ContextMenus\\cis_file', arg: '-f "%1"' },
|
|
119
75
|
];
|
|
120
|
-
// 1.
|
|
76
|
+
// 1. 注册格式子菜单
|
|
121
77
|
const REG_ROOT = 'HKCU\\Software\\Classes\\';
|
|
122
78
|
for (const menu of menuBases) {
|
|
123
79
|
for (const fmt of formats) {
|
|
124
80
|
const shellKey = `${REG_ROOT}${menu.subMenu}\\shell\\${fmt.verb}`;
|
|
125
|
-
|
|
126
|
-
if (menu.useBat) {
|
|
127
|
-
// bat 脚本接收格式参数,%1 为 Windows 传入的文件/目录路径
|
|
128
|
-
cmd = `"${batPath}" ${fmt.verb} "%1"`;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
cmd = `"${cisCmd}" -t ${fmt.verb} ${menu.arg}`;
|
|
132
|
-
}
|
|
81
|
+
const cmd = `"${nodeExe}" "${scriptPath}" --pause -t ${fmt.verb} ${menu.arg}`;
|
|
133
82
|
execSync(`reg add "${shellKey}" /ve /d "${fmt.label}" /f`, { stdio: 'ignore' });
|
|
134
83
|
execSync(`reg add "${shellKey}" /v Icon /d "${iconPath}" /f`, { stdio: 'ignore' });
|
|
135
84
|
execSync(`reg add "${shellKey}\\command" /ve /d "${cmd}" /f`, { stdio: 'ignore' });
|
|
136
85
|
}
|
|
137
86
|
}
|
|
138
|
-
// 2.
|
|
87
|
+
// 2. 注册主菜单项
|
|
139
88
|
for (const menu of menuBases) {
|
|
140
89
|
execSync(`reg add "${menu.base}" /ve /d "🖼 转换图片 (cis)" /f`, { stdio: 'ignore' });
|
|
141
90
|
execSync(`reg add "${menu.base}" /v Icon /d "${iconPath}" /f`, { stdio: 'ignore' });
|
|
142
91
|
execSync(`reg add "${menu.base}" /v ExtendedSubCommandsKey /d "${menu.subMenu}" /f`, { stdio: 'ignore' });
|
|
143
|
-
// 使用 bat 的菜单(文件右键和目录右键):设置 command 接收文件路径 %1
|
|
144
|
-
// Windows 会将父命令收到的 %1 自动传递给子命令
|
|
145
|
-
if (menu.useBat) {
|
|
146
|
-
execSync(`reg add "${menu.base}\\command" /ve /d "cmd /c echo %1 > nul" /f`, { stdio: 'ignore' });
|
|
147
|
-
// 注意:不添加 AppliesTo 限制,让菜单始终显示
|
|
148
|
-
// bat 脚本会检查文件扩展名,自动忽略非图片文件
|
|
149
|
-
}
|
|
150
92
|
}
|
|
151
93
|
// 写入版本标记,用于检测 npm update 后自动刷新菜单
|
|
152
94
|
const versionFile = path.join(appDataDir, 'version.json');
|
|
@@ -657,61 +599,73 @@ async function main() {
|
|
|
657
599
|
const dirResult = await processDirs(options.multiPaths);
|
|
658
600
|
console.log('\n----------------------------------------');
|
|
659
601
|
console.log(`📊 转换完成!成功: ${fileResult.success + dirResult.success}, 失败: ${fileResult.fail + dirResult.fail}\n`);
|
|
660
|
-
return;
|
|
661
602
|
}
|
|
662
|
-
|
|
663
|
-
|
|
603
|
+
else if (options.multiFiles && options.multiFiles.length > 0) {
|
|
604
|
+
// ─── 单/多文件模式 ───
|
|
664
605
|
const result = await processFiles(options.multiFiles, '图片转换工具');
|
|
665
606
|
console.log('\n----------------------------------------\n');
|
|
666
607
|
console.log(`📊 转换完成!成功: ${result.success}, 失败: ${result.fail}\n`);
|
|
667
|
-
return;
|
|
668
608
|
}
|
|
669
|
-
|
|
670
|
-
|
|
609
|
+
else if (options.multiPaths) {
|
|
610
|
+
// ─── 多路径模式 ───
|
|
671
611
|
console.log(`\n🖼️ change-image-suffix - 批量转换工具\n`);
|
|
672
612
|
const result = await processDirs(options.multiPaths);
|
|
673
613
|
console.log('\n----------------------------------------');
|
|
674
614
|
console.log(`📊 转换完成!成功: ${result.success}, 失败: ${result.fail}\n`);
|
|
675
|
-
return;
|
|
676
615
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
}
|
|
688
|
-
console.log(`📋 找到 ${files.length} 个文件,准备开始转换...\n`);
|
|
689
|
-
let successCount = 0;
|
|
690
|
-
let failCount = 0;
|
|
691
|
-
const results = [];
|
|
692
|
-
for (const file of files) {
|
|
693
|
-
const relativePath = path.relative(options.directory, file);
|
|
694
|
-
process.stdout.write(` 处理中: ${relativePath} ... `);
|
|
695
|
-
const result = await convertImage(file, options.targetFormat, files);
|
|
696
|
-
if (result.success) {
|
|
697
|
-
const outputRelativePath = path.relative(options.directory, result.outputPath);
|
|
698
|
-
console.log(`✅ -> ${outputRelativePath}`);
|
|
699
|
-
results.push({ input: file, output: result.outputPath, status: 'success' });
|
|
700
|
-
successCount++;
|
|
616
|
+
else {
|
|
617
|
+
// ─── 目录批量模式 ───
|
|
618
|
+
console.log(`📂 目录: ${options.directory}`);
|
|
619
|
+
console.log(`🔁 递归: ${options.recursive ? `是 (深度: ${options.maxDepth === Infinity ? '无限制' : options.maxDepth})` : '否'}`);
|
|
620
|
+
console.log(`📄 后缀: ${options.extensions.join(', ')}`);
|
|
621
|
+
console.log(`🎯 目标格式: ${options.targetFormat}`);
|
|
622
|
+
console.log('\n----------------------------------------\n');
|
|
623
|
+
const files = getAllFiles(options.directory, options.extensions, options.recursive, 0, options.maxDepth, options.targetFormat);
|
|
624
|
+
if (files.length === 0) {
|
|
625
|
+
console.log('✅ 没有找到需要转换的图片文件。');
|
|
701
626
|
}
|
|
702
627
|
else {
|
|
703
|
-
console.log(
|
|
704
|
-
|
|
705
|
-
failCount
|
|
628
|
+
console.log(`📋 找到 ${files.length} 个文件,准备开始转换...\n`);
|
|
629
|
+
let successCount = 0;
|
|
630
|
+
let failCount = 0;
|
|
631
|
+
const results = [];
|
|
632
|
+
for (const file of files) {
|
|
633
|
+
const relativePath = path.relative(options.directory, file);
|
|
634
|
+
process.stdout.write(` 处理中: ${relativePath} ... `);
|
|
635
|
+
const result = await convertImage(file, options.targetFormat, files);
|
|
636
|
+
if (result.success) {
|
|
637
|
+
const outputRelativePath = path.relative(options.directory, result.outputPath);
|
|
638
|
+
console.log(`✅ -> ${outputRelativePath}`);
|
|
639
|
+
results.push({ input: file, output: result.outputPath, status: 'success' });
|
|
640
|
+
successCount++;
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
console.log(`❌ 失败 (${result.error})`);
|
|
644
|
+
results.push({ input: file, output: '', status: 'fail' });
|
|
645
|
+
failCount++;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
console.log('\n----------------------------------------');
|
|
649
|
+
console.log(`\n📊 转换完成!成功: ${successCount}, 失败: ${failCount}\n`);
|
|
650
|
+
if (failCount > 0) {
|
|
651
|
+
console.log('❌ 失败的文件:');
|
|
652
|
+
for (const r of results.filter(x => x.status === 'fail')) {
|
|
653
|
+
console.log(` - ${r.input}`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
706
656
|
}
|
|
707
657
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
658
|
+
// 右键菜单调用时暂停,让用户看到输出
|
|
659
|
+
if (process.argv.includes('--pause')) {
|
|
660
|
+
console.log('\n按任意键退出...');
|
|
661
|
+
process.stdin.setRawMode(true);
|
|
662
|
+
process.stdin.resume();
|
|
663
|
+
await new Promise(resolve => {
|
|
664
|
+
process.stdin.once('data', () => {
|
|
665
|
+
process.stdin.setRawMode(false);
|
|
666
|
+
resolve();
|
|
667
|
+
});
|
|
668
|
+
});
|
|
715
669
|
}
|
|
716
670
|
}
|
|
717
671
|
main().catch(console.error);
|