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 +4 -4
- package/src/index.js +4 -9
- package/templates/base/README.md.template +10 -12
- package/templates/manager.js +61 -18
- package/templates/react/frontend/src/App.tsx +29 -27
- package/templates/react/frontend/src/plusui.ts +305 -70
- package/templates/react/frontend/tsconfig.json +5 -1
- package/templates/react/frontend/vite.config.ts +7 -0
- package/templates/react/main.cpp.template +26 -6
- package/templates/solid/frontend/src/App.tsx +28 -26
- package/templates/solid/frontend/src/plusui.ts +306 -68
- package/templates/solid/frontend/tsconfig.json +5 -1
- package/templates/solid/frontend/vite.config.ts +7 -0
- package/templates/solid/main.cpp.template +26 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plusui-native",
|
|
3
|
-
"version": "0.2.
|
|
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.
|
|
31
|
-
"plusui-native-connect": "^0.1.
|
|
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.
|
|
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
|
-
|
|
404
|
-
|
|
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
|
|
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: `
|
|
43
|
+
- Output: `Connections/` (at project root)
|
|
44
44
|
|
|
45
45
|
Generated structure:
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
48
|
-
- `
|
|
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
|
-
|
|
162
|
+
All features and custom channels are available via a single import:
|
|
167
163
|
|
|
168
164
|
```tsx
|
|
169
|
-
import
|
|
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();
|
package/templates/manager.js
CHANGED
|
@@ -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.
|
|
57
|
-
const
|
|
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:
|
|
77
|
-
PLUSUI_CORE_VERSION:
|
|
78
|
-
PLUSUI_BUILDER_VERSION:
|
|
79
|
-
PLUSUI_CONNECT_VERSION:
|
|
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
|
|
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
|