plusui-native 0.2.106 → 0.2.107
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/assets/icon-generator.js +251 -251
- package/src/assets/resource-embedder.js +351 -351
- package/src/index.js +1358 -1354
- package/templates/base/assets/README.md +88 -88
- package/templates/manager.js +288 -275
- package/templates/react/frontend/vite.config.ts +1 -1
- package/templates/react/main.cpp.template +5 -7
- package/templates/solid/frontend/vite.config.ts +1 -1
- package/templates/solid/main.cpp.template +5 -7
package/templates/manager.js
CHANGED
|
@@ -1,275 +1,288 @@
|
|
|
1
|
-
import { mkdir, readdir, readFile, writeFile, copyFile, stat } from 'fs/promises';
|
|
2
|
-
import { join, dirname, resolve } from 'path';
|
|
3
|
-
import { existsSync } from 'fs';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
import { EnvironmentDoctor } from '../src/doctor/index.js';
|
|
6
|
-
import chalk from 'chalk';
|
|
7
|
-
import { spawn } from 'child_process';
|
|
8
|
-
import { execSync } from 'child_process';
|
|
9
|
-
|
|
10
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
-
const __dirname = dirname(__filename);
|
|
12
|
-
|
|
13
|
-
export class TemplateManager {
|
|
14
|
-
constructor() {
|
|
15
|
-
this.templatesDir = __dirname;
|
|
16
|
-
this.cliPackageJson = null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async loadCliVersion() {
|
|
20
|
-
if (!this.cliPackageJson) {
|
|
21
|
-
const packageJsonPath = join(this.templatesDir, '..', 'package.json');
|
|
22
|
-
const content = await readFile(packageJsonPath, 'utf8');
|
|
23
|
-
this.cliPackageJson = JSON.parse(content);
|
|
24
|
-
}
|
|
25
|
-
return this.cliPackageJson.version;
|
|
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
|
-
|
|
80
|
-
async create(projectName, options = {}) {
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
console.log(chalk.
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
console.log(chalk.blue('
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
await this.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
1
|
+
import { mkdir, readdir, readFile, writeFile, copyFile, stat } from 'fs/promises';
|
|
2
|
+
import { join, dirname, resolve } from 'path';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import { EnvironmentDoctor } from '../src/doctor/index.js';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
|
|
13
|
+
export class TemplateManager {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.templatesDir = __dirname;
|
|
16
|
+
this.cliPackageJson = null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async loadCliVersion() {
|
|
20
|
+
if (!this.cliPackageJson) {
|
|
21
|
+
const packageJsonPath = join(this.templatesDir, '..', 'package.json');
|
|
22
|
+
const content = await readFile(packageJsonPath, 'utf8');
|
|
23
|
+
this.cliPackageJson = JSON.parse(content);
|
|
24
|
+
}
|
|
25
|
+
return this.cliPackageJson.version;
|
|
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
|
+
|
|
80
|
+
async create(projectName, options = {}) {
|
|
81
|
+
// Validate and sanitize project name
|
|
82
|
+
const invalidChars = projectName.replace(/[a-zA-Z0-9_-]/g, '');
|
|
83
|
+
if (invalidChars.length > 0) {
|
|
84
|
+
console.log(chalk.yellow(`\nWarning: Project name contains invalid characters: ${invalidChars}`));
|
|
85
|
+
console.log(chalk.yellow('Only lowercase letters, numbers, hyphens, and underscores are allowed.'));
|
|
86
|
+
projectName = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, '-');
|
|
87
|
+
console.log(chalk.green(`Using sanitized name: ${projectName}\n`));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (/^[0-9]/.test(projectName)) {
|
|
91
|
+
throw new Error('Project name cannot start with a number');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const template = options.template || 'react';
|
|
95
|
+
const templatePath = join(this.templatesDir, template);
|
|
96
|
+
|
|
97
|
+
// Determine target path and actual project name
|
|
98
|
+
let targetDirName = projectName;
|
|
99
|
+
let projectPath = resolve(projectName);
|
|
100
|
+
|
|
101
|
+
if (projectName === '.') {
|
|
102
|
+
targetDirName = require('path').basename(process.cwd());
|
|
103
|
+
projectPath = process.cwd();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
console.log(chalk.bold(`\nCreating PlusUI project: ${targetDirName}\n`));
|
|
107
|
+
|
|
108
|
+
// 1. Check environment first
|
|
109
|
+
console.log(chalk.blue('Checking environment...'));
|
|
110
|
+
const doctor = new EnvironmentDoctor();
|
|
111
|
+
const quickCheck = await doctor.quickCheck();
|
|
112
|
+
|
|
113
|
+
if (!quickCheck.ready) {
|
|
114
|
+
console.log(chalk.red('\n✗ Environment not ready!\n'));
|
|
115
|
+
console.log(chalk.yellow('Run: plusui doctor --fix\n'));
|
|
116
|
+
throw new Error('Environment check failed');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
console.log(chalk.green('✓ Environment ready\n'));
|
|
120
|
+
|
|
121
|
+
// 2. Create project directory
|
|
122
|
+
console.log(chalk.blue('Creating project structure...'));
|
|
123
|
+
|
|
124
|
+
if (projectName !== '.') {
|
|
125
|
+
if (existsSync(projectPath)) {
|
|
126
|
+
throw new Error(`Directory ${projectName} already exists`);
|
|
127
|
+
}
|
|
128
|
+
await mkdir(projectPath, { recursive: true });
|
|
129
|
+
} else {
|
|
130
|
+
// Warn but proceed if using current directory
|
|
131
|
+
console.log(chalk.yellow(`Initializing in current directory...`));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 3. Resolve package versions for generated app dependencies
|
|
135
|
+
const resolvedVersions = await this.resolveTemplatePackageVersions();
|
|
136
|
+
|
|
137
|
+
// 4. Prepare template variables
|
|
138
|
+
const variables = {
|
|
139
|
+
PROJECT_NAME: targetDirName,
|
|
140
|
+
PROJECT_NAME_LOWER: targetDirName.toLowerCase(),
|
|
141
|
+
PROJECT_VERSION: '0.1.0',
|
|
142
|
+
PLUSUI_CLI_VERSION: resolvedVersions.cli,
|
|
143
|
+
PLUSUI_CORE_VERSION: resolvedVersions.core,
|
|
144
|
+
PLUSUI_BUILDER_VERSION: resolvedVersions.builder,
|
|
145
|
+
PLUSUI_CONNECT_VERSION: resolvedVersions.connect
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
console.log(chalk.dim(`Using versions: core=${variables.PLUSUI_CORE_VERSION}, builder=${variables.PLUSUI_BUILDER_VERSION}, connect=${variables.PLUSUI_CONNECT_VERSION}`));
|
|
149
|
+
|
|
150
|
+
// 5. Copy template files
|
|
151
|
+
await this.copyTemplate(templatePath, projectPath, variables);
|
|
152
|
+
|
|
153
|
+
// 5. Copy base files
|
|
154
|
+
const basePath = join(this.templatesDir, 'base');
|
|
155
|
+
await this.copyTemplate(basePath, projectPath, variables, true);
|
|
156
|
+
|
|
157
|
+
console.log(chalk.green('✓ Project structure created\n'));
|
|
158
|
+
|
|
159
|
+
// 6. Install npm dependencies
|
|
160
|
+
console.log(chalk.blue('Installing dependencies...'));
|
|
161
|
+
await this.runNpmInstall(projectPath);
|
|
162
|
+
console.log(chalk.green('✓ Dependencies installed\n'));
|
|
163
|
+
|
|
164
|
+
// 7. Start dev server in the created project immediately
|
|
165
|
+
process.chdir(projectPath);
|
|
166
|
+
console.log(chalk.green(`✓ Changed directory to: ${projectPath}`));
|
|
167
|
+
console.log(chalk.blue('Starting development server...'));
|
|
168
|
+
await this.runPlusuiDev(projectPath);
|
|
169
|
+
|
|
170
|
+
return { success: true, path: projectPath };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
async copyTemplate(templatePath, destPath, variables, skipSubdirs = false) {
|
|
174
|
+
if (!existsSync(templatePath)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const entries = await readdir(templatePath, { withFileTypes: true });
|
|
179
|
+
|
|
180
|
+
for (const entry of entries) {
|
|
181
|
+
const srcPath = join(templatePath, entry.name);
|
|
182
|
+
const destName = entry.name.replace('.template', '');
|
|
183
|
+
const destFilePath = join(destPath, destName);
|
|
184
|
+
|
|
185
|
+
if (entry.isDirectory()) {
|
|
186
|
+
if (!skipSubdirs) {
|
|
187
|
+
await mkdir(destFilePath, { recursive: true });
|
|
188
|
+
await this.copyTemplate(srcPath, destFilePath, variables, false);
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
// Ensure parent directory exists
|
|
192
|
+
await mkdir(dirname(destFilePath), { recursive: true });
|
|
193
|
+
|
|
194
|
+
// Read file content
|
|
195
|
+
let content = await readFile(srcPath, 'utf8');
|
|
196
|
+
|
|
197
|
+
// Replace variables
|
|
198
|
+
content = this.replaceVariables(content, variables);
|
|
199
|
+
|
|
200
|
+
// Write to destination
|
|
201
|
+
await writeFile(destFilePath, content, 'utf8');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
replaceVariables(content, variables) {
|
|
207
|
+
let result = content;
|
|
208
|
+
|
|
209
|
+
for (const [key, value] of Object.entries(variables)) {
|
|
210
|
+
// Replace without spaces (e.g. {{PROJECT_NAME}})
|
|
211
|
+
result = result.split(`{{${key}}}`).join(value);
|
|
212
|
+
// Replace with spaces (e.g. {{ PROJECT_NAME }})
|
|
213
|
+
result = result.split(`{{ ${key} }}`).join(value);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async runNpmInstall(projectPath) {
|
|
220
|
+
return new Promise((resolve, reject) => {
|
|
221
|
+
const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
222
|
+
|
|
223
|
+
// Install root dependencies
|
|
224
|
+
const rootProc = spawn(npm, ['install'], {
|
|
225
|
+
cwd: projectPath,
|
|
226
|
+
stdio: 'inherit',
|
|
227
|
+
shell: true
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
rootProc.on('close', (code) => {
|
|
231
|
+
if (code !== 0) {
|
|
232
|
+
reject(new Error(`npm install failed with code ${code}`));
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Install frontend dependencies
|
|
237
|
+
const frontendPath = join(projectPath, 'frontend');
|
|
238
|
+
if (existsSync(join(frontendPath, 'package.json'))) {
|
|
239
|
+
const frontendProc = spawn(npm, ['install'], {
|
|
240
|
+
cwd: frontendPath,
|
|
241
|
+
stdio: 'inherit',
|
|
242
|
+
shell: true
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
frontendProc.on('close', (feCode) => {
|
|
246
|
+
if (feCode === 0) {
|
|
247
|
+
resolve();
|
|
248
|
+
} else {
|
|
249
|
+
reject(new Error(`frontend npm install failed with code ${feCode}`));
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
frontendProc.on('error', reject);
|
|
254
|
+
} else {
|
|
255
|
+
resolve();
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
rootProc.on('error', reject);
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async runPlusuiDev(projectPath) {
|
|
264
|
+
return new Promise((resolve, reject) => {
|
|
265
|
+
const plusuiCmd = process.platform === 'win32' ? 'plusui.cmd' : 'plusui';
|
|
266
|
+
|
|
267
|
+
const devProc = spawn(plusuiCmd, ['dev'], {
|
|
268
|
+
cwd: projectPath,
|
|
269
|
+
stdio: 'inherit',
|
|
270
|
+
shell: true,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
devProc.on('close', (code, signal) => {
|
|
274
|
+
// Normal shutdown via Ctrl+C should not be treated as an error.
|
|
275
|
+
if (signal === 'SIGINT' || code === 0 || code === null) {
|
|
276
|
+
resolve();
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
reject(new Error(`plusui dev exited with code ${code}`));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
devProc.on('error', reject);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
|
|
@@ -23,7 +23,6 @@ using json = nlohmann::json;
|
|
|
23
23
|
struct AppConfig {
|
|
24
24
|
std::string name = "{{PROJECT_NAME}}";
|
|
25
25
|
std::string version = "0.1.0";
|
|
26
|
-
bool showWhenFrontendReady = true; // Hides blank webview until React mounts
|
|
27
26
|
} appConfig;
|
|
28
27
|
|
|
29
28
|
struct WindowConfig {
|
|
@@ -38,6 +37,7 @@ struct WindowConfig {
|
|
|
38
37
|
bool devTools = true;
|
|
39
38
|
bool scrollbars = false;
|
|
40
39
|
bool fileDrop = true; // Enable file drop from OS to webview
|
|
40
|
+
bool tray = true; // Enable system tray
|
|
41
41
|
std::string route = "/"; // Starting route
|
|
42
42
|
int devServerPort = 5173;
|
|
43
43
|
} windowConfig;
|
|
@@ -80,30 +80,28 @@ int main() {
|
|
|
80
80
|
.skipTaskbar(!windowConfig.showInTaskbar)
|
|
81
81
|
.scrollbars(windowConfig.scrollbars)
|
|
82
82
|
.fileDrop(windowConfig.fileDrop)
|
|
83
|
+
.tray(windowConfig.tray)
|
|
83
84
|
.centered(true)
|
|
84
85
|
.build();
|
|
85
86
|
|
|
86
87
|
// Icon from embedded assets
|
|
87
88
|
#ifdef ASSET_ICON_PNG
|
|
88
89
|
mainWindow.setIconFromMemory(ASSET_ICON_PNG, ASSET_ICON_PNG_LEN);
|
|
90
|
+
mainWindow.setTrayIconFromMemory(ASSET_ICON_PNG, ASSET_ICON_PNG_LEN);
|
|
89
91
|
#endif
|
|
90
92
|
|
|
91
|
-
// Hide until frontend signals it's ready (no blank flash)
|
|
92
|
-
if (appConfig.showWhenFrontendReady) {
|
|
93
|
-
mainWindow.hide();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
93
|
// Wire up connect system (for custom frontend ↔ backend communication)
|
|
97
94
|
// Run `plusui connect` after adding custom connect methods
|
|
98
95
|
static Connect conn;
|
|
99
96
|
bindConnect(mainWindow, conn);
|
|
100
97
|
|
|
101
|
-
// Load frontend
|
|
98
|
+
// Load frontend then show.
|
|
102
99
|
#ifdef PLUSUI_DEV_MODE
|
|
103
100
|
mainWindow.navigate("http://localhost:" + std::to_string(windowConfig.devServerPort) + windowConfig.route);
|
|
104
101
|
#else
|
|
105
102
|
mainWindow.loadFile("frontend/dist/index.html" + (windowConfig.route == "/" ? "" : "#" + windowConfig.route));
|
|
106
103
|
#endif
|
|
104
|
+
mainWindow.show();
|
|
107
105
|
|
|
108
106
|
App runtime;
|
|
109
107
|
runtime.run();
|