@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/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { extname as extname3 } from "path";
5
- import { dirname as dirname10, join as join9 } from "path";
6
- import { fileURLToPath as fileURLToPath3 } from "url";
7
- import chalk from "chalk";
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/lib/paths.ts
91
- import { basename, dirname as dirname2, extname, resolve } from "path";
92
- function windowsToWsl(winPath) {
93
- if (winPath.startsWith("/mnt/")) {
94
- return winPath;
95
- }
96
- const match = winPath.match(/^([A-Za-z]):[/\\](.*)$/);
97
- if (match) {
98
- const drive = match[1].toLowerCase();
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
- return response.value ?? null;
195
- }
196
- async function multiSelect(message, options, defaultValues) {
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
- prompts.override({});
213
- async function pauseOnError(message = "Press Enter to close...") {
214
- if (!useDefaults) return;
215
- console.log();
216
- await prompts({
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/lib/registry.ts
224
- import { exec } from "child_process";
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 dirname7 } from "path";
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 dirname5, join as join3 } from "path";
278
- import { fileURLToPath as fileURLToPath2 } from "url";
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 dirname4, join as join2 } from "path";
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 = dirname4(presetsPath);
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 = dirname5(fileURLToPath2(import.meta.url));
343
+ var __dirname2 = dirname3(fileURLToPath(import.meta.url));
495
344
  function signalReady() {
496
- spawn("cmd.exe", ["/c", "echo.>", "%TEMP%\\piclet-ready.tmp"], {
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("cmd.exe", ["/c", "start", '""', "msedge", `--app=${url}`], {
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 as exec2 } from "child_process";
568
+ import { exec } from "child_process";
702
569
  import { copyFileSync, existsSync as existsSync3, mkdirSync as mkdirSync3, unlinkSync } from "fs";
703
- import { dirname as dirname6 } from "path";
704
- import { promisify as promisify2 } from "util";
705
- var execAsync2 = promisify2(exec2);
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 execAsync2("command -v convert");
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 execAsync2(
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 execAsync2(
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 execAsync2(`convert ${input} -trim +repage "${outputPath}"`);
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 execAsync2(
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 execAsync2(
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 execAsync2(
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 execAsync2(
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 execAsync2(
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 execAsync2(
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 execAsync2(
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 execAsync2(
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 execAsync2(`convert ${inputs} "${outputPath}"`);
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/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 = dirname7(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
- }
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 = dirname7(outputPath);
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 dirname8, extname as extname2, join as join5 } from "path";
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(dirname8(p), { recursive: true });
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 = { config: config3, runGUI: runGUI3 };
2570
- var program = new Command();
2571
- function getDistDir() {
2572
- const currentFile = fileURLToPath3(import.meta.url);
2573
- return dirname10(currentFile);
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
- async function registerUnifiedMenu(extension, iconsDir, launcherPath) {
2591
- const basePath = `HKCU\\Software\\Classes\\SystemFileAssociations\\${extension}\\shell\\PicLet`;
2592
- const iconsDirWin = wslToWindows(iconsDir);
2593
- const launcherWin = wslToWindows(launcherPath);
2594
- const menuSuccess = await addRegistryKey(basePath, "MUIVerb", "PicLet");
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
- async function unregisterMenuForExtension(extension) {
2606
- const results = [];
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
- async function registerAllTools(distDir) {
2624
- const iconsDir = join9(distDir, "icons");
2625
- const launcherPath = join9(distDir, "launcher.vbs");
2626
- const results = [];
2627
- for (const extension of picletTool.config.extensions) {
2628
- const result = await registerUnifiedMenu(extension, iconsDir, launcherPath);
2629
- results.push(result);
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 results;
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(chalk.red(`Tool not found: ${toolId}`));
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(chalk.red("Invalid file types:"));
2624
+ console.error(chalk2.red("Invalid file types:"));
2670
2625
  for (const file of invalid) {
2671
- console.error(chalk.red(` \u2717 ${file}`));
2626
+ console.error(chalk2.red(` \u2717 ${file}`));
2672
2627
  }
2673
2628
  console.error(
2674
- chalk.yellow(
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(chalk.cyan(`
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 = chalk.gray;
2700
- const cmd = chalk.cyan;
2701
- const arg = chalk.yellow;
2702
- const opt = chalk.green;
2703
- const head = chalk.white.bold;
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
- program.helpInformation = () => "";
2754
- program.on("--help", () => {
2755
- });
2756
- program.name("piclet").description("Image manipulation utility toolkit with Windows shell integration").version("1.0.0").action(() => {
2757
- showHelp();
2758
- });
2759
- program.command("help").description("Show help").action(() => {
2760
- showHelp();
2761
- });
2762
- program.command("install").description("Install Windows shell context menu integration").action(async () => {
2763
- showBanner();
2764
- console.log(chalk.bold("Installing...\n"));
2765
- if (!isWSL()) {
2766
- console.log(
2767
- chalk.yellow("! Not running in WSL. Registry integration skipped.")
2768
- );
2769
- console.log(
2770
- chalk.yellow('! Run "piclet install" from WSL to add context menu.')
2771
- );
2772
- return;
2773
- }
2774
- console.log(chalk.dim("Removing old entries..."));
2775
- await unregisterAllTools();
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(chalk.red(`Error: ${error2.message}`));
3165
+ console.error(chalk12.red(`Error: ${error2.message}`));
2987
3166
  process.exit(1);
2988
3167
  });
2989
3168
  //# sourceMappingURL=cli.js.map