@mrdemonwolf/iconwolf 0.1.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/dist/generator.d.ts +3 -0
  3. package/dist/generator.d.ts.map +1 -0
  4. package/dist/generator.js +122 -0
  5. package/dist/generator.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +48 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib.d.ts +10 -0
  11. package/dist/lib.d.ts.map +1 -0
  12. package/dist/lib.js +9 -0
  13. package/dist/lib.js.map +1 -0
  14. package/dist/types.d.ts +21 -0
  15. package/dist/types.d.ts.map +1 -0
  16. package/dist/types.js +2 -0
  17. package/dist/types.js.map +1 -0
  18. package/dist/utils/icon-composer.d.ts +14 -0
  19. package/dist/utils/icon-composer.d.ts.map +1 -0
  20. package/dist/utils/icon-composer.js +200 -0
  21. package/dist/utils/icon-composer.js.map +1 -0
  22. package/dist/utils/image.d.ts +22 -0
  23. package/dist/utils/image.d.ts.map +1 -0
  24. package/dist/utils/image.js +145 -0
  25. package/dist/utils/image.js.map +1 -0
  26. package/dist/utils/logger.d.ts +10 -0
  27. package/dist/utils/logger.d.ts.map +1 -0
  28. package/dist/utils/logger.js +38 -0
  29. package/dist/utils/logger.js.map +1 -0
  30. package/dist/utils/paths.d.ts +17 -0
  31. package/dist/utils/paths.d.ts.map +1 -0
  32. package/dist/utils/paths.js +27 -0
  33. package/dist/utils/paths.js.map +1 -0
  34. package/dist/utils/update-notifier.d.ts +9 -0
  35. package/dist/utils/update-notifier.d.ts.map +1 -0
  36. package/dist/utils/update-notifier.js +80 -0
  37. package/dist/utils/update-notifier.js.map +1 -0
  38. package/dist/variants/android.d.ts +5 -0
  39. package/dist/variants/android.d.ts.map +1 -0
  40. package/dist/variants/android.js +18 -0
  41. package/dist/variants/android.js.map +1 -0
  42. package/dist/variants/favicon.d.ts +3 -0
  43. package/dist/variants/favicon.d.ts.map +1 -0
  44. package/dist/variants/favicon.js +23 -0
  45. package/dist/variants/favicon.js.map +1 -0
  46. package/dist/variants/splash.d.ts +3 -0
  47. package/dist/variants/splash.d.ts.map +1 -0
  48. package/dist/variants/splash.js +7 -0
  49. package/dist/variants/splash.js.map +1 -0
  50. package/dist/variants/standard.d.ts +3 -0
  51. package/dist/variants/standard.d.ts.map +1 -0
  52. package/dist/variants/standard.js +7 -0
  53. package/dist/variants/standard.js.map +1 -0
  54. package/eslint.config.js +10 -0
  55. package/package.json +57 -0
  56. package/scripts/build-release.sh +63 -0
  57. package/src/generator.ts +163 -0
  58. package/src/index.ts +73 -0
  59. package/src/lib.ts +16 -0
  60. package/src/types.ts +22 -0
  61. package/src/utils/icon-composer.ts +283 -0
  62. package/src/utils/image.ts +207 -0
  63. package/src/utils/logger.ts +61 -0
  64. package/src/utils/paths.ts +30 -0
  65. package/src/utils/update-notifier.ts +99 -0
  66. package/src/variants/android.ts +45 -0
  67. package/src/variants/favicon.ts +32 -0
  68. package/src/variants/splash.ts +11 -0
  69. package/src/variants/standard.ts +11 -0
  70. package/tests/cli.test.ts +84 -0
  71. package/tests/generator.test.ts +368 -0
  72. package/tests/helpers.ts +36 -0
  73. package/tests/utils/icon-composer.test.ts +208 -0
  74. package/tests/utils/image.test.ts +207 -0
  75. package/tests/utils/logger.test.ts +128 -0
  76. package/tests/utils/paths.test.ts +51 -0
  77. package/tests/utils/update-notifier.test.ts +184 -0
  78. package/tests/variants/android.test.ts +77 -0
  79. package/tests/variants/favicon.test.ts +36 -0
  80. package/tests/variants/splash.test.ts +35 -0
  81. package/tests/variants/standard.test.ts +35 -0
  82. package/tsconfig.json +19 -0
  83. package/vitest.config.ts +8 -0
@@ -0,0 +1,145 @@
1
+ import sharp from 'sharp';
2
+ const ADAPTIVE_ICON_SIZE = 1024;
3
+ const SAFE_ZONE_RATIO = 66 / 108;
4
+ const SAFE_ZONE_PX = Math.round(ADAPTIVE_ICON_SIZE * SAFE_ZONE_RATIO);
5
+ export async function validateSourceImage(inputPath) {
6
+ const metadata = await sharp(inputPath).metadata();
7
+ if (!metadata.format || metadata.format !== 'png') {
8
+ throw new Error(`Source image must be a PNG file (got ${metadata.format || 'unknown'})`);
9
+ }
10
+ if (!metadata.width || !metadata.height) {
11
+ throw new Error('Could not read image dimensions');
12
+ }
13
+ if (metadata.width !== metadata.height) {
14
+ throw new Error(`Source image must be square (got ${metadata.width}x${metadata.height})`);
15
+ }
16
+ return {
17
+ width: metadata.width,
18
+ height: metadata.height,
19
+ format: metadata.format,
20
+ };
21
+ }
22
+ export async function resizeImage(inputPath, width, height, outputPath) {
23
+ const info = await sharp(inputPath)
24
+ .resize(width, height, {
25
+ fit: 'contain',
26
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
27
+ })
28
+ .png()
29
+ .toFile(outputPath);
30
+ return {
31
+ filePath: outputPath,
32
+ width: info.width,
33
+ height: info.height,
34
+ size: info.size,
35
+ };
36
+ }
37
+ export async function createAdaptiveForeground(inputPath, targetSize, outputPath) {
38
+ const artwork = await sharp(inputPath)
39
+ .resize(SAFE_ZONE_PX, SAFE_ZONE_PX, {
40
+ fit: 'contain',
41
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
42
+ })
43
+ .png()
44
+ .toBuffer();
45
+ const margin = Math.round((targetSize - SAFE_ZONE_PX) / 2);
46
+ const info = await sharp({
47
+ create: {
48
+ width: targetSize,
49
+ height: targetSize,
50
+ channels: 4,
51
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
52
+ },
53
+ })
54
+ .composite([{ input: artwork, left: margin, top: margin }])
55
+ .png()
56
+ .toFile(outputPath);
57
+ return {
58
+ filePath: outputPath,
59
+ width: info.width,
60
+ height: info.height,
61
+ size: info.size,
62
+ };
63
+ }
64
+ export async function createSolidBackground(hexColor, size, outputPath) {
65
+ const { r, g, b } = parseHexColor(hexColor);
66
+ const info = await sharp({
67
+ create: {
68
+ width: size,
69
+ height: size,
70
+ channels: 4,
71
+ background: { r, g, b, alpha: 255 },
72
+ },
73
+ })
74
+ .png()
75
+ .toFile(outputPath);
76
+ return {
77
+ filePath: outputPath,
78
+ width: info.width,
79
+ height: info.height,
80
+ size: info.size,
81
+ };
82
+ }
83
+ export async function createMonochromeIcon(inputPath, targetSize, outputPath) {
84
+ const artwork = await sharp(inputPath)
85
+ .resize(SAFE_ZONE_PX, SAFE_ZONE_PX, {
86
+ fit: 'contain',
87
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
88
+ })
89
+ .grayscale()
90
+ .png()
91
+ .toBuffer();
92
+ const margin = Math.round((targetSize - SAFE_ZONE_PX) / 2);
93
+ const info = await sharp({
94
+ create: {
95
+ width: targetSize,
96
+ height: targetSize,
97
+ channels: 4,
98
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
99
+ },
100
+ })
101
+ .composite([{ input: artwork, left: margin, top: margin }])
102
+ .png()
103
+ .toFile(outputPath);
104
+ return {
105
+ filePath: outputPath,
106
+ width: info.width,
107
+ height: info.height,
108
+ size: info.size,
109
+ };
110
+ }
111
+ /**
112
+ * Apply rounded corners to an image using an SVG mask.
113
+ * Uses Apple's icon corner radius ratio (~22.37%).
114
+ */
115
+ export async function applyRoundedCorners(inputBuffer, size) {
116
+ const radius = Math.round(size * 0.2237);
117
+ const mask = Buffer.from(`<svg width="${size}" height="${size}"><rect x="0" y="0" width="${size}" height="${size}" rx="${radius}" ry="${radius}" fill="white"/></svg>`);
118
+ return sharp(inputBuffer)
119
+ .resize(size, size)
120
+ .composite([{ input: mask, blend: 'dest-in' }])
121
+ .png()
122
+ .toBuffer();
123
+ }
124
+ export function parseHexColor(hex) {
125
+ const cleaned = hex.replace(/^#/, '');
126
+ let r, g, b;
127
+ if (cleaned.length === 3) {
128
+ r = parseInt(cleaned[0] + cleaned[0], 16);
129
+ g = parseInt(cleaned[1] + cleaned[1], 16);
130
+ b = parseInt(cleaned[2] + cleaned[2], 16);
131
+ }
132
+ else if (cleaned.length === 6) {
133
+ r = parseInt(cleaned.slice(0, 2), 16);
134
+ g = parseInt(cleaned.slice(2, 4), 16);
135
+ b = parseInt(cleaned.slice(4, 6), 16);
136
+ }
137
+ else {
138
+ throw new Error(`Invalid hex color: ${hex}. Use #RGB or #RRGGBB format.`);
139
+ }
140
+ if (isNaN(r) || isNaN(g) || isNaN(b)) {
141
+ throw new Error(`Invalid hex color: ${hex}. Contains non-hex characters.`);
142
+ }
143
+ return { r, g, b };
144
+ }
145
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/utils/image.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { GenerationResult } from '../types.js';
2
+ export declare function banner(): void;
3
+ export declare function info(message: string): void;
4
+ export declare function success(message: string): void;
5
+ export declare function generated(result: GenerationResult): void;
6
+ export declare function warn(message: string): void;
7
+ export declare function error(message: string): void;
8
+ export declare function summary(results: GenerationResult[]): void;
9
+ export declare function updateNotice(currentVersion: string, latestVersion: string): void;
10
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAgB,MAAM,IAAI,IAAI,CAK7B;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAQxD;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAQzD;AAED,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,GACpB,IAAI,CAWN"}
@@ -0,0 +1,38 @@
1
+ import chalk from 'chalk';
2
+ export function banner() {
3
+ console.log(chalk.bold.hex('#FF6B35')('\n iconwolf') +
4
+ chalk.dim(' - app icon generator\n'));
5
+ }
6
+ export function info(message) {
7
+ console.log(chalk.blue(' info') + chalk.dim(' · ') + message);
8
+ }
9
+ export function success(message) {
10
+ console.log(chalk.green(' done') + chalk.dim(' · ') + message);
11
+ }
12
+ export function generated(result) {
13
+ const sizeKB = (result.size / 1024).toFixed(1);
14
+ console.log(chalk.green(' generated') +
15
+ chalk.dim(' · ') +
16
+ chalk.white(result.filePath) +
17
+ chalk.dim(` (${result.width}x${result.height}, ${sizeKB} KB)`));
18
+ }
19
+ export function warn(message) {
20
+ console.log(chalk.yellow(' warn') + chalk.dim(' · ') + message);
21
+ }
22
+ export function error(message) {
23
+ console.error(chalk.red(' error') + chalk.dim(' · ') + message);
24
+ }
25
+ export function summary(results) {
26
+ const totalSize = results.reduce((sum, r) => sum + r.size, 0);
27
+ const totalKB = (totalSize / 1024).toFixed(1);
28
+ console.log(chalk.bold(`\n ${results.length} file${results.length === 1 ? '' : 's'} generated`) + chalk.dim(` (${totalKB} KB total)\n`));
29
+ }
30
+ export function updateNotice(currentVersion, latestVersion) {
31
+ console.log(chalk.yellow(' update') +
32
+ chalk.dim(' · ') +
33
+ `New version available: ${chalk.dim(currentVersion)} → ${chalk.green(latestVersion)}`);
34
+ console.log(' ' +
35
+ chalk.dim(' · ') +
36
+ `Run ${chalk.cyan('brew upgrade iconwolf')} to update\n`);
37
+ }
38
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,UAAU,MAAM;IACpB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC;QACvC,KAAK,CAAC,GAAG,CAAC,yBAAyB,CAAC,CACvC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAwB;IAChD,MAAM,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC;QACxB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;QAChB,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC5B,KAAK,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,MAAM,MAAM,CAAC,CACjE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,OAA2B;IACjD,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC9D,MAAM,OAAO,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,IAAI,CACR,OAAO,OAAO,CAAC,MAAM,QAAQ,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,CACzE,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,OAAO,cAAc,CAAC,CAC1C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,cAAsB,EACtB,aAAqB;IAErB,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC;QACtB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;QAChB,0BAA0B,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CACxF,CAAC;IACF,OAAO,CAAC,GAAG,CACT,WAAW;QACT,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;QAChB,OAAO,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,cAAc,CAC3D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,17 @@
1
+ export declare const DEFAULT_OUTPUT_DIR = "./assets/images";
2
+ /**
3
+ * Detect the default output directory based on project structure.
4
+ * If a `src/` directory exists (common in React Native/Expo projects),
5
+ * defaults to `./src/assets/images/`. Otherwise uses `./assets/images/`.
6
+ */
7
+ export declare function resolveDefaultOutputDir(): string;
8
+ export declare const OUTPUT_FILES: {
9
+ readonly icon: "icon.png";
10
+ readonly androidForeground: "adaptive-icon.png";
11
+ readonly androidBackground: "android-icon-background.png";
12
+ readonly androidMonochrome: "monochrome-icon.png";
13
+ readonly favicon: "favicon.png";
14
+ readonly splashIcon: "splash-icon.png";
15
+ };
16
+ export declare function resolveOutputPath(outputDir: string, fileName: string): string;
17
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAMhD;AAED,eAAO,MAAM,YAAY;;;;;;;CAOf,CAAC;AAEX,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7E"}
@@ -0,0 +1,27 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export const DEFAULT_OUTPUT_DIR = './assets/images';
4
+ /**
5
+ * Detect the default output directory based on project structure.
6
+ * If a `src/` directory exists (common in React Native/Expo projects),
7
+ * defaults to `./src/assets/images/`. Otherwise uses `./assets/images/`.
8
+ */
9
+ export function resolveDefaultOutputDir() {
10
+ const srcDir = path.resolve('src');
11
+ if (fs.existsSync(srcDir) && fs.statSync(srcDir).isDirectory()) {
12
+ return './src/assets/images';
13
+ }
14
+ return DEFAULT_OUTPUT_DIR;
15
+ }
16
+ export const OUTPUT_FILES = {
17
+ icon: 'icon.png',
18
+ androidForeground: 'adaptive-icon.png',
19
+ androidBackground: 'android-icon-background.png',
20
+ androidMonochrome: 'monochrome-icon.png',
21
+ favicon: 'favicon.png',
22
+ splashIcon: 'splash-icon.png',
23
+ };
24
+ export function resolveOutputPath(outputDir, fileName) {
25
+ return path.resolve(outputDir, fileName);
26
+ }
27
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEpD;;;;GAIG;AACH,MAAM,UAAU,uBAAuB;IACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QAC/D,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,UAAU;IAChB,iBAAiB,EAAE,mBAAmB;IACtC,iBAAiB,EAAE,6BAA6B;IAChD,iBAAiB,EAAE,qBAAqB;IACxC,OAAO,EAAE,aAAa;IACtB,UAAU,EAAE,iBAAiB;CACrB,CAAC;AAEX,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,QAAgB;IACnE,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface UpdateInfo {
2
+ updateAvailable: boolean;
3
+ currentVersion: string;
4
+ latestVersion: string;
5
+ }
6
+ export declare function isNewerVersion(current: string, latest: string): boolean;
7
+ export declare function readCachedUpdateInfo(currentVersion: string): UpdateInfo | null;
8
+ export declare function refreshCacheInBackground(currentCacheFile?: string): Promise<void>;
9
+ //# sourceMappingURL=update-notifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-notifier.d.ts","sourceRoot":"","sources":["../../src/utils/update-notifier.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB;AAOD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAavE;AAED,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,MAAM,GACrB,UAAU,GAAG,IAAI,CAcnB;AAED,wBAAsB,wBAAwB,CAC5C,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,IAAI,CAAC,CAyCf"}
@@ -0,0 +1,80 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import os from 'node:os';
4
+ const CACHE_DIR = path.join(os.homedir(), '.iconwolf');
5
+ const CACHE_FILE = path.join(CACHE_DIR, 'update-check.json');
6
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
7
+ const FETCH_TIMEOUT_MS = 5000;
8
+ const RELEASES_URL = 'https://api.github.com/repos/MrDemonWolf/iconwolf/releases/latest';
9
+ export function isNewerVersion(current, latest) {
10
+ const currentParts = current.split('.').map(Number);
11
+ const latestParts = latest.split('.').map(Number);
12
+ const len = Math.max(currentParts.length, latestParts.length);
13
+ for (let i = 0; i < len; i++) {
14
+ const c = currentParts[i] ?? 0;
15
+ const l = latestParts[i] ?? 0;
16
+ if (l > c)
17
+ return true;
18
+ if (l < c)
19
+ return false;
20
+ }
21
+ return false;
22
+ }
23
+ export function readCachedUpdateInfo(currentVersion) {
24
+ try {
25
+ const raw = fs.readFileSync(CACHE_FILE, 'utf-8');
26
+ const data = JSON.parse(raw);
27
+ if (!data.latestVersion)
28
+ return null;
29
+ return {
30
+ updateAvailable: isNewerVersion(currentVersion, data.latestVersion),
31
+ currentVersion,
32
+ latestVersion: data.latestVersion,
33
+ };
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ export async function refreshCacheInBackground(currentCacheFile) {
40
+ const cacheFile = currentCacheFile ?? CACHE_FILE;
41
+ const cacheDir = path.dirname(cacheFile);
42
+ try {
43
+ // Check if cache is still fresh
44
+ try {
45
+ const stat = fs.statSync(cacheFile);
46
+ if (Date.now() - stat.mtimeMs < CACHE_TTL_MS)
47
+ return;
48
+ }
49
+ catch {
50
+ // Cache missing, proceed with fetch
51
+ }
52
+ const controller = new AbortController();
53
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
54
+ try {
55
+ const res = await fetch(RELEASES_URL, {
56
+ signal: controller.signal,
57
+ headers: { Accept: 'application/vnd.github.v3+json' },
58
+ });
59
+ if (!res.ok)
60
+ return;
61
+ const body = (await res.json());
62
+ if (!body.tag_name)
63
+ return;
64
+ const latestVersion = body.tag_name.replace(/^v/, '');
65
+ const cacheData = {
66
+ latestVersion,
67
+ checkedAt: Date.now(),
68
+ };
69
+ fs.mkdirSync(cacheDir, { recursive: true });
70
+ fs.writeFileSync(cacheFile, JSON.stringify(cacheData));
71
+ }
72
+ finally {
73
+ clearTimeout(timeout);
74
+ }
75
+ }
76
+ catch {
77
+ // Silently swallow all errors
78
+ }
79
+ }
80
+ //# sourceMappingURL=update-notifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-notifier.js","sourceRoot":"","sources":["../../src/utils/update-notifier.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { GenerationResult } from '../types.js';
2
+ export declare function generateAndroidIcons(inputPath: string, outputDir: string, bgColor: string, options?: {
3
+ includeBackground?: boolean;
4
+ }): Promise<GenerationResult[]>;
5
+ //# sourceMappingURL=android.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android.d.ts","sourceRoot":"","sources":["../../src/variants/android.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAE,GACxC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CA6B7B"}
@@ -0,0 +1,18 @@
1
+ import { createAdaptiveForeground, createSolidBackground, createMonochromeIcon, } from '../utils/image.js';
2
+ import { resolveOutputPath, OUTPUT_FILES } from '../utils/paths.js';
3
+ const ANDROID_ICON_SIZE = 1024;
4
+ export async function generateAndroidIcons(inputPath, outputDir, bgColor, options) {
5
+ const includeBackground = options?.includeBackground ?? false;
6
+ const foregroundPromise = createAdaptiveForeground(inputPath, ANDROID_ICON_SIZE, resolveOutputPath(outputDir, OUTPUT_FILES.androidForeground));
7
+ if (!includeBackground) {
8
+ const foreground = await foregroundPromise;
9
+ return [foreground];
10
+ }
11
+ const [foreground, background, monochrome] = await Promise.all([
12
+ foregroundPromise,
13
+ createSolidBackground(bgColor, ANDROID_ICON_SIZE, resolveOutputPath(outputDir, OUTPUT_FILES.androidBackground)),
14
+ createMonochromeIcon(inputPath, ANDROID_ICON_SIZE, resolveOutputPath(outputDir, OUTPUT_FILES.androidMonochrome)),
15
+ ]);
16
+ return [foreground, background, monochrome];
17
+ }
18
+ //# sourceMappingURL=android.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"android.js","sourceRoot":"","sources":["../../src/variants/android.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGpE,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,SAAiB,EACjB,OAAe,EACf,OAAyC;IAEzC,MAAM,iBAAiB,GAAG,OAAO,EAAE,iBAAiB,IAAI,KAAK,CAAC;IAE9D,MAAM,iBAAiB,GAAG,wBAAwB,CAChD,SAAS,EACT,iBAAiB,EACjB,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAC7D,CAAC;IAEF,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC;QAC3C,OAAO,CAAC,UAAU,CAAC,CAAC;IACtB,CAAC;IAED,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC7D,iBAAiB;QACjB,qBAAqB,CACnB,OAAO,EACP,iBAAiB,EACjB,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAC7D;QACD,oBAAoB,CAClB,SAAS,EACT,iBAAiB,EACjB,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAC7D;KACF,CAAC,CAAC;IAEH,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { GenerationResult } from '../types.js';
2
+ export declare function generateFavicon(inputPath: string, outputDir: string): Promise<GenerationResult>;
3
+ //# sourceMappingURL=favicon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"favicon.d.ts","sourceRoot":"","sources":["../../src/variants/favicon.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAqB3B"}
@@ -0,0 +1,23 @@
1
+ import sharp from 'sharp';
2
+ import { applyRoundedCorners } from '../utils/image.js';
3
+ import { resolveOutputPath, OUTPUT_FILES } from '../utils/paths.js';
4
+ const FAVICON_SIZE = 48;
5
+ export async function generateFavicon(inputPath, outputDir) {
6
+ const outputPath = resolveOutputPath(outputDir, OUTPUT_FILES.favicon);
7
+ const resized = await sharp(inputPath)
8
+ .resize(FAVICON_SIZE, FAVICON_SIZE, {
9
+ fit: 'contain',
10
+ background: { r: 0, g: 0, b: 0, alpha: 0 },
11
+ })
12
+ .png()
13
+ .toBuffer();
14
+ const rounded = await applyRoundedCorners(resized, FAVICON_SIZE);
15
+ const info = await sharp(rounded).png().toFile(outputPath);
16
+ return {
17
+ filePath: outputPath,
18
+ width: info.width,
19
+ height: info.height,
20
+ size: info.size,
21
+ };
22
+ }
23
+ //# sourceMappingURL=favicon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"favicon.js","sourceRoot":"","sources":["../../src/variants/favicon.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGpE,MAAM,YAAY,GAAG,EAAE,CAAC;AAExB,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAAiB,EACjB,SAAiB;IAEjB,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;IAEtE,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC;SACnC,MAAM,CAAC,YAAY,EAAE,YAAY,EAAE;QAClC,GAAG,EAAE,SAAS;QACd,UAAU,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;KAC3C,CAAC;SACD,GAAG,EAAE;SACL,QAAQ,EAAE,CAAC;IAEd,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAEjE,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAE3D,OAAO;QACL,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,IAAI;KAChB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { GenerationResult } from '../types.js';
2
+ export declare function generateSplashIcon(inputPath: string, outputDir: string): Promise<GenerationResult>;
3
+ //# sourceMappingURL=splash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splash.d.ts","sourceRoot":"","sources":["../../src/variants/splash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAG3B"}
@@ -0,0 +1,7 @@
1
+ import { resizeImage } from '../utils/image.js';
2
+ import { resolveOutputPath, OUTPUT_FILES } from '../utils/paths.js';
3
+ export async function generateSplashIcon(inputPath, outputDir) {
4
+ const outputPath = resolveOutputPath(outputDir, OUTPUT_FILES.splashIcon);
5
+ return resizeImage(inputPath, 1024, 1024, outputPath);
6
+ }
7
+ //# sourceMappingURL=splash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"splash.js","sourceRoot":"","sources":["../../src/variants/splash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGpE,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,SAAiB,EACjB,SAAiB;IAEjB,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;IACzE,OAAO,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { GenerationResult } from '../types.js';
2
+ export declare function generateStandardIcon(inputPath: string, outputDir: string): Promise<GenerationResult>;
3
+ //# sourceMappingURL=standard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard.d.ts","sourceRoot":"","sources":["../../src/variants/standard.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAsB,oBAAoB,CACxC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAG3B"}
@@ -0,0 +1,7 @@
1
+ import { resizeImage } from '../utils/image.js';
2
+ import { resolveOutputPath, OUTPUT_FILES } from '../utils/paths.js';
3
+ export async function generateStandardIcon(inputPath, outputDir) {
4
+ const outputPath = resolveOutputPath(outputDir, OUTPUT_FILES.icon);
5
+ return resizeImage(inputPath, 1024, 1024, outputPath);
6
+ }
7
+ //# sourceMappingURL=standard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"standard.js","sourceRoot":"","sources":["../../src/variants/standard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGpE,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,SAAiB,EACjB,SAAiB;IAEjB,MAAM,UAAU,GAAG,iBAAiB,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC;IACnE,OAAO,WAAW,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,10 @@
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+
4
+ export default tseslint.config(
5
+ eslint.configs.recommended,
6
+ ...tseslint.configs.recommended,
7
+ {
8
+ ignores: ['dist/', 'dist-bin/', 'node_modules/', 'scripts/'],
9
+ },
10
+ );
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@mrdemonwolf/iconwolf",
3
+ "version": "0.1.1",
4
+ "description": "A CLI tool that takes an iOS app compositor icon and generates all necessary icon variants for cross-platform use.",
5
+ "type": "module",
6
+ "main": "./dist/lib.js",
7
+ "exports": {
8
+ ".": "./dist/lib.js",
9
+ "./cli": "./dist/index.js"
10
+ },
11
+ "bin": {
12
+ "iconwolf": "./dist/index.js"
13
+ },
14
+ "keywords": [
15
+ "icon",
16
+ "app-icon",
17
+ "expo",
18
+ "react-native",
19
+ "android",
20
+ "adaptive-icon",
21
+ "favicon",
22
+ "splash",
23
+ "cli"
24
+ ],
25
+ "author": "MrDemonWolf",
26
+ "license": "MIT",
27
+ "dependencies": {
28
+ "chalk": "^5.4.1",
29
+ "commander": "^13.1.0",
30
+ "sharp": "^0.33.5"
31
+ },
32
+ "devDependencies": {
33
+ "@eslint/js": "^9.39.2",
34
+ "@types/node": "^22.13.1",
35
+ "@typescript-eslint/eslint-plugin": "^8.24.0",
36
+ "@typescript-eslint/parser": "^8.24.0",
37
+ "esbuild": "^0.27.3",
38
+ "eslint": "^9.19.0",
39
+ "prettier": "^3.4.2",
40
+ "typescript": "^5.7.3",
41
+ "typescript-eslint": "^8.55.0",
42
+ "vitest": "^3.0.5"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc",
49
+ "dev": "tsc --watch",
50
+ "start": "node dist/index.js",
51
+ "lint": "eslint src/ tests/",
52
+ "format": "prettier --write src/ tests/",
53
+ "format:check": "prettier --check src/ tests/",
54
+ "test": "vitest run",
55
+ "build:release": "bash scripts/build-release.sh"
56
+ }
57
+ }
@@ -0,0 +1,63 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ PLATFORM="${1:-macos-arm64}"
5
+ VERSION=$(node -p "require('./package.json').version")
6
+ STAGING="dist-bin/iconwolf"
7
+
8
+ echo "Building iconwolf v${VERSION} for ${PLATFORM}"
9
+
10
+ # Clean
11
+ rm -rf dist-bin
12
+ mkdir -p "$STAGING/lib"
13
+
14
+ # Step 1: Compile TypeScript
15
+ echo " Compiling TypeScript..."
16
+ pnpm run build
17
+
18
+ # Step 2: Bundle into single CJS file (sharp stays external)
19
+ echo " Bundling with esbuild..."
20
+ pnpm exec esbuild dist/index.js \
21
+ --bundle \
22
+ --platform=node \
23
+ --format=cjs \
24
+ --external:sharp \
25
+ --outfile="$STAGING/lib/bundle.cjs"
26
+
27
+ # Step 3: Install sharp with all deps into staging (production, self-contained)
28
+ echo " Installing sharp into staging..."
29
+ SHARP_VERSION=$(node -p "require('./package.json').dependencies.sharp")
30
+ cd "$STAGING/lib"
31
+ cat > package.json << EOF
32
+ {"private":true,"dependencies":{"sharp":"${SHARP_VERSION}"}}
33
+ EOF
34
+ npm install --omit=dev 2>&1 | tail -1
35
+ rm -f package.json package-lock.json
36
+ cd - > /dev/null
37
+
38
+ # Step 4: Create CLI wrapper
39
+ echo " Creating CLI wrapper..."
40
+ mkdir -p "$STAGING/bin"
41
+ cat > "$STAGING/bin/iconwolf" << 'WRAPPER'
42
+ #!/bin/bash
43
+ # Resolve symlinks (Homebrew symlinks from /opt/homebrew/bin/ to libexec/)
44
+ SOURCE="$0"
45
+ while [ -L "$SOURCE" ]; do
46
+ LINK_DIR="$(cd "$(dirname "$SOURCE")" && pwd)"
47
+ SOURCE="$(readlink "$SOURCE")"
48
+ [[ "$SOURCE" != /* ]] && SOURCE="$LINK_DIR/$SOURCE"
49
+ done
50
+ DIR="$(cd "$(dirname "$SOURCE")/.." && pwd)"
51
+ NODE_PATH="$DIR/lib/node_modules" exec node "$DIR/lib/bundle.cjs" "$@"
52
+ WRAPPER
53
+ chmod +x "$STAGING/bin/iconwolf"
54
+
55
+ # Step 5: Create tarball
56
+ ARCHIVE="dist-bin/iconwolf-${PLATFORM}.tar.gz"
57
+ echo " Creating archive..."
58
+ tar -czf "$ARCHIVE" -C dist-bin iconwolf/
59
+
60
+ # Show result
61
+ SIZE=$(du -h "$ARCHIVE" | cut -f1)
62
+ echo ""
63
+ echo "Done! ${ARCHIVE} (${SIZE})"