plusui-native 0.2.108 → 0.2.109

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plusui-native",
3
- "version": "0.2.108",
3
+ "version": "0.2.109",
4
4
  "description": "PlusUI CLI - Build C++ desktop apps modern UI ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -27,11 +27,11 @@
27
27
  "semver": "^7.6.0",
28
28
  "which": "^4.0.0",
29
29
  "execa": "^8.0.1",
30
- "plusui-native-builder": "^0.1.106",
31
- "plusui-native-connect": "^0.1.106"
30
+ "plusui-native-builder": "^0.1.107",
31
+ "plusui-native-connect": "^0.1.107"
32
32
  },
33
33
  "peerDependencies": {
34
- "plusui-native-connect": "^0.1.106"
34
+ "plusui-native-connect": "^0.1.107"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -1,251 +1,251 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Icon Generator for PlusUI
5
- * Generates all platform-specific icon formats from a single source icon
6
- *
7
- * Supports:
8
- * - Windows: .ico (16x16, 32x32, 48x48, 64x64, 128x128, 256x256)
9
- * - macOS: .icns (16x16@1x/2x, 32x32@1x/2x, 128x128@1x/2x, 256x256@1x/2x, 512x512@1x/2x)
10
- * - Linux: PNG (16x16, 32x32, 48x48, 64x64, 128x128, 256x256, 512x512)
11
- * - Tray icons: 16x16, 32x32 (for system tray)
12
- * - Taskbar/Title bar: 32x32, 48x48
13
- */
14
-
15
- import { readFile, writeFile, mkdir } from 'fs/promises';
16
- import { existsSync } from 'fs';
17
- import { join, basename, extname } from 'path';
18
- import { execSync } from 'child_process';
19
-
20
- const SIZES = {
21
- windows: [16, 32, 48, 64, 128, 256],
22
- macos: [16, 32, 128, 256, 512],
23
- linux: [16, 32, 48, 64, 128, 256, 512],
24
- tray: [16, 32],
25
- taskbar: [32, 48],
26
- };
27
-
28
- class IconGenerator {
29
- constructor() {
30
- this.hasSharp = this.checkSharp();
31
- this.hasImageMagick = this.checkImageMagick();
32
- }
33
-
34
- checkSharp() {
35
- try {
36
- require.resolve('sharp');
37
- return true;
38
- } catch {
39
- return false;
40
- }
41
- }
42
-
43
- checkImageMagick() {
44
- try {
45
- execSync('magick -version', { stdio: 'ignore' });
46
- return true;
47
- } catch {
48
- try {
49
- execSync('convert -version', { stdio: 'ignore' });
50
- return true;
51
- } catch {
52
- return false;
53
- }
54
- }
55
- }
56
-
57
- async checkDependencies() {
58
- if (!this.hasSharp && !this.hasImageMagick) {
59
- console.log('⚠️ No image processor found.');
60
- console.log('\nOption 1 (Recommended): Install Sharp');
61
- console.log(' npm install sharp --save-dev');
62
- console.log('\nOption 2: Install ImageMagick');
63
- console.log(' Windows: winget install ImageMagick.ImageMagick');
64
- console.log(' macOS: brew install imagemagick');
65
- console.log(' Linux: sudo apt install imagemagick');
66
- return false;
67
- }
68
- return true;
69
- }
70
-
71
- async resizeWithSharp(inputPath, outputPath, size) {
72
- const sharp = require('sharp');
73
- await sharp(inputPath)
74
- .resize(size, size, {
75
- fit: 'contain',
76
- background: { r: 0, g: 0, b: 0, alpha: 0 }
77
- })
78
- .png()
79
- .toFile(outputPath);
80
- }
81
-
82
- async resizeWithImageMagick(inputPath, outputPath, size) {
83
- const cmd = this.hasImageMagick && execSync('which magick 2>/dev/null', { encoding: 'utf8' }).trim()
84
- ? 'magick'
85
- : 'convert';
86
-
87
- execSync(
88
- `${cmd} "${inputPath}" -resize ${size}x${size} -background none -gravity center -extent ${size}x${size} "${outputPath}"`,
89
- { stdio: 'ignore' }
90
- );
91
- }
92
-
93
- async resize(inputPath, outputPath, size) {
94
- if (this.hasSharp) {
95
- await this.resizeWithSharp(inputPath, outputPath, size);
96
- } else {
97
- await this.resizeWithImageMagick(inputPath, outputPath, size);
98
- }
99
- }
100
-
101
- async generateWindowsIco(inputPath, outputPath) {
102
- console.log(' 📦 Generating Windows .ico...');
103
- const tempDir = join(process.cwd(), '.icon-temp');
104
- await mkdir(tempDir, { recursive: true });
105
-
106
- const pngPaths = [];
107
- for (const size of SIZES.windows) {
108
- const pngPath = join(tempDir, `icon_${size}.png`);
109
- await this.resize(inputPath, pngPath, size);
110
- pngPaths.push(pngPath);
111
- }
112
-
113
- // Try to use ImageMagick to create .ico
114
- if (this.hasImageMagick) {
115
- try {
116
- const cmd = execSync('which magick 2>/dev/null', { encoding: 'utf8' }).trim() ? 'magick' : 'convert';
117
- execSync(`${cmd} ${pngPaths.join(' ')} "${outputPath}"`, { stdio: 'ignore' });
118
- console.log(' ✓ Created app.ico');
119
- } catch (e) {
120
- console.log(' ⚠️ Could not create .ico, using PNG fallback');
121
- // Copy largest PNG as fallback
122
- const fallback = pngPaths[pngPaths.length - 1];
123
- await writeFile(outputPath.replace('.ico', '.png'), await readFile(fallback));
124
- }
125
- } else {
126
- // Just copy PNGs
127
- for (let i = 0; i < SIZES.windows.length; i++) {
128
- const size = SIZES.windows[i];
129
- const dest = outputPath.replace('.ico', `_${size}.png`);
130
- await writeFile(dest, await readFile(pngPaths[i]));
131
- console.log(` ✓ Created icon_${size}.png`);
132
- }
133
- }
134
-
135
- // Cleanup temp
136
- for (const p of pngPaths) {
137
- try { await rm(p); } catch {}
138
- }
139
- }
140
-
141
- async generateMacOSIcns(inputPath, outputPath) {
142
- console.log(' 🍎 Generating macOS .icns...');
143
- const iconsetDir = outputPath.replace('.icns', '.iconset');
144
- await mkdir(iconsetDir, { recursive: true });
145
-
146
- const sizes = [
147
- [16, 'icon_16x16.png'],
148
- [32, 'icon_16x16@2x.png'],
149
- [32, 'icon_32x32.png'],
150
- [64, 'icon_32x32@2x.png'],
151
- [128, 'icon_128x128.png'],
152
- [256, 'icon_128x128@2x.png'],
153
- [256, 'icon_256x256.png'],
154
- [512, 'icon_256x256@2x.png'],
155
- [512, 'icon_512x512.png'],
156
- [1024, 'icon_512x512@2x.png'],
157
- ];
158
-
159
- for (const [size, filename] of sizes) {
160
- const dest = join(iconsetDir, filename);
161
- await this.resize(inputPath, dest, size);
162
- }
163
-
164
- // Try to create .icns on macOS
165
- if (process.platform === 'darwin') {
166
- try {
167
- execSync(`iconutil -c icns "${iconsetDir}" -o "${outputPath}"`, { stdio: 'ignore' });
168
- console.log(' ✓ Created app.icns');
169
- } catch {
170
- console.log(' ⚠️ Could not create .icns, keeping .iconset folder');
171
- }
172
- } else {
173
- console.log(' ℹ️ .iconset folder created (use iconutil on macOS to convert)');
174
- }
175
- }
176
-
177
- async generateLinuxIcons(inputPath, outputDir) {
178
- console.log(' 🐧 Generating Linux icons...');
179
- await mkdir(outputDir, { recursive: true });
180
-
181
- for (const size of SIZES.linux) {
182
- const dest = join(outputDir, `icon_${size}x${size}.png`);
183
- await this.resize(inputPath, dest, size);
184
- console.log(` ✓ Created icon_${size}x${size}.png`);
185
- }
186
- }
187
-
188
- async generateTrayIcons(inputPath, outputDir) {
189
- console.log(' 🔔 Generating tray icons...');
190
- await mkdir(outputDir, { recursive: true });
191
-
192
- for (const size of SIZES.tray) {
193
- const dest = join(outputDir, `tray_${size}x${size}.png`);
194
- await this.resize(inputPath, dest, size);
195
- console.log(` ✓ Created tray_${size}x${size}.png`);
196
- }
197
- }
198
-
199
- async generate(inputPath, outputBase) {
200
- console.log(`\n🎨 Generating icons from: ${basename(inputPath)}\n`);
201
-
202
- if (!existsSync(inputPath)) {
203
- throw new Error(`Input icon not found: ${inputPath}`);
204
- }
205
-
206
- const hasDeps = await this.checkDependencies();
207
- if (!hasDeps) {
208
- throw new Error('Missing dependencies');
209
- }
210
-
211
- // Create output directories
212
- const dirs = {
213
- windows: join(outputBase, 'windows'),
214
- macos: join(outputBase, 'macos'),
215
- linux: join(outputBase, 'linux'),
216
- tray: join(outputBase, 'tray'),
217
- };
218
-
219
- for (const dir of Object.values(dirs)) {
220
- await mkdir(dir, { recursive: true });
221
- }
222
-
223
- // Generate platform-specific icons
224
- await this.generateWindowsIco(inputPath, join(dirs.windows, 'app.ico'));
225
- await this.generateMacOSIcns(inputPath, join(dirs.macos, 'app.icns'));
226
- await this.generateLinuxIcons(inputPath, dirs.linux);
227
- await this.generateTrayIcons(inputPath, dirs.tray);
228
-
229
- console.log('\n✨ Icon generation complete!\n');
230
- console.log('Generated icons:');
231
- console.log(` 📁 ${dirs.windows}`);
232
- console.log(` 📁 ${dirs.macos}`);
233
- console.log(` 📁 ${dirs.linux}`);
234
- console.log(` 📁 ${dirs.tray}`);
235
- }
236
- }
237
-
238
- // CLI
239
- if (import.meta.url === `file://${process.argv[1]}`) {
240
- const args = process.argv.slice(2);
241
- const inputPath = args[0] || join(process.cwd(), 'assets', 'icon.png');
242
- const outputBase = args[1] || join(process.cwd(), 'assets', 'icons');
243
-
244
- const generator = new IconGenerator();
245
- generator.generate(inputPath, outputBase).catch(err => {
246
- console.error('❌ Error:', err.message);
247
- process.exit(1);
248
- });
249
- }
250
-
251
- export { IconGenerator };
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Icon Generator for PlusUI
5
+ * Generates all platform-specific icon formats from a single source icon
6
+ *
7
+ * Supports:
8
+ * - Windows: .ico (16x16, 32x32, 48x48, 64x64, 128x128, 256x256)
9
+ * - macOS: .icns (16x16@1x/2x, 32x32@1x/2x, 128x128@1x/2x, 256x256@1x/2x, 512x512@1x/2x)
10
+ * - Linux: PNG (16x16, 32x32, 48x48, 64x64, 128x128, 256x256, 512x512)
11
+ * - Tray icons: 16x16, 32x32 (for system tray)
12
+ * - Taskbar/Title bar: 32x32, 48x48
13
+ */
14
+
15
+ import { readFile, writeFile, mkdir } from 'fs/promises';
16
+ import { existsSync } from 'fs';
17
+ import { join, basename, extname } from 'path';
18
+ import { execSync } from 'child_process';
19
+
20
+ const SIZES = {
21
+ windows: [16, 32, 48, 64, 128, 256],
22
+ macos: [16, 32, 128, 256, 512],
23
+ linux: [16, 32, 48, 64, 128, 256, 512],
24
+ tray: [16, 32],
25
+ taskbar: [32, 48],
26
+ };
27
+
28
+ class IconGenerator {
29
+ constructor() {
30
+ this.hasSharp = this.checkSharp();
31
+ this.hasImageMagick = this.checkImageMagick();
32
+ }
33
+
34
+ checkSharp() {
35
+ try {
36
+ require.resolve('sharp');
37
+ return true;
38
+ } catch {
39
+ return false;
40
+ }
41
+ }
42
+
43
+ checkImageMagick() {
44
+ try {
45
+ execSync('magick -version', { stdio: 'ignore' });
46
+ return true;
47
+ } catch {
48
+ try {
49
+ execSync('convert -version', { stdio: 'ignore' });
50
+ return true;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ }
56
+
57
+ async checkDependencies() {
58
+ if (!this.hasSharp && !this.hasImageMagick) {
59
+ console.log('⚠️ No image processor found.');
60
+ console.log('\nOption 1 (Recommended): Install Sharp');
61
+ console.log(' npm install sharp --save-dev');
62
+ console.log('\nOption 2: Install ImageMagick');
63
+ console.log(' Windows: winget install ImageMagick.ImageMagick');
64
+ console.log(' macOS: brew install imagemagick');
65
+ console.log(' Linux: sudo apt install imagemagick');
66
+ return false;
67
+ }
68
+ return true;
69
+ }
70
+
71
+ async resizeWithSharp(inputPath, outputPath, size) {
72
+ const sharp = require('sharp');
73
+ await sharp(inputPath)
74
+ .resize(size, size, {
75
+ fit: 'contain',
76
+ background: { r: 0, g: 0, b: 0, alpha: 0 }
77
+ })
78
+ .png()
79
+ .toFile(outputPath);
80
+ }
81
+
82
+ async resizeWithImageMagick(inputPath, outputPath, size) {
83
+ const cmd = this.hasImageMagick && execSync('which magick 2>/dev/null', { encoding: 'utf8' }).trim()
84
+ ? 'magick'
85
+ : 'convert';
86
+
87
+ execSync(
88
+ `${cmd} "${inputPath}" -resize ${size}x${size} -background none -gravity center -extent ${size}x${size} "${outputPath}"`,
89
+ { stdio: 'ignore' }
90
+ );
91
+ }
92
+
93
+ async resize(inputPath, outputPath, size) {
94
+ if (this.hasSharp) {
95
+ await this.resizeWithSharp(inputPath, outputPath, size);
96
+ } else {
97
+ await this.resizeWithImageMagick(inputPath, outputPath, size);
98
+ }
99
+ }
100
+
101
+ async generateWindowsIco(inputPath, outputPath) {
102
+ console.log(' 📦 Generating Windows .ico...');
103
+ const tempDir = join(process.cwd(), '.icon-temp');
104
+ await mkdir(tempDir, { recursive: true });
105
+
106
+ const pngPaths = [];
107
+ for (const size of SIZES.windows) {
108
+ const pngPath = join(tempDir, `icon_${size}.png`);
109
+ await this.resize(inputPath, pngPath, size);
110
+ pngPaths.push(pngPath);
111
+ }
112
+
113
+ // Try to use ImageMagick to create .ico
114
+ if (this.hasImageMagick) {
115
+ try {
116
+ const cmd = execSync('which magick 2>/dev/null', { encoding: 'utf8' }).trim() ? 'magick' : 'convert';
117
+ execSync(`${cmd} ${pngPaths.join(' ')} "${outputPath}"`, { stdio: 'ignore' });
118
+ console.log(' ✓ Created app.ico');
119
+ } catch (e) {
120
+ console.log(' ⚠️ Could not create .ico, using PNG fallback');
121
+ // Copy largest PNG as fallback
122
+ const fallback = pngPaths[pngPaths.length - 1];
123
+ await writeFile(outputPath.replace('.ico', '.png'), await readFile(fallback));
124
+ }
125
+ } else {
126
+ // Just copy PNGs
127
+ for (let i = 0; i < SIZES.windows.length; i++) {
128
+ const size = SIZES.windows[i];
129
+ const dest = outputPath.replace('.ico', `_${size}.png`);
130
+ await writeFile(dest, await readFile(pngPaths[i]));
131
+ console.log(` ✓ Created icon_${size}.png`);
132
+ }
133
+ }
134
+
135
+ // Cleanup temp
136
+ for (const p of pngPaths) {
137
+ try { await rm(p); } catch {}
138
+ }
139
+ }
140
+
141
+ async generateMacOSIcns(inputPath, outputPath) {
142
+ console.log(' 🍎 Generating macOS .icns...');
143
+ const iconsetDir = outputPath.replace('.icns', '.iconset');
144
+ await mkdir(iconsetDir, { recursive: true });
145
+
146
+ const sizes = [
147
+ [16, 'icon_16x16.png'],
148
+ [32, 'icon_16x16@2x.png'],
149
+ [32, 'icon_32x32.png'],
150
+ [64, 'icon_32x32@2x.png'],
151
+ [128, 'icon_128x128.png'],
152
+ [256, 'icon_128x128@2x.png'],
153
+ [256, 'icon_256x256.png'],
154
+ [512, 'icon_256x256@2x.png'],
155
+ [512, 'icon_512x512.png'],
156
+ [1024, 'icon_512x512@2x.png'],
157
+ ];
158
+
159
+ for (const [size, filename] of sizes) {
160
+ const dest = join(iconsetDir, filename);
161
+ await this.resize(inputPath, dest, size);
162
+ }
163
+
164
+ // Try to create .icns on macOS
165
+ if (process.platform === 'darwin') {
166
+ try {
167
+ execSync(`iconutil -c icns "${iconsetDir}" -o "${outputPath}"`, { stdio: 'ignore' });
168
+ console.log(' ✓ Created app.icns');
169
+ } catch {
170
+ console.log(' ⚠️ Could not create .icns, keeping .iconset folder');
171
+ }
172
+ } else {
173
+ console.log(' ℹ️ .iconset folder created (use iconutil on macOS to convert)');
174
+ }
175
+ }
176
+
177
+ async generateLinuxIcons(inputPath, outputDir) {
178
+ console.log(' 🐧 Generating Linux icons...');
179
+ await mkdir(outputDir, { recursive: true });
180
+
181
+ for (const size of SIZES.linux) {
182
+ const dest = join(outputDir, `icon_${size}x${size}.png`);
183
+ await this.resize(inputPath, dest, size);
184
+ console.log(` ✓ Created icon_${size}x${size}.png`);
185
+ }
186
+ }
187
+
188
+ async generateTrayIcons(inputPath, outputDir) {
189
+ console.log(' 🔔 Generating tray icons...');
190
+ await mkdir(outputDir, { recursive: true });
191
+
192
+ for (const size of SIZES.tray) {
193
+ const dest = join(outputDir, `tray_${size}x${size}.png`);
194
+ await this.resize(inputPath, dest, size);
195
+ console.log(` ✓ Created tray_${size}x${size}.png`);
196
+ }
197
+ }
198
+
199
+ async generate(inputPath, outputBase) {
200
+ console.log(`\n🎨 Generating icons from: ${basename(inputPath)}\n`);
201
+
202
+ if (!existsSync(inputPath)) {
203
+ throw new Error(`Input icon not found: ${inputPath}`);
204
+ }
205
+
206
+ const hasDeps = await this.checkDependencies();
207
+ if (!hasDeps) {
208
+ throw new Error('Missing dependencies');
209
+ }
210
+
211
+ // Create output directories
212
+ const dirs = {
213
+ windows: join(outputBase, 'windows'),
214
+ macos: join(outputBase, 'macos'),
215
+ linux: join(outputBase, 'linux'),
216
+ tray: join(outputBase, 'tray'),
217
+ };
218
+
219
+ for (const dir of Object.values(dirs)) {
220
+ await mkdir(dir, { recursive: true });
221
+ }
222
+
223
+ // Generate platform-specific icons
224
+ await this.generateWindowsIco(inputPath, join(dirs.windows, 'app.ico'));
225
+ await this.generateMacOSIcns(inputPath, join(dirs.macos, 'app.icns'));
226
+ await this.generateLinuxIcons(inputPath, dirs.linux);
227
+ await this.generateTrayIcons(inputPath, dirs.tray);
228
+
229
+ console.log('\n✨ Icon generation complete!\n');
230
+ console.log('Generated icons:');
231
+ console.log(` 📁 ${dirs.windows}`);
232
+ console.log(` 📁 ${dirs.macos}`);
233
+ console.log(` 📁 ${dirs.linux}`);
234
+ console.log(` 📁 ${dirs.tray}`);
235
+ }
236
+ }
237
+
238
+ // CLI
239
+ if (import.meta.url === `file://${process.argv[1]}`) {
240
+ const args = process.argv.slice(2);
241
+ const inputPath = args[0] || join(process.cwd(), 'assets', 'icon.png');
242
+ const outputBase = args[1] || join(process.cwd(), 'assets', 'icons');
243
+
244
+ const generator = new IconGenerator();
245
+ generator.generate(inputPath, outputBase).catch(err => {
246
+ console.error('❌ Error:', err.message);
247
+ process.exit(1);
248
+ });
249
+ }
250
+
251
+ export { IconGenerator };