plusui-native 0.2.59 → 0.2.62

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.59",
3
+ "version": "0.2.62",
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.58",
31
- "plusui-native-connect": "^0.1.58"
30
+ "plusui-native-builder": "^0.1.61",
31
+ "plusui-native-connect": "^0.1.61"
32
32
  },
33
33
  "peerDependencies": {
34
- "plusui-native-connect": "^0.1.58"
34
+ "plusui-native-connect": "^0.1.61"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
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';
@@ -400,8 +400,8 @@ async function updatePlusUIPackages() {
400
400
  function getAppBindgenPaths() {
401
401
  return {
402
402
  featuresDir: join(process.cwd(), 'src', 'features'),
403
- outputDir: join(process.cwd(), 'src', 'Bindings'),
404
- frontendOutputDir: join(process.cwd(), 'frontend', 'src', 'Bindings'),
403
+ // Connections/ is now at the project root — shared by C++ and TS
404
+ outputDir: join(process.cwd(), 'Connections'),
405
405
  };
406
406
  }
407
407
 
@@ -1089,14 +1089,12 @@ async function runBindgen(providedArgs = null, options = {}) {
1089
1089
 
1090
1090
  let usedDefaultAppMode = false;
1091
1091
  let defaultOutputDir = null;
1092
- let defaultFrontendOutputDir = null;
1093
1092
 
1094
1093
  if (bindgenArgs.length === 0) {
1095
- const { outputDir: appOutputDir, frontendOutputDir } = getAppBindgenPaths();
1094
+ const { outputDir: appOutputDir } = getAppBindgenPaths();
1096
1095
  bindgenArgs = [process.cwd(), appOutputDir];
1097
1096
  usedDefaultAppMode = true;
1098
1097
  defaultOutputDir = appOutputDir;
1099
- defaultFrontendOutputDir = frontendOutputDir;
1100
1098
  log(`Project mode: ${process.cwd()} -> ${appOutputDir}`, 'dim');
1101
1099
  }
1102
1100
 
@@ -1110,9 +1108,6 @@ async function runBindgen(providedArgs = null, options = {}) {
1110
1108
  proc.on('close', async (code) => {
1111
1109
  if (code === 0) {
1112
1110
  try {
1113
- if (usedDefaultAppMode && defaultOutputDir && defaultFrontendOutputDir) {
1114
- await syncGeneratedTsBindings(defaultOutputDir, defaultFrontendOutputDir);
1115
- }
1116
1111
  log('\nBindgen complete!', 'green');
1117
1112
  resolve();
1118
1113
  } catch (syncErr) {
@@ -40,23 +40,19 @@ npm run connect
40
40
 
41
41
  Default connect generator paths:
42
42
  - Input: project root scan (frontend + backend files)
43
- - Output: `src/Bindings`
43
+ - Output: `Connections/` (at project root)
44
44
 
45
45
  Generated structure:
46
- - `src/Bindings/bindings.gen.ts`
47
- - `src/Bindings/bindings.gen.hpp`
48
- - `src/Bindings/connect.manifest.json`
46
+ - `Connections/connections.gen.ts` — typed TS channel exports (regenerated)
47
+ - `Connections/connections.gen.hpp` — C++ `Connections` struct (regenerated)
48
+ - `Connections/connect.manifest.json` — detected channel list (regenerated)
49
+
50
+ All three files are always regenerated — do not manually edit them.
49
51
 
50
52
  Scan extensions:
51
53
  - `WEB_IO`: `.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, `.cts`, `.html`
52
54
  - `CPP_IO`: `.h`, `.hpp`, `.hh`, `.hxx`, `.cpp`, `.cc`, `.cxx`
53
55
 
54
- Custom binding kinds detected:
55
- - `method`
56
- - `service`
57
- - `stream`
58
- - `event`
59
-
60
56
  `plusui connect` scans your project structure and does not require a schema file.
61
57
 
62
58
  You can also pass custom paths:
@@ -163,10 +159,12 @@ just help # Show all commands
163
159
 
164
160
  ## Calling C++ from TypeScript
165
161
 
166
- Use the local `frontend/src/plusui.ts` bridge to interact with the native backend:
162
+ All features and custom channels are available via a single import:
167
163
 
168
164
  ```tsx
169
- import { win } from './plusui';
165
+ import plusui from 'plusui';
166
+ // or named imports:
167
+ import { connect, win, clipboard, app } from 'plusui';
170
168
 
171
169
  // Window controls
172
170
  await win.minimize();
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  import { EnvironmentDoctor } from '../src/doctor/index.js';
6
6
  import chalk from 'chalk';
7
7
  import { spawn } from 'child_process';
8
+ import { execSync } from 'child_process';
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
10
11
  const __dirname = dirname(__filename);
@@ -24,6 +25,58 @@ export class TemplateManager {
24
25
  return this.cliPackageJson.version;
25
26
  }
26
27
 
28
+ async loadCliPackageJson() {
29
+ if (!this.cliPackageJson) {
30
+ const packageJsonPath = join(this.templatesDir, '..', 'package.json');
31
+ const content = await readFile(packageJsonPath, 'utf8');
32
+ this.cliPackageJson = JSON.parse(content);
33
+ }
34
+ return this.cliPackageJson;
35
+ }
36
+
37
+ getLatestPublishedVersion(packageName) {
38
+ try {
39
+ const version = execSync(`npm view ${packageName} version`, {
40
+ encoding: 'utf8',
41
+ stdio: ['pipe', 'pipe', 'ignore']
42
+ }).trim();
43
+ return version || null;
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ toCompatibleRange(version) {
50
+ if (!version) {
51
+ return null;
52
+ }
53
+ return version.startsWith('^') || version.startsWith('~') ? version : `^${version}`;
54
+ }
55
+
56
+ async resolveTemplatePackageVersions() {
57
+ const cliPkg = await this.loadCliPackageJson();
58
+ const cliVersion = cliPkg.version;
59
+ const [major, minor] = cliVersion.split('.');
60
+
61
+ const fallbackCliRange = `^${major}.${minor}.0`;
62
+ const fallbackToolsRange = `^${major}.1.0`;
63
+
64
+ const publishedCli = this.getLatestPublishedVersion('plusui-native');
65
+ const publishedCore = this.getLatestPublishedVersion('plusui-native-core');
66
+ const publishedBuilder = this.getLatestPublishedVersion('plusui-native-builder');
67
+ const publishedConnect = this.getLatestPublishedVersion('plusui-native-connect');
68
+
69
+ const cliDependencyBuilder = cliPkg.dependencies?.['plusui-native-builder'] || null;
70
+ const cliDependencyConnect = cliPkg.dependencies?.['plusui-native-connect'] || null;
71
+
72
+ return {
73
+ cli: this.toCompatibleRange(publishedCli) || fallbackCliRange,
74
+ core: this.toCompatibleRange(publishedCore) || fallbackToolsRange,
75
+ builder: this.toCompatibleRange(publishedBuilder) || cliDependencyBuilder || fallbackToolsRange,
76
+ connect: this.toCompatibleRange(publishedConnect) || cliDependencyConnect || fallbackToolsRange
77
+ };
78
+ }
79
+
27
80
  async create(projectName, options = {}) {
28
81
  const template = options.template || 'react';
29
82
  const templatePath = join(this.templatesDir, template);
@@ -53,32 +106,22 @@ export class TemplateManager {
53
106
 
54
107
  await mkdir(projectPath, { recursive: true });
55
108
 
56
- // 3. Get current package versions
57
- const cliVersion = await this.loadCliVersion();
58
-
59
- // Use version ranges that match latest published versions
60
- // For CLI (plusui-native): Use major.minor.0 to get latest compatible
61
- const [major, minor, patch] = cliVersion.split('.');
62
- const cliVersionRange = `^${major}.${minor}.0`;
63
-
64
- // Core version follows 0.1.x pattern when CLI is 0.2.x
65
- const coreVersionRange = `^${major}.1.0`;
66
-
67
- // Builder and connect generator also follow 0.1.x pattern
68
- const builderVersionRange = `^${major}.1.0`;
69
- const connectVersionRange = `^${major}.1.0`;
109
+ // 3. Resolve package versions for generated app dependencies
110
+ const resolvedVersions = await this.resolveTemplatePackageVersions();
70
111
 
71
112
  // 4. Prepare template variables
72
113
  const variables = {
73
114
  PROJECT_NAME: projectName,
74
115
  PROJECT_NAME_LOWER: projectName.toLowerCase(),
75
116
  PROJECT_VERSION: '0.1.0',
76
- PLUSUI_CLI_VERSION: cliVersionRange,
77
- PLUSUI_CORE_VERSION: coreVersionRange,
78
- PLUSUI_BUILDER_VERSION: builderVersionRange,
79
- PLUSUI_CONNECT_VERSION: connectVersionRange
117
+ PLUSUI_CLI_VERSION: resolvedVersions.cli,
118
+ PLUSUI_CORE_VERSION: resolvedVersions.core,
119
+ PLUSUI_BUILDER_VERSION: resolvedVersions.builder,
120
+ PLUSUI_CONNECT_VERSION: resolvedVersions.connect
80
121
  };
81
122
 
123
+ console.log(chalk.dim(`Using versions: core=${variables.PLUSUI_CORE_VERSION}, builder=${variables.PLUSUI_BUILDER_VERSION}, connect=${variables.PLUSUI_CONNECT_VERSION}`));
124
+
82
125
  // 5. Copy template files
83
126
  await this.copyTemplate(templatePath, projectPath, variables);
84
127
 
@@ -1,5 +1,7 @@
1
1
  import { useState, useEffect } from 'react';
2
- import { win, browser, router, app, fileDrop, formatFileSize, type FileInfo } from './plusui';
2
+ import plusui, { type FileInfo } from 'plusui';
3
+ // Custom channels (generated by `plusui connect`) — import what you use:
4
+ // import { greeting, download } from '../Connections/connections.gen';
3
5
 
4
6
  // Define routes for your app (optional - for SPA routing)
5
7
  const routes = {
@@ -22,39 +24,39 @@ function App() {
22
24
  const [dropAccent] = useState('#60a5fa');
23
25
 
24
26
  useEffect(() => {
25
- win.show().catch(() => {
27
+ plusui.win.show().catch(() => {
26
28
  // no-op for browser preview mode
27
29
  });
28
30
 
29
31
  // Setup routes
30
- router.setRoutes(routes);
32
+ plusui.router.setRoutes(routes);
31
33
 
32
34
  // Listen for navigation changes
33
- const unsub = browser.onNavigate((url) => {
35
+ const unsub = plusui.browser.onNavigate((url) => {
34
36
  setCurrentUrl(url);
35
- browser.canGoBack().then(setCanGoBack);
36
- browser.canGoForward().then(setCanGoForward);
37
+ plusui.browser.canGoBack().then(setCanGoBack);
38
+ plusui.browser.canGoForward().then(setCanGoForward);
37
39
  });
38
40
 
39
41
  // Setup FileDrop listeners
40
- const unsubDrop = fileDrop.onFilesDropped((droppedFiles) => {
42
+ const unsubDrop = plusui.fileDrop.onFilesDropped((droppedFiles) => {
41
43
  console.log('Files dropped:', droppedFiles);
42
44
  setFiles(prev => [...prev, ...droppedFiles]);
43
45
  setIsDragging(false);
44
46
  });
45
47
 
46
- const unsubEnter = fileDrop.onDragEnter(() => {
48
+ const unsubEnter = plusui.fileDrop.onDragEnter(() => {
47
49
  setIsDragging(true);
48
50
  });
49
51
 
50
- const unsubLeave = fileDrop.onDragLeave(() => {
52
+ const unsubLeave = plusui.fileDrop.onDragLeave(() => {
51
53
  setIsDragging(false);
52
54
  });
53
55
 
54
56
  // Get initial state
55
- browser.getUrl().then(setCurrentUrl);
56
- browser.canGoBack().then(setCanGoBack);
57
- browser.canGoForward().then(setCanGoForward);
57
+ plusui.browser.getUrl().then(setCurrentUrl);
58
+ plusui.browser.canGoBack().then(setCanGoBack);
59
+ plusui.browser.canGoForward().then(setCanGoForward);
58
60
 
59
61
  return () => {
60
62
  unsub();
@@ -64,29 +66,29 @@ function App() {
64
66
  };
65
67
  }, []);
66
68
 
67
- const handleMinimize = async () => await win.minimize();
68
- const handleMaximize = async () => await win.maximize();
69
- const handleClose = async () => await win.close();
69
+ const handleMinimize = async () => await plusui.win.minimize();
70
+ const handleMaximize = async () => await plusui.win.maximize();
71
+ const handleClose = async () => await plusui.win.close();
70
72
  const handleGetSize = async () => {
71
- const size = await win.getSize();
73
+ const size = await plusui.win.getSize();
72
74
  setWindowSize(size);
73
75
  };
74
76
  const handleGetPosition = async () => {
75
- const pos = await win.getPosition();
77
+ const pos = await plusui.win.getPosition();
76
78
  setWindowPos(pos);
77
79
  };
78
80
 
79
81
  // Browser navigation
80
- const handleGoBack = async () => await browser.goBack();
81
- const handleGoForward = async () => await browser.goForward();
82
- const handleReload = async () => await browser.reload();
82
+ const handleGoBack = async () => await plusui.browser.goBack();
83
+ const handleGoForward = async () => await plusui.browser.goForward();
84
+ const handleReload = async () => await plusui.browser.reload();
83
85
 
84
86
  // Router navigation
85
- const handleGoHome = async () => await router.push('/');
86
- const handleGoSettings = async () => await router.push('/settings');
87
+ const handleGoHome = async () => await plusui.router.push('/');
88
+ const handleGoSettings = async () => await plusui.router.push('/settings');
87
89
 
88
90
  // App control
89
- const handleQuit = async () => await app.quit();
91
+ const handleQuit = async () => await plusui.app.quit();
90
92
 
91
93
  return (
92
94
  <div className="app">
@@ -117,8 +119,8 @@ function App() {
117
119
  <h2>Window Position</h2>
118
120
  <div className="button-group">
119
121
  <button onClick={handleGetPosition} className="button">Get Position</button>
120
- <button onClick={() => win.setPosition(100, 100)} className="button">Move Left</button>
121
- <button onClick={() => win.setPosition(800, 100)} className="button">Move Right</button>
122
+ <button onClick={() => plusui.win.setPosition(100, 100)} className="button">Move Left</button>
123
+ <button onClick={() => plusui.win.setPosition(800, 100)} className="button">Move Right</button>
122
124
  </div>
123
125
  {windowPos.x !== 0 && (
124
126
  <p className="info-text">Position: {windowPos.x}, {windowPos.y}</p>
@@ -142,7 +144,7 @@ function App() {
142
144
  <button onClick={handleGoSettings} className="button">Settings</button>
143
145
  </div>
144
146
  <p style={{ fontSize: '0.85em', color: '#666', marginTop: '10px' }}>
145
- Define routes with <code>router.setRoutes({'{ ... }'})</code> then navigate with <code>router.push('/path')</code>
147
+ Define routes with <code>plusui.router.setRoutes({'{ ... }'})</code> then navigate with <code>plusui.router.push('/path')</code>
146
148
  </p>
147
149
  </div>
148
150
 
@@ -200,7 +202,7 @@ function App() {
200
202
  <div className="filedrop-file-info">
201
203
  <div className="filedrop-file-name">{file.name}</div>
202
204
  <div className="filedrop-file-meta">
203
- {formatFileSize(file.size)} • {file.type}
205
+ {plusui.formatFileSize(file.size)} • {file.type}
204
206
  </div>
205
207
  </div>
206
208
  <button