plusui-native 0.2.66 → 0.2.69

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.66",
3
+ "version": "0.2.69",
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.65",
31
- "plusui-native-connect": "^0.1.65"
30
+ "plusui-native-builder": "^0.1.68",
31
+ "plusui-native-connect": "^0.1.68"
32
32
  },
33
33
  "peerDependencies": {
34
- "plusui-native-connect": "^0.1.65"
34
+ "plusui-native-connect": "^0.1.68"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -19,25 +19,21 @@ async function detectMSVC() {
19
19
  const vswherePath = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe';
20
20
 
21
21
  if (!existsSync(vswherePath)) {
22
- // Fallback: check common VS paths manually
23
- const vsPaths = [
24
- 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC',
25
- 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Tools\\MSVC',
26
- 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC',
27
- 'C:\\Program Files\\Microsoft Visual Studio\\2019\\Community\\VC\\Tools\\MSVC',
28
- 'C:\\Program Files\\Microsoft Visual Studio\\2019\\Professional\\VC\\Tools\\MSVC',
29
- 'C:\\Program Files\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Tools\\MSVC'
30
- ];
31
-
32
- for (const vsPath of vsPaths) {
33
- if (existsSync(vsPath)) {
34
- const version = vsPath.includes('2022') ? 'Visual Studio 2022' : 'Visual Studio 2019';
35
- return {
36
- found: true,
37
- name: version,
38
- path: vsPath,
39
- valid: true
40
- };
22
+ // Fallback: check common VS paths for years 2019–2026
23
+ const vsYears = ['2026', '2025', '2024', '2023', '2022', '2019'];
24
+ const vsEditions = ['Community', 'Professional', 'Enterprise', 'BuildTools'];
25
+
26
+ for (const year of vsYears) {
27
+ for (const edition of vsEditions) {
28
+ const vsPath = `C:\\Program Files\\Microsoft Visual Studio\\${year}\\${edition}\\VC\\Tools\\MSVC`;
29
+ if (existsSync(vsPath)) {
30
+ return {
31
+ found: true,
32
+ name: `Visual Studio ${year} ${edition}`,
33
+ path: vsPath,
34
+ valid: true
35
+ };
36
+ }
41
37
  }
42
38
  }
43
39
 
@@ -95,7 +91,7 @@ async function detectXcode() {
95
91
 
96
92
  // Parse clang version
97
93
  const versionMatch = clangResult.output.match(/clang version (\d+\.\d+\.\d+)/i) ||
98
- clangResult.output.match(/Apple clang version (\d+\.\d+\.\d+)/i);
94
+ clangResult.output.match(/Apple clang version (\d+\.\d+\.\d+)/i);
99
95
  const version = versionMatch ? versionMatch[1] : 'unknown';
100
96
 
101
97
  return {
package/src/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { mkdir, readFile, stat, rm, readdir, writeFile, copyFile } from 'fs/promises';
4
4
  import { existsSync, watch, statSync, mkdirSync } from 'fs';
@@ -74,23 +74,38 @@ function checkTools() {
74
74
  }
75
75
  }
76
76
 
77
+ // Compiler / build tools check
77
78
  if (platform === 'win32') {
78
- const vsPaths = [
79
- 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Tools\\MSVC',
80
- 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\VC\\Tools\\MSVC',
81
- 'C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\VC\\Tools\\MSVC'
82
- ];
83
-
79
+ // Use vswhere.exe first (same detection as `plusui doctor`)
84
80
  let vsFound = false;
85
- for (const p of vsPaths) {
86
- if (existsSync(p)) {
87
- vsFound = true;
88
- break;
81
+ const vswherePath = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\Installer\\vswhere.exe';
82
+ if (existsSync(vswherePath)) {
83
+ try {
84
+ const output = execSync(
85
+ `"${vswherePath}" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`,
86
+ { stdio: ['pipe', 'pipe', 'pipe'], encoding: 'utf8', timeout: 10000 }
87
+ ).trim();
88
+ if (output) vsFound = true;
89
+ } catch { }
90
+ }
91
+
92
+ // Fallback: check common VS paths for years 2019–2026
93
+ if (!vsFound) {
94
+ const vsYears = ['2026', '2025', '2024', '2023', '2022', '2019'];
95
+ const vsEditions = ['Community', 'Professional', 'Enterprise', 'BuildTools'];
96
+ for (const year of vsYears) {
97
+ for (const edition of vsEditions) {
98
+ if (existsSync(`C:\\Program Files\\Microsoft Visual Studio\\${year}\\${edition}\\VC\\Tools\\MSVC`)) {
99
+ vsFound = true;
100
+ break;
101
+ }
102
+ }
103
+ if (vsFound) break;
89
104
  }
90
105
  }
91
106
 
92
107
  if (!vsFound) {
93
- required.push({ name: 'Visual Studio 2022', install: 'Download from visualstudio.microsoft.com with C++ workload', auto: null });
108
+ required.push({ name: 'Visual Studio (C++ workload)', install: 'Download from visualstudio.microsoft.com with C++ workload', auto: null });
94
109
  }
95
110
  } else if (platform === 'darwin') {
96
111
  try {
@@ -203,9 +218,9 @@ function runCMake(args, options = {}) {
203
218
 
204
219
  function getInstalledPackageVersion(packageName) {
205
220
  try {
206
- const result = execSync(`npm list ${packageName} --depth=0 --json`, {
207
- encoding: 'utf8',
208
- stdio: ['pipe', 'pipe', 'ignore']
221
+ const result = execSync(`npm list ${packageName} --depth=0 --json`, {
222
+ encoding: 'utf8',
223
+ stdio: ['pipe', 'pipe', 'ignore']
209
224
  });
210
225
  const json = JSON.parse(result);
211
226
  if (json.dependencies && json.dependencies[packageName]) {
@@ -214,12 +229,12 @@ function getInstalledPackageVersion(packageName) {
214
229
  } catch {
215
230
  // Package not installed locally
216
231
  }
217
-
232
+
218
233
  // Try global installation
219
234
  try {
220
- const result = execSync(`npm list -g ${packageName} --depth=0 --json`, {
221
- encoding: 'utf8',
222
- stdio: ['pipe', 'pipe', 'ignore']
235
+ const result = execSync(`npm list -g ${packageName} --depth=0 --json`, {
236
+ encoding: 'utf8',
237
+ stdio: ['pipe', 'pipe', 'ignore']
223
238
  });
224
239
  const json = JSON.parse(result);
225
240
  if (json.dependencies && json.dependencies[packageName]) {
@@ -228,15 +243,15 @@ function getInstalledPackageVersion(packageName) {
228
243
  } catch {
229
244
  return null;
230
245
  }
231
-
246
+
232
247
  return null;
233
248
  }
234
249
 
235
250
  function getLatestPackageVersion(packageName) {
236
251
  try {
237
- const result = execSync(`npm view ${packageName} version`, {
238
- encoding: 'utf8',
239
- stdio: ['pipe', 'pipe', 'ignore']
252
+ const result = execSync(`npm view ${packageName} version`, {
253
+ encoding: 'utf8',
254
+ stdio: ['pipe', 'pipe', 'ignore']
240
255
  });
241
256
  return result.trim();
242
257
  } catch {
@@ -247,7 +262,7 @@ function getLatestPackageVersion(packageName) {
247
262
  function compareVersions(v1, v2) {
248
263
  const parts1 = v1.split('.').map(Number);
249
264
  const parts2 = v2.split('.').map(Number);
250
-
265
+
251
266
  for (let i = 0; i < 3; i++) {
252
267
  if (parts1[i] > parts2[i]) return 1;
253
268
  if (parts1[i] < parts2[i]) return -1;
@@ -262,9 +277,9 @@ function showVersionInfo() {
262
277
  'plusui-native-builder',
263
278
  'plusui-native-connect'
264
279
  ];
265
-
280
+
266
281
  logSection('PlusUI Package Versions');
267
-
282
+
268
283
  packages.forEach(pkg => {
269
284
  let version;
270
285
  if (pkg === cliPackageJson.name) {
@@ -279,33 +294,33 @@ function showVersionInfo() {
279
294
  }
280
295
  }
281
296
  });
282
-
297
+
283
298
  console.log('');
284
299
  }
285
300
 
286
301
  async function updatePlusUIPackages() {
287
302
  logSection('Updating PlusUI Packages');
288
-
303
+
289
304
  const packages = [
290
305
  cliPackageJson.name,
291
306
  'plusui-native-core',
292
307
  'plusui-native-builder',
293
308
  'plusui-native-connect'
294
309
  ];
295
-
310
+
296
311
  log('Checking for updates...\n', 'blue');
297
-
312
+
298
313
  // Check if packages are installed locally or globally
299
314
  const isInProject = existsSync(join(process.cwd(), 'package.json'));
300
-
315
+
301
316
  if (isInProject) {
302
317
  let updatedCount = 0;
303
318
  let upToDateCount = 0;
304
319
  let installedCount = 0;
305
-
320
+
306
321
  for (const pkg of packages) {
307
322
  const currentVersion = getInstalledPackageVersion(pkg);
308
-
323
+
309
324
  if (!currentVersion) {
310
325
  const latestVersion = getLatestPackageVersion(pkg);
311
326
 
@@ -327,22 +342,22 @@ async function updatePlusUIPackages() {
327
342
  }
328
343
  continue;
329
344
  }
330
-
345
+
331
346
  // Get latest version from npm
332
347
  const latestVersion = getLatestPackageVersion(pkg);
333
-
348
+
334
349
  if (!latestVersion) {
335
350
  log(`${COLORS.yellow}${pkg}: couldn't check for updates${COLORS.reset}`);
336
351
  continue;
337
352
  }
338
-
353
+
339
354
  const comparison = compareVersions(latestVersion, currentVersion);
340
-
355
+
341
356
  if (comparison > 0) {
342
357
  // Newer version available
343
358
  try {
344
359
  log(`${COLORS.blue}${pkg}: ${currentVersion} → ${latestVersion}${COLORS.reset}`);
345
- execSync(`npm install ${pkg}@${latestVersion}`, {
360
+ execSync(`npm install ${pkg}@${latestVersion}`, {
346
361
  stdio: ['ignore', 'ignore', 'pipe'],
347
362
  encoding: 'utf8'
348
363
  });
@@ -357,7 +372,7 @@ async function updatePlusUIPackages() {
357
372
  upToDateCount++;
358
373
  }
359
374
  }
360
-
375
+
361
376
  console.log('');
362
377
  if (updatedCount > 0) {
363
378
  log(`Updated ${updatedCount} package${updatedCount !== 1 ? 's' : ''}`, 'green');
@@ -370,17 +385,17 @@ async function updatePlusUIPackages() {
370
385
  }
371
386
  } else {
372
387
  log('Updating global CLI package...', 'cyan');
373
-
388
+
374
389
  const currentVersion = cliPackageJson.version;
375
390
  const latestVersion = getLatestPackageVersion(cliPackageJson.name);
376
-
391
+
377
392
  if (!latestVersion) {
378
393
  log('Couldn\'t check for updates', 'yellow');
379
394
  return;
380
395
  }
381
-
396
+
382
397
  const comparison = compareVersions(latestVersion, currentVersion);
383
-
398
+
384
399
  if (comparison > 0) {
385
400
  try {
386
401
  log(`${COLORS.blue}${cliPackageJson.name}: ${currentVersion} → ${latestVersion}${COLORS.reset}`);
@@ -393,7 +408,7 @@ async function updatePlusUIPackages() {
393
408
  log(`✓ ${cliPackageJson.name} v${currentVersion} (already up to date)`, 'green');
394
409
  }
395
410
  }
396
-
411
+
397
412
  console.log('');
398
413
  }
399
414
 
@@ -411,7 +426,7 @@ function findLikelyProjectDirs(baseDir) {
411
426
  if (entries) {
412
427
  // noop; just to ensure cwd is a Node project when possible
413
428
  }
414
- } catch {}
429
+ } catch { }
415
430
 
416
431
  const candidates = [];
417
432
  try {
@@ -431,7 +446,7 @@ function findLikelyProjectDirs(baseDir) {
431
446
  candidates.push(dirName);
432
447
  }
433
448
  }
434
- } catch {}
449
+ } catch { }
435
450
 
436
451
  return candidates;
437
452
  }
@@ -645,30 +660,30 @@ async function embedAssets() {
645
660
  if (!existsSync(assetsDir)) {
646
661
  try {
647
662
  await mkdir(assetsDir, { recursive: true });
648
- } catch(e) {}
663
+ } catch (e) { }
649
664
  }
650
665
 
651
666
  logSection('Embedding Assets');
652
-
667
+
653
668
  // Always generate the header file, even if empty
654
669
  let headerContent = '#pragma once\n\n';
655
670
  headerContent += '// THIS FILE IS AUTO-GENERATED BY PLUSUI CLI\n';
656
671
  headerContent += '// DO NOT MODIFY MANUALLY\n\n';
657
-
658
- const files = existsSync(assetsDir)
672
+
673
+ const files = existsSync(assetsDir)
659
674
  ? (await readdir(assetsDir)).filter(f => !statSync(join(assetsDir, f)).isDirectory())
660
675
  : [];
661
-
676
+
662
677
  if (files.length === 0) {
663
678
  log('No assets found in assets/ folder', 'dim');
664
679
  } else {
665
680
  for (const file of files) {
666
681
  const filePath = join(assetsDir, file);
667
682
  log(`Processing ${file}...`, 'dim');
668
-
683
+
669
684
  const varName = file.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase();
670
685
  const data = await readFile(filePath);
671
-
686
+
672
687
  headerContent += `static const unsigned char ASSET_${varName}[] = {`;
673
688
  for (let i = 0; i < data.length; i++) {
674
689
  if (i % 16 === 0) headerContent += '\n ';
@@ -681,7 +696,7 @@ async function embedAssets() {
681
696
 
682
697
  const genDir = join(process.cwd(), 'generated');
683
698
  if (!existsSync(genDir)) await mkdir(genDir, { recursive: true });
684
-
699
+
685
700
  await writeFile(join(genDir, 'assets.h'), headerContent);
686
701
  log(`✓ Assets header generated: generated/assets.h`, 'green');
687
702
  }
@@ -828,7 +843,7 @@ async function killPort(port) {
828
843
  try {
829
844
  const output = execSync(`netstat -ano | findstr :${port}`, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
830
845
  const lines = output.split('\n').filter(line => line.includes(`:${port}`) && line.includes('LISTENING'));
831
-
846
+
832
847
  for (const line of lines) {
833
848
  const parts = line.trim().split(/\s+/);
834
849
  const pid = parts[parts.length - 1];
@@ -1097,7 +1112,7 @@ async function runBindgen(providedArgs = null, options = {}) {
1097
1112
  defaultOutputDir = appOutputDir;
1098
1113
  log(`Project mode: ${process.cwd()} -> ${appOutputDir}`, 'dim');
1099
1114
  }
1100
-
1115
+
1101
1116
  // Spawn node process
1102
1117
  const proc = spawn(process.execPath, [scriptPath, ...bindgenArgs], {
1103
1118
  stdio: 'inherit',
@@ -16,7 +16,7 @@ function App() {
16
16
  const [currentUrl, setCurrentUrl] = useState('');
17
17
  const [canGoBack, setCanGoBack] = useState(false);
18
18
  const [canGoForward, setCanGoForward] = useState(false);
19
-
19
+
20
20
  // customFileDrop connect channel state
21
21
  const [isDragging, setIsDragging] = useState(false);
22
22
  const [droppedFiles, setDroppedFiles] = useState<{ name: string; size: number; type: string }[]>([]);
@@ -29,14 +29,14 @@ function App() {
29
29
 
30
30
  // Setup routes
31
31
  plusui.router.setRoutes(routes);
32
-
32
+
33
33
  // Listen for navigation changes
34
34
  const unsub = plusui.browser.onNavigate((url) => {
35
35
  setCurrentUrl(url);
36
36
  plusui.browser.canGoBack().then(setCanGoBack);
37
37
  plusui.browser.canGoForward().then(setCanGoForward);
38
38
  });
39
-
39
+
40
40
  // Listen for responses emitted from C++ via ch.customFileDrop.emit(...) in main.cpp
41
41
  const unsubChannel = customFileDrop.on((data: any) => {
42
42
  setBackendMsg(data?.message ?? JSON.stringify(data));
@@ -46,7 +46,7 @@ function App() {
46
46
  plusui.browser.getUrl().then(setCurrentUrl);
47
47
  plusui.browser.canGoBack().then(setCanGoBack);
48
48
  plusui.browser.canGoForward().then(setCanGoForward);
49
-
49
+
50
50
  return () => {
51
51
  unsub();
52
52
  unsubChannel();
@@ -96,7 +96,7 @@ function App() {
96
96
  return (
97
97
  <div className="app">
98
98
  <header className="app-header">
99
- <h1>{{PROJECT_NAME}}</h1>
99
+ <h1>{{ PROJECT_NAME }}</h1>
100
100
  <p>Built with PlusUI Framework</p>
101
101
  </header>
102
102
 
@@ -164,7 +164,7 @@ function App() {
164
164
  The frontend receives the reply via <code>customFileDrop.on()</code>. Run{' '}
165
165
  <code>plusui connect</code> to regenerate the channel bindings from both sides.
166
166
  </p>
167
-
167
+
168
168
  <div
169
169
  className={`filedrop-zone ${isDragging ? 'filedrop-active' : ''}`}
170
170
  onDragOver={handleDragOver}
@@ -175,7 +175,7 @@ function App() {
175
175
  <div className="filedrop-content">
176
176
  <svg className="filedrop-icon" fill="none" viewBox="0 0 24 24" stroke="currentColor">
177
177
  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2}
178
- d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
178
+ d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12" />
179
179
  </svg>
180
180
  <div className="filedrop-text">
181
181
  {isDragging ? 'Drop files here' : 'Drag & drop files to send to C++'}