@spark-apps/piclet 1.0.0 → 1.0.1
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/LICENSE +661 -21
- package/Readme.md +3 -2
- package/dist/cli.js +773 -594
- package/dist/cli.js.map +1 -1
- package/dist/gui/css/theme.css +1 -1
- package/dist/gui/js/piclet.js +10 -1
- package/dist/gui/loading.hta +5 -12
- package/dist/gui/piclet.html +31 -25
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import
|
|
4
|
+
import chalk12 from "chalk";
|
|
5
|
+
|
|
6
|
+
// src/cli/index.ts
|
|
7
|
+
import chalk11 from "chalk";
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
|
|
10
10
|
// src/lib/banner.ts
|
|
@@ -36,6 +36,9 @@ ${renderLogo()}`);
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
// src/cli/commands/config.ts
|
|
40
|
+
import chalk from "chalk";
|
|
41
|
+
|
|
39
42
|
// src/lib/config.ts
|
|
40
43
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
41
44
|
import { dirname, join } from "path";
|
|
@@ -87,200 +90,46 @@ function resetConfig() {
|
|
|
87
90
|
writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
// src/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const rest = match[2].replace(/\\/g, "/");
|
|
100
|
-
return `/mnt/${drive}/${rest}`;
|
|
101
|
-
}
|
|
102
|
-
return winPath;
|
|
103
|
-
}
|
|
104
|
-
function wslToWindows(wslPath) {
|
|
105
|
-
const match = wslPath.match(/^\/mnt\/([a-z])\/(.*)$/i);
|
|
106
|
-
if (match) {
|
|
107
|
-
const drive = match[1].toUpperCase();
|
|
108
|
-
const rest = match[2].replace(/\//g, "\\");
|
|
109
|
-
return `${drive}:\\${rest}`;
|
|
110
|
-
}
|
|
111
|
-
return wslPath;
|
|
112
|
-
}
|
|
113
|
-
function normalizePath(inputPath) {
|
|
114
|
-
if (/^[A-Za-z]:[/\\]/.test(inputPath)) {
|
|
115
|
-
return windowsToWsl(inputPath);
|
|
116
|
-
}
|
|
117
|
-
return resolve(inputPath);
|
|
118
|
-
}
|
|
119
|
-
function getFileInfo(filePath) {
|
|
120
|
-
const dir = dirname2(filePath);
|
|
121
|
-
const base = basename(filePath);
|
|
122
|
-
const ext = extname(filePath);
|
|
123
|
-
const name = base.slice(0, -ext.length);
|
|
124
|
-
return {
|
|
125
|
-
dirname: dir,
|
|
126
|
-
basename: base,
|
|
127
|
-
filename: name,
|
|
128
|
-
extension: ext
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// src/lib/prompts.ts
|
|
133
|
-
import prompts from "prompts";
|
|
134
|
-
var useDefaults = false;
|
|
135
|
-
var overrides = {};
|
|
136
|
-
function setUseDefaults(value) {
|
|
137
|
-
useDefaults = value;
|
|
138
|
-
}
|
|
139
|
-
function setOverrides(values) {
|
|
140
|
-
Object.assign(overrides, values);
|
|
141
|
-
}
|
|
142
|
-
function clearOverrides() {
|
|
143
|
-
for (const key of Object.keys(overrides)) {
|
|
144
|
-
delete overrides[key];
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
function getOverride(message) {
|
|
148
|
-
const msgLower = message.toLowerCase();
|
|
149
|
-
for (const [key, value] of Object.entries(overrides)) {
|
|
150
|
-
if (msgLower.includes(key.toLowerCase())) {
|
|
151
|
-
return value;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return void 0;
|
|
155
|
-
}
|
|
156
|
-
async function number(message, defaultValue, min, max) {
|
|
157
|
-
const override = getOverride(message);
|
|
158
|
-
if (override !== void 0) return Number(override);
|
|
159
|
-
if (useDefaults) return defaultValue ?? 0;
|
|
160
|
-
const response = await prompts({
|
|
161
|
-
type: "number",
|
|
162
|
-
name: "value",
|
|
163
|
-
message,
|
|
164
|
-
initial: defaultValue,
|
|
165
|
-
min,
|
|
166
|
-
max
|
|
167
|
-
});
|
|
168
|
-
return response.value ?? defaultValue ?? 0;
|
|
169
|
-
}
|
|
170
|
-
async function confirm(message, defaultValue = true) {
|
|
171
|
-
const override = getOverride(message);
|
|
172
|
-
if (override !== void 0) return Boolean(override);
|
|
173
|
-
if (useDefaults) return defaultValue;
|
|
174
|
-
const response = await prompts({
|
|
175
|
-
type: "confirm",
|
|
176
|
-
name: "value",
|
|
177
|
-
message,
|
|
178
|
-
initial: defaultValue
|
|
179
|
-
});
|
|
180
|
-
return response.value ?? defaultValue;
|
|
181
|
-
}
|
|
182
|
-
async function select(message, options, defaultValue) {
|
|
183
|
-
if (useDefaults) return defaultValue ?? options[0]?.value ?? null;
|
|
184
|
-
const response = await prompts({
|
|
185
|
-
type: "select",
|
|
186
|
-
name: "value",
|
|
187
|
-
message,
|
|
188
|
-
choices: options.map((opt) => ({
|
|
189
|
-
title: opt.title,
|
|
190
|
-
value: opt.value,
|
|
191
|
-
description: opt.description
|
|
192
|
-
}))
|
|
93
|
+
// src/cli/commands/config.ts
|
|
94
|
+
function registerConfigCommand(program2) {
|
|
95
|
+
const configCmd = program2.command("config").description("Display current settings").action(() => {
|
|
96
|
+
const config7 = loadConfig();
|
|
97
|
+
console.log(chalk.white.bold("\n PicLet Configuration"));
|
|
98
|
+
console.log(chalk.gray(` ${getConfigPath()}
|
|
99
|
+
`));
|
|
100
|
+
console.log(JSON.stringify(config7, null, 2));
|
|
101
|
+
console.log();
|
|
193
102
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (useDefaults) return defaultValues ?? [];
|
|
198
|
-
const response = await prompts({
|
|
199
|
-
type: "multiselect",
|
|
200
|
-
name: "value",
|
|
201
|
-
message,
|
|
202
|
-
choices: options.map((opt) => ({
|
|
203
|
-
title: opt.title,
|
|
204
|
-
value: opt.value,
|
|
205
|
-
description: opt.description
|
|
206
|
-
})),
|
|
207
|
-
instructions: false,
|
|
208
|
-
hint: "- Space to select, Enter to confirm"
|
|
103
|
+
configCmd.command("reset").description("Restore defaults").action(() => {
|
|
104
|
+
resetConfig();
|
|
105
|
+
console.log(chalk.green("Configuration reset to defaults."));
|
|
209
106
|
});
|
|
210
|
-
return response.value ?? [];
|
|
211
107
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
type: "text",
|
|
218
|
-
name: "value",
|
|
219
|
-
message
|
|
108
|
+
|
|
109
|
+
// src/cli/commands/help.ts
|
|
110
|
+
function registerHelpCommand(program2) {
|
|
111
|
+
program2.command("help").description("Show help").action(() => {
|
|
112
|
+
showHelp();
|
|
220
113
|
});
|
|
221
114
|
}
|
|
222
115
|
|
|
223
|
-
// src/
|
|
224
|
-
import
|
|
225
|
-
import { dirname as dirname3 } from "path";
|
|
226
|
-
import { fileURLToPath } from "url";
|
|
227
|
-
import { promisify } from "util";
|
|
228
|
-
var execAsync = promisify(exec);
|
|
229
|
-
function isWSL() {
|
|
230
|
-
return process.platform === "linux" && (process.env.WSL_DISTRO_NAME !== void 0 || process.env.WSLENV !== void 0);
|
|
231
|
-
}
|
|
232
|
-
async function addRegistryKey(keyPath, valueName, value, type = "REG_SZ") {
|
|
233
|
-
const valueArg = valueName ? `/v "${valueName}"` : "/ve";
|
|
234
|
-
const escapedValue = value.replace(/"/g, '\\"');
|
|
235
|
-
const cmd = `reg.exe add "${keyPath}" ${valueArg} /t ${type} /d "${escapedValue}" /f`;
|
|
236
|
-
try {
|
|
237
|
-
await execAsync(cmd);
|
|
238
|
-
return true;
|
|
239
|
-
} catch (error2) {
|
|
240
|
-
const message = error2.message;
|
|
241
|
-
const ignoredErrors = ["Exec format error", "not found"];
|
|
242
|
-
const shouldIgnore = ignoredErrors.some(
|
|
243
|
-
(e) => message.toLowerCase().includes(e.toLowerCase())
|
|
244
|
-
);
|
|
245
|
-
if (!shouldIgnore) {
|
|
246
|
-
console.error(`Failed to add registry key: ${keyPath}`);
|
|
247
|
-
console.error(message);
|
|
248
|
-
}
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
async function deleteRegistryKey(keyPath) {
|
|
253
|
-
const cmd = `reg.exe delete "${keyPath}" /f`;
|
|
254
|
-
try {
|
|
255
|
-
await execAsync(cmd);
|
|
256
|
-
return true;
|
|
257
|
-
} catch (error2) {
|
|
258
|
-
const message = error2.message;
|
|
259
|
-
const ignoredErrors = ["unable to find", "Exec format error", "not found"];
|
|
260
|
-
const shouldIgnore = ignoredErrors.some(
|
|
261
|
-
(e) => message.toLowerCase().includes(e.toLowerCase())
|
|
262
|
-
);
|
|
263
|
-
if (!shouldIgnore) {
|
|
264
|
-
console.error(`Failed to delete registry key: ${keyPath}`);
|
|
265
|
-
}
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
116
|
+
// src/cli/commands/iconpack.ts
|
|
117
|
+
import chalk3 from "chalk";
|
|
269
118
|
|
|
270
119
|
// src/tools/iconpack.ts
|
|
271
120
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
272
|
-
import { basename as basename2, dirname as
|
|
121
|
+
import { basename as basename2, dirname as dirname6 } from "path";
|
|
273
122
|
|
|
274
123
|
// src/lib/gui-server.ts
|
|
275
124
|
import { spawn } from "child_process";
|
|
276
125
|
import { createServer } from "http";
|
|
277
|
-
import { dirname as
|
|
278
|
-
import { fileURLToPath
|
|
126
|
+
import { dirname as dirname3, join as join3 } from "path";
|
|
127
|
+
import { fileURLToPath } from "url";
|
|
279
128
|
import express from "express";
|
|
280
129
|
|
|
281
130
|
// src/lib/presets.ts
|
|
282
131
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
283
|
-
import { dirname as
|
|
132
|
+
import { dirname as dirname2, join as join2 } from "path";
|
|
284
133
|
import { homedir as homedir2 } from "os";
|
|
285
134
|
var BUILT_IN_PRESETS = [
|
|
286
135
|
// ── Game Asset Stores ──
|
|
@@ -414,7 +263,7 @@ function getPresetsPath() {
|
|
|
414
263
|
}
|
|
415
264
|
function ensurePresetsDir() {
|
|
416
265
|
const presetsPath = getPresetsPath();
|
|
417
|
-
const dir =
|
|
266
|
+
const dir = dirname2(presetsPath);
|
|
418
267
|
if (!existsSync2(dir)) {
|
|
419
268
|
mkdirSync2(dir, { recursive: true });
|
|
420
269
|
}
|
|
@@ -491,15 +340,20 @@ function deletePreset(id) {
|
|
|
491
340
|
}
|
|
492
341
|
|
|
493
342
|
// src/lib/gui-server.ts
|
|
494
|
-
var __dirname2 =
|
|
343
|
+
var __dirname2 = dirname3(fileURLToPath(import.meta.url));
|
|
495
344
|
function signalReady() {
|
|
496
|
-
spawn("
|
|
345
|
+
spawn("powershell.exe", ["-WindowStyle", "Hidden", "-Command", "New-Item -Path $env:TEMP\\piclet-ready.tmp -ItemType File -Force | Out-Null"], {
|
|
497
346
|
stdio: "ignore",
|
|
498
347
|
windowsHide: true
|
|
499
348
|
});
|
|
500
349
|
}
|
|
501
350
|
function openAppWindow(url) {
|
|
502
|
-
spawn("
|
|
351
|
+
spawn("powershell.exe", [
|
|
352
|
+
"-WindowStyle",
|
|
353
|
+
"Hidden",
|
|
354
|
+
"-Command",
|
|
355
|
+
`Start-Process msedge -ArgumentList '--app=${url}'`
|
|
356
|
+
], {
|
|
503
357
|
detached: true,
|
|
504
358
|
stdio: "ignore",
|
|
505
359
|
windowsHide: true
|
|
@@ -618,6 +472,19 @@ function startGuiServer(options) {
|
|
|
618
472
|
res.json({ success: false, error: err.message });
|
|
619
473
|
}
|
|
620
474
|
});
|
|
475
|
+
app.post("/api/open-url", (req, res) => {
|
|
476
|
+
const { url } = req.body;
|
|
477
|
+
if (!url || typeof url !== "string") {
|
|
478
|
+
res.json({ success: false, error: "Missing URL" });
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
spawn("powershell.exe", ["-WindowStyle", "Hidden", "-Command", `Start-Process '${url}'`], {
|
|
482
|
+
detached: true,
|
|
483
|
+
stdio: "ignore",
|
|
484
|
+
windowsHide: true
|
|
485
|
+
}).unref();
|
|
486
|
+
res.json({ success: true });
|
|
487
|
+
});
|
|
621
488
|
function shutdown() {
|
|
622
489
|
setTimeout(() => {
|
|
623
490
|
server?.close();
|
|
@@ -698,11 +565,11 @@ function clearLine() {
|
|
|
698
565
|
}
|
|
699
566
|
|
|
700
567
|
// src/lib/magick.ts
|
|
701
|
-
import { exec
|
|
568
|
+
import { exec } from "child_process";
|
|
702
569
|
import { copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, unlinkSync } from "fs";
|
|
703
|
-
import { dirname as
|
|
704
|
-
import { promisify
|
|
705
|
-
var
|
|
570
|
+
import { dirname as dirname4 } from "path";
|
|
571
|
+
import { promisify } from "util";
|
|
572
|
+
var execAsync = promisify(exec);
|
|
706
573
|
function getInputSelector(imagePath) {
|
|
707
574
|
const lowerPath = imagePath.toLowerCase();
|
|
708
575
|
if (lowerPath.endsWith(".ico")) {
|
|
@@ -712,7 +579,7 @@ function getInputSelector(imagePath) {
|
|
|
712
579
|
}
|
|
713
580
|
async function checkImageMagick() {
|
|
714
581
|
try {
|
|
715
|
-
await
|
|
582
|
+
await execAsync("command -v convert");
|
|
716
583
|
return true;
|
|
717
584
|
} catch {
|
|
718
585
|
return false;
|
|
@@ -721,7 +588,7 @@ async function checkImageMagick() {
|
|
|
721
588
|
async function getDimensions(imagePath) {
|
|
722
589
|
try {
|
|
723
590
|
const input = getInputSelector(imagePath);
|
|
724
|
-
const { stdout } = await
|
|
591
|
+
const { stdout } = await execAsync(
|
|
725
592
|
`convert ${input} -ping -format "%w %h" info:`
|
|
726
593
|
);
|
|
727
594
|
const [w, h] = stdout.trim().split(" ").map(Number);
|
|
@@ -734,7 +601,7 @@ async function getDimensions(imagePath) {
|
|
|
734
601
|
async function getBorderColor(imagePath) {
|
|
735
602
|
try {
|
|
736
603
|
const input = getInputSelector(imagePath);
|
|
737
|
-
const { stdout } = await
|
|
604
|
+
const { stdout } = await execAsync(
|
|
738
605
|
`convert ${input} -format "%[pixel:u.p{0,0}]" info:`
|
|
739
606
|
);
|
|
740
607
|
return stdout.trim();
|
|
@@ -745,7 +612,7 @@ async function getBorderColor(imagePath) {
|
|
|
745
612
|
async function trim(inputPath, outputPath) {
|
|
746
613
|
try {
|
|
747
614
|
const input = getInputSelector(inputPath);
|
|
748
|
-
await
|
|
615
|
+
await execAsync(`convert ${input} -trim +repage "${outputPath}"`);
|
|
749
616
|
return true;
|
|
750
617
|
} catch {
|
|
751
618
|
return false;
|
|
@@ -764,7 +631,7 @@ async function squarify2(inputPath, outputPath) {
|
|
|
764
631
|
const size = Math.max(width, height);
|
|
765
632
|
const input = getInputSelector(inputPath);
|
|
766
633
|
try {
|
|
767
|
-
await
|
|
634
|
+
await execAsync(
|
|
768
635
|
`convert ${input} -background none -gravity center -extent ${size}x${size} "${outputPath}"`
|
|
769
636
|
);
|
|
770
637
|
return true;
|
|
@@ -774,7 +641,7 @@ async function squarify2(inputPath, outputPath) {
|
|
|
774
641
|
}
|
|
775
642
|
async function scaleToSize(inputPath, outputPath, size) {
|
|
776
643
|
try {
|
|
777
|
-
await
|
|
644
|
+
await execAsync(
|
|
778
645
|
`convert "${inputPath}" -resize ${size}x${size} -background none -gravity center -extent ${size}x${size} "${outputPath}"`
|
|
779
646
|
);
|
|
780
647
|
return true;
|
|
@@ -784,7 +651,7 @@ async function scaleToSize(inputPath, outputPath, size) {
|
|
|
784
651
|
}
|
|
785
652
|
async function scaleWithPadding(inputPath, outputPath, width, height) {
|
|
786
653
|
try {
|
|
787
|
-
await
|
|
654
|
+
await execAsync(
|
|
788
655
|
`convert "${inputPath}" -resize ${width}x${height} -background none -gravity center -extent ${width}x${height} "${outputPath}"`
|
|
789
656
|
);
|
|
790
657
|
return true;
|
|
@@ -794,7 +661,7 @@ async function scaleWithPadding(inputPath, outputPath, width, height) {
|
|
|
794
661
|
}
|
|
795
662
|
async function resize(inputPath, outputPath, width, height) {
|
|
796
663
|
try {
|
|
797
|
-
await
|
|
664
|
+
await execAsync(
|
|
798
665
|
`convert "${inputPath}" -resize ${width}x${height}! "${outputPath}"`
|
|
799
666
|
);
|
|
800
667
|
return true;
|
|
@@ -804,7 +671,7 @@ async function resize(inputPath, outputPath, width, height) {
|
|
|
804
671
|
}
|
|
805
672
|
async function scaleFillCrop(inputPath, outputPath, width, height) {
|
|
806
673
|
try {
|
|
807
|
-
await
|
|
674
|
+
await execAsync(
|
|
808
675
|
`convert "${inputPath}" -resize ${width}x${height}^ -background none -gravity center -extent ${width}x${height} "${outputPath}"`
|
|
809
676
|
);
|
|
810
677
|
return true;
|
|
@@ -815,7 +682,7 @@ async function scaleFillCrop(inputPath, outputPath, width, height) {
|
|
|
815
682
|
async function removeBackground(inputPath, outputPath, color, fuzz) {
|
|
816
683
|
try {
|
|
817
684
|
const input = getInputSelector(inputPath);
|
|
818
|
-
await
|
|
685
|
+
await execAsync(
|
|
819
686
|
`convert ${input} -fuzz ${fuzz}% -transparent "${color}" "${outputPath}"`
|
|
820
687
|
);
|
|
821
688
|
return true;
|
|
@@ -826,7 +693,7 @@ async function removeBackground(inputPath, outputPath, color, fuzz) {
|
|
|
826
693
|
async function removeBackgroundBorderOnly(inputPath, outputPath, color, fuzz) {
|
|
827
694
|
try {
|
|
828
695
|
const input = getInputSelector(inputPath);
|
|
829
|
-
await
|
|
696
|
+
await execAsync(
|
|
830
697
|
`convert ${input} -bordercolor "${color}" -border 1x1 -fill none -fuzz ${fuzz}% -draw "matte 0,0 floodfill" -shave 1x1 "${outputPath}"`
|
|
831
698
|
);
|
|
832
699
|
return true;
|
|
@@ -837,7 +704,7 @@ async function removeBackgroundBorderOnly(inputPath, outputPath, color, fuzz) {
|
|
|
837
704
|
async function createIco(inputPath, outputPath, sizes = [256, 128, 64, 48, 32, 16]) {
|
|
838
705
|
try {
|
|
839
706
|
const sizeStr = sizes.join(",");
|
|
840
|
-
await
|
|
707
|
+
await execAsync(
|
|
841
708
|
`convert "${inputPath}" -define icon:auto-resize=${sizeStr} "${outputPath}"`
|
|
842
709
|
);
|
|
843
710
|
return true;
|
|
@@ -848,7 +715,7 @@ async function createIco(inputPath, outputPath, sizes = [256, 128, 64, 48, 32, 1
|
|
|
848
715
|
async function createIcoFromMultiple(pngPaths, outputPath) {
|
|
849
716
|
try {
|
|
850
717
|
const inputs = pngPaths.map((p) => `"${p}"`).join(" ");
|
|
851
|
-
await
|
|
718
|
+
await execAsync(`convert ${inputs} "${outputPath}"`);
|
|
852
719
|
return true;
|
|
853
720
|
} catch {
|
|
854
721
|
return false;
|
|
@@ -865,71 +732,204 @@ function cleanup(...files) {
|
|
|
865
732
|
}
|
|
866
733
|
}
|
|
867
734
|
|
|
868
|
-
// src/
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
];
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
];
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
735
|
+
// src/lib/paths.ts
|
|
736
|
+
import { basename, dirname as dirname5, extname, resolve } from "path";
|
|
737
|
+
function windowsToWsl(winPath) {
|
|
738
|
+
if (winPath.startsWith("/mnt/")) {
|
|
739
|
+
return winPath;
|
|
740
|
+
}
|
|
741
|
+
const match = winPath.match(/^([A-Za-z]):[/\\](.*)$/);
|
|
742
|
+
if (match) {
|
|
743
|
+
const drive = match[1].toLowerCase();
|
|
744
|
+
const rest = match[2].replace(/\\/g, "/");
|
|
745
|
+
return `/mnt/${drive}/${rest}`;
|
|
746
|
+
}
|
|
747
|
+
return winPath;
|
|
748
|
+
}
|
|
749
|
+
function wslToWindows(wslPath) {
|
|
750
|
+
const match = wslPath.match(/^\/mnt\/([a-z])\/(.*)$/i);
|
|
751
|
+
if (match) {
|
|
752
|
+
const drive = match[1].toUpperCase();
|
|
753
|
+
const rest = match[2].replace(/\//g, "\\");
|
|
754
|
+
return `${drive}:\\${rest}`;
|
|
755
|
+
}
|
|
756
|
+
return wslPath;
|
|
757
|
+
}
|
|
758
|
+
function normalizePath(inputPath) {
|
|
759
|
+
if (/^[A-Za-z]:[/\\]/.test(inputPath)) {
|
|
760
|
+
return windowsToWsl(inputPath);
|
|
761
|
+
}
|
|
762
|
+
return resolve(inputPath);
|
|
763
|
+
}
|
|
764
|
+
function getFileInfo(filePath) {
|
|
765
|
+
const dir = dirname5(filePath);
|
|
766
|
+
const base = basename(filePath);
|
|
767
|
+
const ext = extname(filePath);
|
|
768
|
+
const name = base.slice(0, -ext.length);
|
|
769
|
+
return {
|
|
770
|
+
dirname: dir,
|
|
771
|
+
basename: base,
|
|
772
|
+
filename: name,
|
|
773
|
+
extension: ext
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// src/lib/prompts.ts
|
|
778
|
+
import prompts from "prompts";
|
|
779
|
+
var useDefaults = false;
|
|
780
|
+
var overrides = {};
|
|
781
|
+
function setUseDefaults(value) {
|
|
782
|
+
useDefaults = value;
|
|
783
|
+
}
|
|
784
|
+
function setOverrides(values) {
|
|
785
|
+
Object.assign(overrides, values);
|
|
786
|
+
}
|
|
787
|
+
function clearOverrides() {
|
|
788
|
+
for (const key of Object.keys(overrides)) {
|
|
789
|
+
delete overrides[key];
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function getOverride(message) {
|
|
793
|
+
const msgLower = message.toLowerCase();
|
|
794
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
795
|
+
if (msgLower.includes(key.toLowerCase())) {
|
|
796
|
+
return value;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
return void 0;
|
|
800
|
+
}
|
|
801
|
+
async function number(message, defaultValue, min, max) {
|
|
802
|
+
const override = getOverride(message);
|
|
803
|
+
if (override !== void 0) return Number(override);
|
|
804
|
+
if (useDefaults) return defaultValue ?? 0;
|
|
805
|
+
const response = await prompts({
|
|
806
|
+
type: "number",
|
|
807
|
+
name: "value",
|
|
808
|
+
message,
|
|
809
|
+
initial: defaultValue,
|
|
810
|
+
min,
|
|
811
|
+
max
|
|
812
|
+
});
|
|
813
|
+
return response.value ?? defaultValue ?? 0;
|
|
814
|
+
}
|
|
815
|
+
async function confirm(message, defaultValue = true) {
|
|
816
|
+
const override = getOverride(message);
|
|
817
|
+
if (override !== void 0) return Boolean(override);
|
|
818
|
+
if (useDefaults) return defaultValue;
|
|
819
|
+
const response = await prompts({
|
|
820
|
+
type: "confirm",
|
|
821
|
+
name: "value",
|
|
822
|
+
message,
|
|
823
|
+
initial: defaultValue
|
|
824
|
+
});
|
|
825
|
+
return response.value ?? defaultValue;
|
|
826
|
+
}
|
|
827
|
+
async function select(message, options, defaultValue) {
|
|
828
|
+
if (useDefaults) return defaultValue ?? options[0]?.value ?? null;
|
|
829
|
+
const response = await prompts({
|
|
830
|
+
type: "select",
|
|
831
|
+
name: "value",
|
|
832
|
+
message,
|
|
833
|
+
choices: options.map((opt) => ({
|
|
834
|
+
title: opt.title,
|
|
835
|
+
value: opt.value,
|
|
836
|
+
description: opt.description
|
|
837
|
+
}))
|
|
838
|
+
});
|
|
839
|
+
return response.value ?? null;
|
|
840
|
+
}
|
|
841
|
+
async function multiSelect(message, options, defaultValues) {
|
|
842
|
+
if (useDefaults) return defaultValues ?? [];
|
|
843
|
+
const response = await prompts({
|
|
844
|
+
type: "multiselect",
|
|
845
|
+
name: "value",
|
|
846
|
+
message,
|
|
847
|
+
choices: options.map((opt) => ({
|
|
848
|
+
title: opt.title,
|
|
849
|
+
value: opt.value,
|
|
850
|
+
description: opt.description
|
|
851
|
+
})),
|
|
852
|
+
instructions: false,
|
|
853
|
+
hint: "- Space to select, Enter to confirm"
|
|
854
|
+
});
|
|
855
|
+
return response.value ?? [];
|
|
856
|
+
}
|
|
857
|
+
prompts.override({});
|
|
858
|
+
async function pauseOnError(message = "Press Enter to close...") {
|
|
859
|
+
if (!useDefaults) return;
|
|
860
|
+
console.log();
|
|
861
|
+
await prompts({
|
|
862
|
+
type: "text",
|
|
863
|
+
name: "value",
|
|
864
|
+
message
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/tools/iconpack.ts
|
|
869
|
+
var WEB_ICONS = [
|
|
870
|
+
{ filename: "favicon-16x16.png", size: 16 },
|
|
871
|
+
{ filename: "favicon-32x32.png", size: 32 },
|
|
872
|
+
{ filename: "favicon-48x48.png", size: 48 },
|
|
873
|
+
{ filename: "apple-touch-icon.png", size: 180 },
|
|
874
|
+
{ filename: "android-chrome-192x192.png", size: 192 },
|
|
875
|
+
{ filename: "android-chrome-512x512.png", size: 512 },
|
|
876
|
+
{ filename: "mstile-150x150.png", size: 150 }
|
|
877
|
+
];
|
|
878
|
+
var ANDROID_ICONS = [
|
|
879
|
+
{ filename: "mipmap-mdpi/ic_launcher.png", size: 48 },
|
|
880
|
+
{ filename: "mipmap-hdpi/ic_launcher.png", size: 72 },
|
|
881
|
+
{ filename: "mipmap-xhdpi/ic_launcher.png", size: 96 },
|
|
882
|
+
{ filename: "mipmap-xxhdpi/ic_launcher.png", size: 144 },
|
|
883
|
+
{ filename: "mipmap-xxxhdpi/ic_launcher.png", size: 192 },
|
|
884
|
+
{ filename: "playstore-icon.png", size: 512 }
|
|
885
|
+
];
|
|
886
|
+
var IOS_ICONS = [
|
|
887
|
+
// 20pt - Notifications
|
|
888
|
+
{ filename: "AppIcon-20.png", size: 20 },
|
|
889
|
+
{ filename: "AppIcon-20@2x.png", size: 40 },
|
|
890
|
+
{ filename: "AppIcon-20@3x.png", size: 60 },
|
|
891
|
+
// 29pt - Settings
|
|
892
|
+
{ filename: "AppIcon-29.png", size: 29 },
|
|
893
|
+
{ filename: "AppIcon-29@2x.png", size: 58 },
|
|
894
|
+
{ filename: "AppIcon-29@3x.png", size: 87 },
|
|
895
|
+
// 40pt - Spotlight
|
|
896
|
+
{ filename: "AppIcon-40.png", size: 40 },
|
|
897
|
+
{ filename: "AppIcon-40@2x.png", size: 80 },
|
|
898
|
+
{ filename: "AppIcon-40@3x.png", size: 120 },
|
|
899
|
+
// 60pt - iPhone App
|
|
900
|
+
{ filename: "AppIcon-60@2x.png", size: 120 },
|
|
901
|
+
{ filename: "AppIcon-60@3x.png", size: 180 },
|
|
902
|
+
// 76pt - iPad App
|
|
903
|
+
{ filename: "AppIcon-76.png", size: 76 },
|
|
904
|
+
{ filename: "AppIcon-76@2x.png", size: 152 },
|
|
905
|
+
// 83.5pt - iPad Pro
|
|
906
|
+
{ filename: "AppIcon-83.5@2x.png", size: 167 },
|
|
907
|
+
// App Store
|
|
908
|
+
{ filename: "AppIcon-1024.png", size: 1024 }
|
|
909
|
+
];
|
|
910
|
+
async function generateIcons(outputDir, sourceImg, icons) {
|
|
911
|
+
const total = icons.length;
|
|
912
|
+
let current = 0;
|
|
913
|
+
let failed = 0;
|
|
914
|
+
for (const icon of icons) {
|
|
915
|
+
current++;
|
|
916
|
+
const outputPath = `${outputDir}/${icon.filename}`;
|
|
917
|
+
const subdir = dirname6(outputPath);
|
|
918
|
+
if (!existsSync4(subdir)) {
|
|
919
|
+
mkdirSync4(subdir, { recursive: true });
|
|
920
|
+
}
|
|
921
|
+
clearLine();
|
|
922
|
+
wip(`[${current}/${total}] Generating ${icon.filename}...`);
|
|
923
|
+
if (await scaleToSize(sourceImg, outputPath, icon.size)) {
|
|
924
|
+
clearLine();
|
|
925
|
+
success(
|
|
926
|
+
`[${current}/${total}] ${icon.filename} (${icon.size}x${icon.size})`
|
|
927
|
+
);
|
|
928
|
+
} else {
|
|
929
|
+
clearLine();
|
|
930
|
+
error(`[${current}/${total}] Failed: ${icon.filename}`);
|
|
931
|
+
failed++;
|
|
932
|
+
}
|
|
933
933
|
}
|
|
934
934
|
return failed;
|
|
935
935
|
}
|
|
@@ -1172,7 +1172,7 @@ async function generateIconsSilent(outputDir, sourceImg, icons, _logs) {
|
|
|
1172
1172
|
let failed = 0;
|
|
1173
1173
|
for (const icon of icons) {
|
|
1174
1174
|
const outputPath = `${outputDir}/${icon.filename}`;
|
|
1175
|
-
const subdir =
|
|
1175
|
+
const subdir = dirname6(outputPath);
|
|
1176
1176
|
if (!existsSync4(subdir)) {
|
|
1177
1177
|
mkdirSync4(subdir, { recursive: true });
|
|
1178
1178
|
}
|
|
@@ -1203,6 +1203,12 @@ var config = {
|
|
|
1203
1203
|
extensions: [".png", ".jpg", ".jpeg"]
|
|
1204
1204
|
};
|
|
1205
1205
|
|
|
1206
|
+
// src/cli/utils.ts
|
|
1207
|
+
import { extname as extname3 } from "path";
|
|
1208
|
+
import { dirname as dirname9 } from "path";
|
|
1209
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1210
|
+
import chalk2 from "chalk";
|
|
1211
|
+
|
|
1206
1212
|
// src/tools/makeicon.ts
|
|
1207
1213
|
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
1208
1214
|
import { tmpdir } from "os";
|
|
@@ -1424,7 +1430,7 @@ var config2 = {
|
|
|
1424
1430
|
// src/tools/piclet-main.ts
|
|
1425
1431
|
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync3 } from "fs";
|
|
1426
1432
|
import { tmpdir as tmpdir2 } from "os";
|
|
1427
|
-
import { basename as basename4, dirname as
|
|
1433
|
+
import { basename as basename4, dirname as dirname7, extname as extname2, join as join5 } from "path";
|
|
1428
1434
|
var TOOL_ORDER = ["removebg", "scale", "icons", "storepack"];
|
|
1429
1435
|
async function generateCombinedPreview(input, borderColor, opts) {
|
|
1430
1436
|
const tempDir = tmpdir2();
|
|
@@ -1717,7 +1723,7 @@ async function processCombined(input, borderColor, opts, logs) {
|
|
|
1717
1723
|
];
|
|
1718
1724
|
for (const i of androidIcons) {
|
|
1719
1725
|
const p = `${androidDir}/${i.name}`;
|
|
1720
|
-
mkdirSync5(
|
|
1726
|
+
mkdirSync5(dirname7(p), { recursive: true });
|
|
1721
1727
|
await scaleToSize(srcTemp, p, i.size);
|
|
1722
1728
|
totalCount++;
|
|
1723
1729
|
}
|
|
@@ -2558,22 +2564,20 @@ var config6 = {
|
|
|
2558
2564
|
extensions: [".png", ".jpg", ".jpeg"]
|
|
2559
2565
|
};
|
|
2560
2566
|
|
|
2561
|
-
// src/cli.ts
|
|
2567
|
+
// src/cli/tools.ts
|
|
2562
2568
|
var tools = [
|
|
2563
|
-
{ config: config2, run: run2 },
|
|
2564
|
-
{ config: config4, run: run3 },
|
|
2565
|
-
{ config: config5, run: run4 },
|
|
2566
|
-
{ config, run },
|
|
2567
|
-
{ config: config6, run: run5 }
|
|
2569
|
+
{ config: config2, run: run2, runGUI: runGUI2 },
|
|
2570
|
+
{ config: config4, run: run3, runGUI: runGUI4 },
|
|
2571
|
+
{ config: config5, run: run4, runGUI: runGUI5 },
|
|
2572
|
+
{ config, run, runGUI },
|
|
2573
|
+
{ config: config6, run: run5, runGUI: runGUI6 }
|
|
2568
2574
|
];
|
|
2569
|
-
var picletTool = {
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
function getMenuBasePath(extension) {
|
|
2576
|
-
return `HKCU\\Software\\Classes\\SystemFileAssociations\\${extension}\\shell\\PicLet`;
|
|
2575
|
+
var picletTool = {
|
|
2576
|
+
config: config3,
|
|
2577
|
+
runGUI: runGUI3
|
|
2578
|
+
};
|
|
2579
|
+
function getTool(id) {
|
|
2580
|
+
return tools.find((t) => t.config.id === id);
|
|
2577
2581
|
}
|
|
2578
2582
|
function getAllExtensions() {
|
|
2579
2583
|
const extensions = /* @__PURE__ */ new Set();
|
|
@@ -2587,91 +2591,42 @@ function getAllExtensions() {
|
|
|
2587
2591
|
function getToolsForExtension(extension) {
|
|
2588
2592
|
return tools.filter((t) => t.config.extensions.includes(extension));
|
|
2589
2593
|
}
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
const
|
|
2594
|
-
|
|
2595
|
-
const iconSuccess = await addRegistryKey(basePath, "Icon", `${iconsDirWin}\\banana.ico`);
|
|
2596
|
-
await addRegistryKey(basePath, "MultiSelectModel", "Player");
|
|
2597
|
-
const commandValue = `wscript.exe //B "${launcherWin}" piclet "%1" -g`;
|
|
2598
|
-
const cmdSuccess = await addRegistryKey(`${basePath}\\command`, "", commandValue);
|
|
2599
|
-
return {
|
|
2600
|
-
extension,
|
|
2601
|
-
toolName: "PicLet",
|
|
2602
|
-
success: menuSuccess && iconSuccess && cmdSuccess
|
|
2603
|
-
};
|
|
2594
|
+
|
|
2595
|
+
// src/cli/utils.ts
|
|
2596
|
+
function getDistDir() {
|
|
2597
|
+
const currentFile = fileURLToPath2(import.meta.url);
|
|
2598
|
+
return dirname9(currentFile);
|
|
2604
2599
|
}
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
const basePath = getMenuBasePath(extension);
|
|
2608
|
-
const extensionTools = getToolsForExtension(extension);
|
|
2609
|
-
for (const { config: config7 } of extensionTools) {
|
|
2610
|
-
const toolPath = `${basePath}\\shell\\${config7.id}`;
|
|
2611
|
-
await deleteRegistryKey(`${toolPath}\\command`);
|
|
2612
|
-
const success2 = await deleteRegistryKey(toolPath);
|
|
2613
|
-
results.push({
|
|
2614
|
-
extension,
|
|
2615
|
-
toolName: config7.name,
|
|
2616
|
-
success: success2
|
|
2617
|
-
});
|
|
2618
|
-
}
|
|
2619
|
-
await deleteRegistryKey(`${basePath}\\shell`);
|
|
2620
|
-
await deleteRegistryKey(basePath);
|
|
2621
|
-
return results;
|
|
2600
|
+
function getMenuBasePath(extension) {
|
|
2601
|
+
return `HKCU\\Software\\Classes\\SystemFileAssociations\\${extension}\\shell\\PicLet`;
|
|
2622
2602
|
}
|
|
2623
|
-
|
|
2624
|
-
const
|
|
2625
|
-
const
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2603
|
+
function validateExtensions(files, allowedExtensions) {
|
|
2604
|
+
const valid = [];
|
|
2605
|
+
const invalid = [];
|
|
2606
|
+
for (const file of files) {
|
|
2607
|
+
const ext = extname3(file).toLowerCase();
|
|
2608
|
+
if (allowedExtensions.includes(ext)) {
|
|
2609
|
+
valid.push(file);
|
|
2610
|
+
} else {
|
|
2611
|
+
invalid.push(file);
|
|
2612
|
+
}
|
|
2630
2613
|
}
|
|
2631
|
-
return
|
|
2632
|
-
}
|
|
2633
|
-
async function unregisterAllTools() {
|
|
2634
|
-
const results = [];
|
|
2635
|
-
const allExts = /* @__PURE__ */ new Set([...getAllExtensions(), ...picletTool.config.extensions]);
|
|
2636
|
-
for (const extension of allExts) {
|
|
2637
|
-
const basePath = getMenuBasePath(extension);
|
|
2638
|
-
const extResults = await unregisterMenuForExtension(extension);
|
|
2639
|
-
results.push(...extResults);
|
|
2640
|
-
await deleteRegistryKey(`${basePath}\\command`);
|
|
2641
|
-
await deleteRegistryKey(basePath);
|
|
2642
|
-
}
|
|
2643
|
-
return results;
|
|
2644
|
-
}
|
|
2645
|
-
function getTool(id) {
|
|
2646
|
-
return tools.find((t) => t.config.id === id);
|
|
2647
|
-
}
|
|
2648
|
-
function validateExtensions(files, allowedExtensions) {
|
|
2649
|
-
const valid = [];
|
|
2650
|
-
const invalid = [];
|
|
2651
|
-
for (const file of files) {
|
|
2652
|
-
const ext = extname3(file).toLowerCase();
|
|
2653
|
-
if (allowedExtensions.includes(ext)) {
|
|
2654
|
-
valid.push(file);
|
|
2655
|
-
} else {
|
|
2656
|
-
invalid.push(file);
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
return { valid, invalid };
|
|
2614
|
+
return { valid, invalid };
|
|
2660
2615
|
}
|
|
2661
2616
|
async function runToolOnFiles(toolId, files, useYes) {
|
|
2662
2617
|
const tool = getTool(toolId);
|
|
2663
2618
|
if (!tool) {
|
|
2664
|
-
console.error(
|
|
2619
|
+
console.error(chalk2.red(`Tool not found: ${toolId}`));
|
|
2665
2620
|
return false;
|
|
2666
2621
|
}
|
|
2667
2622
|
const { valid, invalid } = validateExtensions(files, tool.config.extensions);
|
|
2668
2623
|
if (invalid.length > 0) {
|
|
2669
|
-
console.error(
|
|
2624
|
+
console.error(chalk2.red("Invalid file types:"));
|
|
2670
2625
|
for (const file of invalid) {
|
|
2671
|
-
console.error(
|
|
2626
|
+
console.error(chalk2.red(` \u2717 ${file}`));
|
|
2672
2627
|
}
|
|
2673
2628
|
console.error(
|
|
2674
|
-
|
|
2629
|
+
chalk2.yellow(
|
|
2675
2630
|
`
|
|
2676
2631
|
Supported extensions: ${tool.config.extensions.join(", ")}`
|
|
2677
2632
|
)
|
|
@@ -2686,7 +2641,7 @@ Supported extensions: ${tool.config.extensions.join(", ")}`
|
|
|
2686
2641
|
for (let i = 0; i < valid.length; i++) {
|
|
2687
2642
|
const file = valid[i];
|
|
2688
2643
|
if (valid.length > 1) {
|
|
2689
|
-
console.log(
|
|
2644
|
+
console.log(chalk2.cyan(`
|
|
2690
2645
|
[${i + 1}/${valid.length}] ${file}`));
|
|
2691
2646
|
}
|
|
2692
2647
|
const success2 = await tool.run(file);
|
|
@@ -2694,13 +2649,446 @@ Supported extensions: ${tool.config.extensions.join(", ")}`
|
|
|
2694
2649
|
}
|
|
2695
2650
|
return allSuccess;
|
|
2696
2651
|
}
|
|
2652
|
+
|
|
2653
|
+
// src/cli/commands/iconpack.ts
|
|
2654
|
+
function registerIconpackCommand(program2) {
|
|
2655
|
+
program2.command("iconpack <files...>").description("Generate icon sets for Web, Android, iOS").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for options").action(async (files, options) => {
|
|
2656
|
+
if (options.gui) {
|
|
2657
|
+
const { valid, invalid } = validateExtensions(files, config.extensions);
|
|
2658
|
+
if (invalid.length > 0) {
|
|
2659
|
+
console.error(chalk3.red("Invalid file types:"));
|
|
2660
|
+
for (const file of invalid) {
|
|
2661
|
+
console.error(chalk3.red(` - ${file}`));
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
if (valid.length === 0) {
|
|
2665
|
+
process.exit(1);
|
|
2666
|
+
}
|
|
2667
|
+
const result = await runGUI(valid[0]);
|
|
2668
|
+
process.exit(result ? 0 : 1);
|
|
2669
|
+
}
|
|
2670
|
+
const success2 = await runToolOnFiles(
|
|
2671
|
+
"iconpack",
|
|
2672
|
+
files,
|
|
2673
|
+
options.yes ?? false
|
|
2674
|
+
);
|
|
2675
|
+
process.exit(success2 ? 0 : 1);
|
|
2676
|
+
});
|
|
2677
|
+
}
|
|
2678
|
+
|
|
2679
|
+
// src/cli/commands/install.ts
|
|
2680
|
+
import chalk4 from "chalk";
|
|
2681
|
+
|
|
2682
|
+
// src/lib/registry.ts
|
|
2683
|
+
import { exec as exec2 } from "child_process";
|
|
2684
|
+
import { existsSync as existsSync10 } from "fs";
|
|
2685
|
+
import { dirname as dirname10 } from "path";
|
|
2686
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2687
|
+
import { promisify as promisify2 } from "util";
|
|
2688
|
+
var execAsync2 = promisify2(exec2);
|
|
2689
|
+
function isWSL() {
|
|
2690
|
+
return process.platform === "linux" && (process.env.WSL_DISTRO_NAME !== void 0 || process.env.WSLENV !== void 0);
|
|
2691
|
+
}
|
|
2692
|
+
function isWSLInteropEnabled() {
|
|
2693
|
+
return existsSync10("/proc/sys/fs/binfmt_misc/WSLInterop");
|
|
2694
|
+
}
|
|
2695
|
+
async function addRegistryKey(keyPath, valueName, value, type = "REG_SZ") {
|
|
2696
|
+
const valueArg = valueName ? `/v "${valueName}"` : "/ve";
|
|
2697
|
+
const escapedValue = value.replace(/"/g, '\\"');
|
|
2698
|
+
const cmd = `reg.exe add "${keyPath}" ${valueArg} /t ${type} /d "${escapedValue}" /f`;
|
|
2699
|
+
try {
|
|
2700
|
+
await execAsync2(cmd);
|
|
2701
|
+
return true;
|
|
2702
|
+
} catch (error2) {
|
|
2703
|
+
const message = error2.message;
|
|
2704
|
+
const ignoredErrors = ["Exec format error", "not found"];
|
|
2705
|
+
const shouldIgnore = ignoredErrors.some(
|
|
2706
|
+
(e) => message.toLowerCase().includes(e.toLowerCase())
|
|
2707
|
+
);
|
|
2708
|
+
if (!shouldIgnore) {
|
|
2709
|
+
console.error(`Failed to add registry key: ${keyPath}`);
|
|
2710
|
+
console.error(message);
|
|
2711
|
+
}
|
|
2712
|
+
return false;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
async function deleteRegistryKey(keyPath) {
|
|
2716
|
+
const cmd = `reg.exe delete "${keyPath}" /f`;
|
|
2717
|
+
try {
|
|
2718
|
+
await execAsync2(cmd);
|
|
2719
|
+
return true;
|
|
2720
|
+
} catch (error2) {
|
|
2721
|
+
const message = error2.message;
|
|
2722
|
+
const ignoredErrors = ["unable to find", "Exec format error", "not found"];
|
|
2723
|
+
const shouldIgnore = ignoredErrors.some(
|
|
2724
|
+
(e) => message.toLowerCase().includes(e.toLowerCase())
|
|
2725
|
+
);
|
|
2726
|
+
if (!shouldIgnore) {
|
|
2727
|
+
console.error(`Failed to delete registry key: ${keyPath}`);
|
|
2728
|
+
}
|
|
2729
|
+
return false;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
// src/cli/registry.ts
|
|
2734
|
+
import { writeFile } from "fs/promises";
|
|
2735
|
+
import { join as join9 } from "path";
|
|
2736
|
+
async function registerUnifiedMenu(extension, iconsDir, launcherPath) {
|
|
2737
|
+
const basePath = `HKCU\\Software\\Classes\\SystemFileAssociations\\${extension}\\shell\\PicLet`;
|
|
2738
|
+
const iconsDirWin = wslToWindows(iconsDir);
|
|
2739
|
+
const launcherWin = wslToWindows(launcherPath);
|
|
2740
|
+
const menuSuccess = await addRegistryKey(basePath, "MUIVerb", "PicLet");
|
|
2741
|
+
const iconSuccess = await addRegistryKey(basePath, "Icon", `${iconsDirWin}\\banana.ico`);
|
|
2742
|
+
await addRegistryKey(basePath, "MultiSelectModel", "Player");
|
|
2743
|
+
const commandValue = `wscript.exe //B "${launcherWin}" piclet "%1" -g`;
|
|
2744
|
+
const cmdSuccess = await addRegistryKey(`${basePath}\\command`, "", commandValue);
|
|
2745
|
+
return {
|
|
2746
|
+
extension,
|
|
2747
|
+
toolName: "PicLet",
|
|
2748
|
+
success: menuSuccess && iconSuccess && cmdSuccess
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
async function unregisterMenuForExtension(extension) {
|
|
2752
|
+
const results = [];
|
|
2753
|
+
const basePath = getMenuBasePath(extension);
|
|
2754
|
+
const extensionTools = getToolsForExtension(extension);
|
|
2755
|
+
for (const { config: config7 } of extensionTools) {
|
|
2756
|
+
const toolPath = `${basePath}\\shell\\${config7.id}`;
|
|
2757
|
+
await deleteRegistryKey(`${toolPath}\\command`);
|
|
2758
|
+
const success2 = await deleteRegistryKey(toolPath);
|
|
2759
|
+
results.push({
|
|
2760
|
+
extension,
|
|
2761
|
+
toolName: config7.name,
|
|
2762
|
+
success: success2
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
await deleteRegistryKey(`${basePath}\\shell`);
|
|
2766
|
+
await deleteRegistryKey(basePath);
|
|
2767
|
+
return results;
|
|
2768
|
+
}
|
|
2769
|
+
async function registerAllTools() {
|
|
2770
|
+
const distDir = getDistDir();
|
|
2771
|
+
const iconsDir = join9(distDir, "icons");
|
|
2772
|
+
const launcherPath = join9(distDir, "launcher.vbs");
|
|
2773
|
+
const results = [];
|
|
2774
|
+
for (const extension of picletTool.config.extensions) {
|
|
2775
|
+
const result = await registerUnifiedMenu(extension, iconsDir, launcherPath);
|
|
2776
|
+
results.push(result);
|
|
2777
|
+
}
|
|
2778
|
+
return results;
|
|
2779
|
+
}
|
|
2780
|
+
async function unregisterAllTools() {
|
|
2781
|
+
const results = [];
|
|
2782
|
+
const allExts = /* @__PURE__ */ new Set([...getAllExtensions(), ...picletTool.config.extensions]);
|
|
2783
|
+
for (const extension of allExts) {
|
|
2784
|
+
const basePath = getMenuBasePath(extension);
|
|
2785
|
+
const extResults = await unregisterMenuForExtension(extension);
|
|
2786
|
+
results.push(...extResults);
|
|
2787
|
+
await deleteRegistryKey(`${basePath}\\command`);
|
|
2788
|
+
await deleteRegistryKey(basePath);
|
|
2789
|
+
}
|
|
2790
|
+
return results;
|
|
2791
|
+
}
|
|
2792
|
+
function escapeRegValue(value) {
|
|
2793
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
2794
|
+
}
|
|
2795
|
+
function generateRegContent() {
|
|
2796
|
+
const distDir = getDistDir();
|
|
2797
|
+
const iconsDir = join9(distDir, "icons");
|
|
2798
|
+
const launcherPath = join9(distDir, "launcher.vbs");
|
|
2799
|
+
const iconsDirWin = wslToWindows(iconsDir);
|
|
2800
|
+
const launcherWin = wslToWindows(launcherPath);
|
|
2801
|
+
const lines = ["Windows Registry Editor Version 5.00", ""];
|
|
2802
|
+
for (const extension of picletTool.config.extensions) {
|
|
2803
|
+
const basePath = `HKEY_CURRENT_USER\\Software\\Classes\\SystemFileAssociations\\${extension}\\shell\\PicLet`;
|
|
2804
|
+
lines.push(`[${basePath}]`);
|
|
2805
|
+
lines.push(`"MUIVerb"="${escapeRegValue("PicLet")}"`);
|
|
2806
|
+
lines.push(`"Icon"="${escapeRegValue(`${iconsDirWin}\\banana.ico`)}"`);
|
|
2807
|
+
lines.push('"MultiSelectModel"="Player"');
|
|
2808
|
+
lines.push("");
|
|
2809
|
+
const commandValue = `wscript.exe //B "${launcherWin}" piclet "%1" -g`;
|
|
2810
|
+
lines.push(`[${basePath}\\command]`);
|
|
2811
|
+
lines.push(`@="${escapeRegValue(commandValue)}"`);
|
|
2812
|
+
lines.push("");
|
|
2813
|
+
}
|
|
2814
|
+
return lines.join("\r\n");
|
|
2815
|
+
}
|
|
2816
|
+
function generateUninstallRegContent() {
|
|
2817
|
+
const lines = ["Windows Registry Editor Version 5.00", ""];
|
|
2818
|
+
const allExts = /* @__PURE__ */ new Set([...getAllExtensions(), ...picletTool.config.extensions]);
|
|
2819
|
+
for (const extension of allExts) {
|
|
2820
|
+
const basePath = `HKEY_CURRENT_USER\\Software\\Classes\\SystemFileAssociations\\${extension}\\shell\\PicLet`;
|
|
2821
|
+
lines.push(`[-${basePath}]`);
|
|
2822
|
+
lines.push("");
|
|
2823
|
+
}
|
|
2824
|
+
return lines.join("\r\n");
|
|
2825
|
+
}
|
|
2826
|
+
async function generateRegFile() {
|
|
2827
|
+
const distDir = getDistDir();
|
|
2828
|
+
const regPath = join9(distDir, "piclet-install.reg");
|
|
2829
|
+
const content = generateRegContent();
|
|
2830
|
+
await writeFile(regPath, content, "utf-8");
|
|
2831
|
+
return regPath;
|
|
2832
|
+
}
|
|
2833
|
+
async function generateUninstallRegFile() {
|
|
2834
|
+
const distDir = getDistDir();
|
|
2835
|
+
const regPath = join9(distDir, "piclet-uninstall.reg");
|
|
2836
|
+
const content = generateUninstallRegContent();
|
|
2837
|
+
await writeFile(regPath, content, "utf-8");
|
|
2838
|
+
return regPath;
|
|
2839
|
+
}
|
|
2840
|
+
|
|
2841
|
+
// src/cli/commands/install.ts
|
|
2842
|
+
function registerInstallCommand(program2) {
|
|
2843
|
+
program2.command("install").description("Install Windows shell context menu integration").action(async () => {
|
|
2844
|
+
showBanner();
|
|
2845
|
+
console.log(chalk4.bold("Installing...\n"));
|
|
2846
|
+
if (!isWSL()) {
|
|
2847
|
+
console.log(
|
|
2848
|
+
chalk4.yellow("! Not running in WSL. Registry integration skipped.")
|
|
2849
|
+
);
|
|
2850
|
+
console.log(
|
|
2851
|
+
chalk4.yellow('! Run "piclet install" from WSL to add context menu.')
|
|
2852
|
+
);
|
|
2853
|
+
return;
|
|
2854
|
+
}
|
|
2855
|
+
if (!isWSLInteropEnabled()) {
|
|
2856
|
+
console.log(chalk4.yellow("WSL Interop not available. Generating registry file...\n"));
|
|
2857
|
+
const regPath = await generateRegFile();
|
|
2858
|
+
const winPath = wslToWindows(regPath);
|
|
2859
|
+
console.log(chalk4.green("\u2713 Generated registry file:"));
|
|
2860
|
+
console.log(chalk4.cyan(` ${winPath}
|
|
2861
|
+
`));
|
|
2862
|
+
console.log(chalk4.bold("To install, either:"));
|
|
2863
|
+
console.log(chalk4.dim(" 1. Double-click the .reg file in Windows Explorer"));
|
|
2864
|
+
console.log(chalk4.dim(` 2. Run in elevated PowerShell: reg import "${winPath}"`));
|
|
2865
|
+
console.log();
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
console.log(chalk4.dim("Removing old entries..."));
|
|
2869
|
+
await unregisterAllTools();
|
|
2870
|
+
console.log();
|
|
2871
|
+
const results = await registerAllTools();
|
|
2872
|
+
const allSuccess = results.every((r) => r.success);
|
|
2873
|
+
for (const { config: config7 } of tools) {
|
|
2874
|
+
const extList = config7.extensions.join(", ");
|
|
2875
|
+
console.log(`${chalk4.green("\u2713")} ${config7.name} ${chalk4.dim(`[${extList}]`)}`);
|
|
2876
|
+
}
|
|
2877
|
+
console.log();
|
|
2878
|
+
if (allSuccess) {
|
|
2879
|
+
console.log(
|
|
2880
|
+
chalk4.green(`\u2713 Registered ${tools.length} tools for context menu.`)
|
|
2881
|
+
);
|
|
2882
|
+
} else {
|
|
2883
|
+
const successCount = results.filter((r) => r.success).length;
|
|
2884
|
+
console.log(
|
|
2885
|
+
chalk4.yellow(`! Registered ${successCount}/${results.length} entries.`)
|
|
2886
|
+
);
|
|
2887
|
+
}
|
|
2888
|
+
console.log(chalk4.bold("\nUsage:"));
|
|
2889
|
+
console.log(" Right-click any supported image in Windows Explorer.");
|
|
2890
|
+
console.log(" Multi-select supported for batch processing.");
|
|
2891
|
+
console.log();
|
|
2892
|
+
});
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
// src/cli/commands/makeicon.ts
|
|
2896
|
+
import chalk5 from "chalk";
|
|
2897
|
+
function registerMakeiconCommand(program2) {
|
|
2898
|
+
program2.command("makeicon <files...>").description("Convert PNG to multi-resolution ICO file").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for confirmation").action(async (files, options) => {
|
|
2899
|
+
if (options.gui) {
|
|
2900
|
+
const { valid, invalid } = validateExtensions(files, config2.extensions);
|
|
2901
|
+
if (invalid.length > 0) {
|
|
2902
|
+
console.error(chalk5.red("Invalid file types:"));
|
|
2903
|
+
for (const file of invalid) {
|
|
2904
|
+
console.error(chalk5.red(` - ${file}`));
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
if (valid.length === 0) {
|
|
2908
|
+
process.exit(1);
|
|
2909
|
+
}
|
|
2910
|
+
const result = await runGUI2(valid[0]);
|
|
2911
|
+
process.exit(result ? 0 : 1);
|
|
2912
|
+
}
|
|
2913
|
+
const success2 = await runToolOnFiles(
|
|
2914
|
+
"makeicon",
|
|
2915
|
+
files,
|
|
2916
|
+
options.yes ?? false
|
|
2917
|
+
);
|
|
2918
|
+
process.exit(success2 ? 0 : 1);
|
|
2919
|
+
});
|
|
2920
|
+
}
|
|
2921
|
+
|
|
2922
|
+
// src/cli/commands/piclet.ts
|
|
2923
|
+
import chalk6 from "chalk";
|
|
2924
|
+
function registerPicletCommand(program2) {
|
|
2925
|
+
program2.command("piclet <file>").description("Open unified PicLet window with all tools").option("-g, --gui", "Use GUI (default)").action(async (file) => {
|
|
2926
|
+
const { valid, invalid } = validateExtensions([file], picletTool.config.extensions);
|
|
2927
|
+
if (invalid.length > 0) {
|
|
2928
|
+
console.error(chalk6.red(`Invalid file type: ${file}`));
|
|
2929
|
+
console.error(chalk6.yellow(`Supported: ${picletTool.config.extensions.join(", ")}`));
|
|
2930
|
+
process.exit(1);
|
|
2931
|
+
}
|
|
2932
|
+
const result = await picletTool.runGUI(valid[0]);
|
|
2933
|
+
process.exit(result ? 0 : 1);
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2936
|
+
|
|
2937
|
+
// src/cli/commands/remove-bg.ts
|
|
2938
|
+
import chalk7 from "chalk";
|
|
2939
|
+
function registerRemoveBgCommand(program2) {
|
|
2940
|
+
program2.command("remove-bg <files...>").alias("removebg").description("Remove solid background from image").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use TUI (terminal GUI) for options").option("-f, --fuzz <percent>", "Fuzz tolerance 0-100 (default: 10)").option("-t, --trim", "Trim transparent edges (default: true)").option("--no-trim", "Do not trim transparent edges").option("-p, --preserve-inner", "Preserve inner areas of same color").option("-s, --square", "Make output square with padding").action(async (files, options) => {
|
|
2941
|
+
if (options.gui) {
|
|
2942
|
+
const { valid, invalid } = validateExtensions(files, config4.extensions);
|
|
2943
|
+
if (invalid.length > 0) {
|
|
2944
|
+
console.error(chalk7.red("Invalid file types:"));
|
|
2945
|
+
for (const file of invalid) {
|
|
2946
|
+
console.error(chalk7.red(` - ${file}`));
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
if (valid.length === 0) {
|
|
2950
|
+
process.exit(1);
|
|
2951
|
+
}
|
|
2952
|
+
const result = await runGUI4(valid[0]);
|
|
2953
|
+
process.exit(result ? 0 : 1);
|
|
2954
|
+
}
|
|
2955
|
+
if (options.fuzz !== void 0) {
|
|
2956
|
+
setOverrides({ "fuzz": Number(options.fuzz) });
|
|
2957
|
+
}
|
|
2958
|
+
if (options.trim !== void 0) {
|
|
2959
|
+
setOverrides({ "trim": options.trim });
|
|
2960
|
+
}
|
|
2961
|
+
if (options.preserveInner) {
|
|
2962
|
+
setOverrides({ "preserve inner": true });
|
|
2963
|
+
}
|
|
2964
|
+
if (options.square) {
|
|
2965
|
+
setOverrides({ "square": true });
|
|
2966
|
+
}
|
|
2967
|
+
const success2 = await runToolOnFiles(
|
|
2968
|
+
"remove-bg",
|
|
2969
|
+
files,
|
|
2970
|
+
options.yes ?? false
|
|
2971
|
+
);
|
|
2972
|
+
clearOverrides();
|
|
2973
|
+
process.exit(success2 ? 0 : 1);
|
|
2974
|
+
});
|
|
2975
|
+
}
|
|
2976
|
+
|
|
2977
|
+
// src/cli/commands/scale.ts
|
|
2978
|
+
import chalk8 from "chalk";
|
|
2979
|
+
function registerScaleCommand(program2) {
|
|
2980
|
+
program2.command("scale <files...>").alias("rescale").description("Resize image with optional padding").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for options").action(async (files, options) => {
|
|
2981
|
+
if (options.gui) {
|
|
2982
|
+
const { valid, invalid } = validateExtensions(files, config5.extensions);
|
|
2983
|
+
if (invalid.length > 0) {
|
|
2984
|
+
console.error(chalk8.red("Invalid file types:"));
|
|
2985
|
+
for (const file of invalid) {
|
|
2986
|
+
console.error(chalk8.red(` - ${file}`));
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
if (valid.length === 0) {
|
|
2990
|
+
process.exit(1);
|
|
2991
|
+
}
|
|
2992
|
+
const result = await runGUI5(valid[0]);
|
|
2993
|
+
process.exit(result ? 0 : 1);
|
|
2994
|
+
}
|
|
2995
|
+
const success2 = await runToolOnFiles(
|
|
2996
|
+
"rescale",
|
|
2997
|
+
files,
|
|
2998
|
+
options.yes ?? false
|
|
2999
|
+
);
|
|
3000
|
+
process.exit(success2 ? 0 : 1);
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// src/cli/commands/storepack.ts
|
|
3005
|
+
import chalk9 from "chalk";
|
|
3006
|
+
function registerStorepackCommand(program2) {
|
|
3007
|
+
program2.command("storepack <files...>").description("Generate assets for app stores (Windows, Unity, Steam, etc.)").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for options").action(async (files, options) => {
|
|
3008
|
+
if (options.gui) {
|
|
3009
|
+
const { valid, invalid } = validateExtensions(files, config6.extensions);
|
|
3010
|
+
if (invalid.length > 0) {
|
|
3011
|
+
console.error(chalk9.red("Invalid file types:"));
|
|
3012
|
+
for (const file of invalid) {
|
|
3013
|
+
console.error(chalk9.red(` - ${file}`));
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
if (valid.length === 0) {
|
|
3017
|
+
process.exit(1);
|
|
3018
|
+
}
|
|
3019
|
+
const result = await runGUI6(valid[0]);
|
|
3020
|
+
process.exit(result ? 0 : 1);
|
|
3021
|
+
}
|
|
3022
|
+
const success2 = await runToolOnFiles(
|
|
3023
|
+
"storepack",
|
|
3024
|
+
files,
|
|
3025
|
+
options.yes ?? false
|
|
3026
|
+
);
|
|
3027
|
+
process.exit(success2 ? 0 : 1);
|
|
3028
|
+
});
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
// src/cli/commands/uninstall.ts
|
|
3032
|
+
import chalk10 from "chalk";
|
|
3033
|
+
function registerUninstallCommand(program2) {
|
|
3034
|
+
program2.command("uninstall").description("Remove Windows shell context menu integration").action(async () => {
|
|
3035
|
+
showBanner();
|
|
3036
|
+
console.log(chalk10.bold("Uninstalling...\n"));
|
|
3037
|
+
if (!isWSL()) {
|
|
3038
|
+
console.log(
|
|
3039
|
+
chalk10.yellow("! Not running in WSL. Registry cleanup skipped.")
|
|
3040
|
+
);
|
|
3041
|
+
console.log(
|
|
3042
|
+
chalk10.yellow(
|
|
3043
|
+
'! Run "piclet uninstall" from WSL to remove context menu.'
|
|
3044
|
+
)
|
|
3045
|
+
);
|
|
3046
|
+
return;
|
|
3047
|
+
}
|
|
3048
|
+
if (!isWSLInteropEnabled()) {
|
|
3049
|
+
console.log(chalk10.yellow("WSL Interop not available. Generating registry file...\n"));
|
|
3050
|
+
const regPath = await generateUninstallRegFile();
|
|
3051
|
+
const winPath = wslToWindows(regPath);
|
|
3052
|
+
console.log(chalk10.green("\u2713 Generated uninstall registry file:"));
|
|
3053
|
+
console.log(chalk10.cyan(` ${winPath}
|
|
3054
|
+
`));
|
|
3055
|
+
console.log(chalk10.bold("To uninstall, either:"));
|
|
3056
|
+
console.log(chalk10.dim(" 1. Double-click the .reg file in Windows Explorer"));
|
|
3057
|
+
console.log(chalk10.dim(` 2. Run in elevated PowerShell: reg import "${winPath}"`));
|
|
3058
|
+
console.log();
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
3061
|
+
const results = await unregisterAllTools();
|
|
3062
|
+
const removedCount = results.filter((r) => r.success).length;
|
|
3063
|
+
for (const result of results) {
|
|
3064
|
+
if (result.success) {
|
|
3065
|
+
console.log(
|
|
3066
|
+
`${chalk10.green("\u2713")} Removed: ${result.extension} \u2192 ${result.toolName}`
|
|
3067
|
+
);
|
|
3068
|
+
} else {
|
|
3069
|
+
console.log(
|
|
3070
|
+
`${chalk10.gray("-")} Skipped: ${result.extension} \u2192 ${result.toolName}`
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
console.log();
|
|
3075
|
+
console.log(
|
|
3076
|
+
chalk10.green(
|
|
3077
|
+
`\u2713 Cleanup complete. Removed ${removedCount}/${results.length} entries.`
|
|
3078
|
+
)
|
|
3079
|
+
);
|
|
3080
|
+
console.log(chalk10.dim("\nThanks for using PicLet!\n"));
|
|
3081
|
+
});
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
// src/cli/index.ts
|
|
2697
3085
|
function showHelp() {
|
|
2698
3086
|
showBanner();
|
|
2699
|
-
const dim =
|
|
2700
|
-
const cmd =
|
|
2701
|
-
const arg =
|
|
2702
|
-
const opt =
|
|
2703
|
-
const head =
|
|
3087
|
+
const dim = chalk11.gray;
|
|
3088
|
+
const cmd = chalk11.cyan;
|
|
3089
|
+
const arg = chalk11.yellow;
|
|
3090
|
+
const opt = chalk11.green;
|
|
3091
|
+
const head = chalk11.white.bold;
|
|
2704
3092
|
console.log(
|
|
2705
3093
|
` ${head("Usage:")} piclet ${cmd("<command>")} ${arg("<file>")} ${opt("[options]")}`
|
|
2706
3094
|
);
|
|
@@ -2750,240 +3138,31 @@ function showHelp() {
|
|
|
2750
3138
|
console.log(" - ImageMagick: sudo apt install imagemagick");
|
|
2751
3139
|
console.log();
|
|
2752
3140
|
}
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
console.log();
|
|
2777
|
-
const results = await registerAllTools(getDistDir());
|
|
2778
|
-
const successCount = results.filter((r) => r.success).length;
|
|
2779
|
-
for (const result of results) {
|
|
2780
|
-
if (result.success) {
|
|
2781
|
-
console.log(
|
|
2782
|
-
`${chalk.green("\u2713")} ${result.extension} \u2192 ${result.toolName}`
|
|
2783
|
-
);
|
|
2784
|
-
} else {
|
|
2785
|
-
console.log(
|
|
2786
|
-
`${chalk.red("\u2717")} ${result.extension} \u2192 ${result.toolName} (failed)`
|
|
2787
|
-
);
|
|
2788
|
-
}
|
|
2789
|
-
}
|
|
2790
|
-
console.log();
|
|
2791
|
-
if (successCount === results.length) {
|
|
2792
|
-
console.log(
|
|
2793
|
-
chalk.green(`\u2713 Registered ${successCount} context menu entries.`)
|
|
2794
|
-
);
|
|
2795
|
-
} else {
|
|
2796
|
-
console.log(
|
|
2797
|
-
chalk.yellow(`! Registered ${successCount}/${results.length} entries.`)
|
|
2798
|
-
);
|
|
2799
|
-
}
|
|
2800
|
-
console.log(chalk.bold("\nUsage:"));
|
|
2801
|
-
console.log(" Right-click any supported image in Windows Explorer.");
|
|
2802
|
-
console.log(" Multi-select supported for batch processing.");
|
|
2803
|
-
console.log();
|
|
2804
|
-
});
|
|
2805
|
-
program.command("uninstall").description("Remove Windows shell context menu integration").action(async () => {
|
|
2806
|
-
showBanner();
|
|
2807
|
-
console.log(chalk.bold("Uninstalling...\n"));
|
|
2808
|
-
if (!isWSL()) {
|
|
2809
|
-
console.log(
|
|
2810
|
-
chalk.yellow("! Not running in WSL. Registry cleanup skipped.")
|
|
2811
|
-
);
|
|
2812
|
-
console.log(
|
|
2813
|
-
chalk.yellow(
|
|
2814
|
-
'! Run "piclet uninstall" from WSL to remove context menu.'
|
|
2815
|
-
)
|
|
2816
|
-
);
|
|
2817
|
-
return;
|
|
2818
|
-
}
|
|
2819
|
-
const results = await unregisterAllTools();
|
|
2820
|
-
const removedCount = results.filter((r) => r.success).length;
|
|
2821
|
-
for (const result of results) {
|
|
2822
|
-
if (result.success) {
|
|
2823
|
-
console.log(
|
|
2824
|
-
`${chalk.green("\u2713")} Removed: ${result.extension} \u2192 ${result.toolName}`
|
|
2825
|
-
);
|
|
2826
|
-
} else {
|
|
2827
|
-
console.log(
|
|
2828
|
-
`${chalk.gray("-")} Skipped: ${result.extension} \u2192 ${result.toolName}`
|
|
2829
|
-
);
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
console.log();
|
|
2833
|
-
console.log(
|
|
2834
|
-
chalk.green(
|
|
2835
|
-
`\u2713 Cleanup complete. Removed ${removedCount}/${results.length} entries.`
|
|
2836
|
-
)
|
|
2837
|
-
);
|
|
2838
|
-
console.log(chalk.dim("\nThanks for using PicLet!\n"));
|
|
2839
|
-
});
|
|
2840
|
-
program.command("makeicon <files...>").description("Convert PNG to multi-resolution ICO file").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for confirmation").action(async (files, options) => {
|
|
2841
|
-
if (options.gui) {
|
|
2842
|
-
const { valid, invalid } = validateExtensions(files, config2.extensions);
|
|
2843
|
-
if (invalid.length > 0) {
|
|
2844
|
-
console.error(chalk.red("Invalid file types:"));
|
|
2845
|
-
for (const file of invalid) {
|
|
2846
|
-
console.error(chalk.red(` - ${file}`));
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
if (valid.length === 0) {
|
|
2850
|
-
process.exit(1);
|
|
2851
|
-
}
|
|
2852
|
-
const result = await runGUI2(valid[0]);
|
|
2853
|
-
process.exit(result ? 0 : 1);
|
|
2854
|
-
}
|
|
2855
|
-
const success2 = await runToolOnFiles(
|
|
2856
|
-
"makeicon",
|
|
2857
|
-
files,
|
|
2858
|
-
options.yes ?? false
|
|
2859
|
-
);
|
|
2860
|
-
process.exit(success2 ? 0 : 1);
|
|
2861
|
-
});
|
|
2862
|
-
program.command("remove-bg <files...>").alias("removebg").description("Remove solid background from image").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use TUI (terminal GUI) for options").option("-f, --fuzz <percent>", "Fuzz tolerance 0-100 (default: 10)").option("-t, --trim", "Trim transparent edges (default: true)").option("--no-trim", "Do not trim transparent edges").option("-p, --preserve-inner", "Preserve inner areas of same color").option("-s, --square", "Make output square with padding").action(async (files, options) => {
|
|
2863
|
-
if (options.gui) {
|
|
2864
|
-
const { valid, invalid } = validateExtensions(files, config4.extensions);
|
|
2865
|
-
if (invalid.length > 0) {
|
|
2866
|
-
console.error(chalk.red("Invalid file types:"));
|
|
2867
|
-
for (const file of invalid) {
|
|
2868
|
-
console.error(chalk.red(` - ${file}`));
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
if (valid.length === 0) {
|
|
2872
|
-
process.exit(1);
|
|
2873
|
-
}
|
|
2874
|
-
const result = await runGUI4(valid[0]);
|
|
2875
|
-
process.exit(result ? 0 : 1);
|
|
2876
|
-
}
|
|
2877
|
-
if (options.fuzz !== void 0) {
|
|
2878
|
-
setOverrides({ "fuzz": Number(options.fuzz) });
|
|
2879
|
-
}
|
|
2880
|
-
if (options.trim !== void 0) {
|
|
2881
|
-
setOverrides({ "trim": options.trim });
|
|
2882
|
-
}
|
|
2883
|
-
if (options.preserveInner) {
|
|
2884
|
-
setOverrides({ "preserve inner": true });
|
|
2885
|
-
}
|
|
2886
|
-
if (options.square) {
|
|
2887
|
-
setOverrides({ "square": true });
|
|
2888
|
-
}
|
|
2889
|
-
const success2 = await runToolOnFiles(
|
|
2890
|
-
"remove-bg",
|
|
2891
|
-
files,
|
|
2892
|
-
options.yes ?? false
|
|
2893
|
-
);
|
|
2894
|
-
clearOverrides();
|
|
2895
|
-
process.exit(success2 ? 0 : 1);
|
|
2896
|
-
});
|
|
2897
|
-
program.command("scale <files...>").alias("rescale").description("Resize image with optional padding").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for options").action(async (files, options) => {
|
|
2898
|
-
if (options.gui) {
|
|
2899
|
-
const { valid, invalid } = validateExtensions(files, config5.extensions);
|
|
2900
|
-
if (invalid.length > 0) {
|
|
2901
|
-
console.error(chalk.red("Invalid file types:"));
|
|
2902
|
-
for (const file of invalid) {
|
|
2903
|
-
console.error(chalk.red(` - ${file}`));
|
|
2904
|
-
}
|
|
2905
|
-
}
|
|
2906
|
-
if (valid.length === 0) {
|
|
2907
|
-
process.exit(1);
|
|
2908
|
-
}
|
|
2909
|
-
const result = await runGUI5(valid[0]);
|
|
2910
|
-
process.exit(result ? 0 : 1);
|
|
2911
|
-
}
|
|
2912
|
-
const success2 = await runToolOnFiles(
|
|
2913
|
-
"rescale",
|
|
2914
|
-
files,
|
|
2915
|
-
options.yes ?? false
|
|
2916
|
-
);
|
|
2917
|
-
process.exit(success2 ? 0 : 1);
|
|
2918
|
-
});
|
|
2919
|
-
program.command("iconpack <files...>").description("Generate icon sets for Web, Android, iOS").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for options").action(async (files, options) => {
|
|
2920
|
-
if (options.gui) {
|
|
2921
|
-
const { valid, invalid } = validateExtensions(files, config.extensions);
|
|
2922
|
-
if (invalid.length > 0) {
|
|
2923
|
-
console.error(chalk.red("Invalid file types:"));
|
|
2924
|
-
for (const file of invalid) {
|
|
2925
|
-
console.error(chalk.red(` - ${file}`));
|
|
2926
|
-
}
|
|
2927
|
-
}
|
|
2928
|
-
if (valid.length === 0) {
|
|
2929
|
-
process.exit(1);
|
|
2930
|
-
}
|
|
2931
|
-
const result = await runGUI(valid[0]);
|
|
2932
|
-
process.exit(result ? 0 : 1);
|
|
2933
|
-
}
|
|
2934
|
-
const success2 = await runToolOnFiles(
|
|
2935
|
-
"iconpack",
|
|
2936
|
-
files,
|
|
2937
|
-
options.yes ?? false
|
|
2938
|
-
);
|
|
2939
|
-
process.exit(success2 ? 0 : 1);
|
|
2940
|
-
});
|
|
2941
|
-
program.command("storepack <files...>").description("Generate assets for app stores (Windows, Unity, Steam, etc.)").option("-y, --yes", "Use defaults, skip prompts").option("-g, --gui", "Use GUI for options").action(async (files, options) => {
|
|
2942
|
-
if (options.gui) {
|
|
2943
|
-
const { valid, invalid } = validateExtensions(files, config6.extensions);
|
|
2944
|
-
if (invalid.length > 0) {
|
|
2945
|
-
console.error(chalk.red("Invalid file types:"));
|
|
2946
|
-
for (const file of invalid) {
|
|
2947
|
-
console.error(chalk.red(` - ${file}`));
|
|
2948
|
-
}
|
|
2949
|
-
}
|
|
2950
|
-
if (valid.length === 0) {
|
|
2951
|
-
process.exit(1);
|
|
2952
|
-
}
|
|
2953
|
-
const result = await runGUI6(valid[0]);
|
|
2954
|
-
process.exit(result ? 0 : 1);
|
|
2955
|
-
}
|
|
2956
|
-
const success2 = await runToolOnFiles(
|
|
2957
|
-
"storepack",
|
|
2958
|
-
files,
|
|
2959
|
-
options.yes ?? false
|
|
2960
|
-
);
|
|
2961
|
-
process.exit(success2 ? 0 : 1);
|
|
2962
|
-
});
|
|
2963
|
-
program.command("piclet <file>").description("Open unified PicLet window with all tools").option("-g, --gui", "Use GUI (default)").action(async (file) => {
|
|
2964
|
-
const { valid, invalid } = validateExtensions([file], picletTool.config.extensions);
|
|
2965
|
-
if (invalid.length > 0) {
|
|
2966
|
-
console.error(chalk.red(`Invalid file type: ${file}`));
|
|
2967
|
-
console.error(chalk.yellow(`Supported: ${picletTool.config.extensions.join(", ")}`));
|
|
2968
|
-
process.exit(1);
|
|
2969
|
-
}
|
|
2970
|
-
const result = await picletTool.runGUI(valid[0]);
|
|
2971
|
-
process.exit(result ? 0 : 1);
|
|
2972
|
-
});
|
|
2973
|
-
var configCmd = program.command("config").description("Display current settings").action(() => {
|
|
2974
|
-
const config7 = loadConfig();
|
|
2975
|
-
console.log(chalk.white.bold("\n PicLet Configuration"));
|
|
2976
|
-
console.log(chalk.gray(` ${getConfigPath()}
|
|
2977
|
-
`));
|
|
2978
|
-
console.log(JSON.stringify(config7, null, 2));
|
|
2979
|
-
console.log();
|
|
2980
|
-
});
|
|
2981
|
-
configCmd.command("reset").description("Restore defaults").action(() => {
|
|
2982
|
-
resetConfig();
|
|
2983
|
-
console.log(chalk.green("Configuration reset to defaults."));
|
|
2984
|
-
});
|
|
3141
|
+
function createProgram() {
|
|
3142
|
+
const program2 = new Command();
|
|
3143
|
+
program2.helpInformation = () => "";
|
|
3144
|
+
program2.on("--help", () => {
|
|
3145
|
+
});
|
|
3146
|
+
program2.name("piclet").description("Image manipulation utility toolkit with Windows shell integration").version("1.0.0").action(() => {
|
|
3147
|
+
showHelp();
|
|
3148
|
+
});
|
|
3149
|
+
registerHelpCommand(program2);
|
|
3150
|
+
registerInstallCommand(program2);
|
|
3151
|
+
registerUninstallCommand(program2);
|
|
3152
|
+
registerMakeiconCommand(program2);
|
|
3153
|
+
registerRemoveBgCommand(program2);
|
|
3154
|
+
registerScaleCommand(program2);
|
|
3155
|
+
registerIconpackCommand(program2);
|
|
3156
|
+
registerStorepackCommand(program2);
|
|
3157
|
+
registerPicletCommand(program2);
|
|
3158
|
+
registerConfigCommand(program2);
|
|
3159
|
+
return program2;
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
// src/cli.ts
|
|
3163
|
+
var program = createProgram();
|
|
2985
3164
|
program.parseAsync(process.argv).catch((error2) => {
|
|
2986
|
-
console.error(
|
|
3165
|
+
console.error(chalk12.red(`Error: ${error2.message}`));
|
|
2987
3166
|
process.exit(1);
|
|
2988
3167
|
});
|
|
2989
3168
|
//# sourceMappingURL=cli.js.map
|