windowpp 0.1.1
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/bin/windowpp.js +86 -0
- package/cmake/embed_assets.py +144 -0
- package/framework/CMakeLists.txt +176 -0
- package/framework/include/windowpp/windowpp.h +704 -0
- package/framework/src/AppData/API/AppData.ts +137 -0
- package/framework/src/AppData/appdata_bridge.h +138 -0
- package/framework/src/AppData/appdata_manager.cpp +126 -0
- package/framework/src/AppData/appdata_manager.h +3 -0
- package/framework/src/FileSystem/API/FileSystem.ts +389 -0
- package/framework/src/FileSystem/Linux/filesearch.cpp +148 -0
- package/framework/src/FileSystem/Linux/readfile.cpp +79 -0
- package/framework/src/FileSystem/Linux/savefile.cpp +333 -0
- package/framework/src/FileSystem/MacOS/filesearch.cpp +149 -0
- package/framework/src/FileSystem/MacOS/readfile.cpp +80 -0
- package/framework/src/FileSystem/MacOS/savefile.cpp +264 -0
- package/framework/src/FileSystem/Windows/filesearch.cpp +195 -0
- package/framework/src/FileSystem/Windows/readfile.cpp +122 -0
- package/framework/src/FileSystem/Windows/savefile.cpp +290 -0
- package/framework/src/FileSystem/file_index_service.cpp +262 -0
- package/framework/src/FileSystem/file_index_service.h +55 -0
- package/framework/src/FileSystem/filesystem_bridge.h +243 -0
- package/framework/src/FileSystem/filesystem_handler.h +93 -0
- package/framework/src/FileSystem/filesystem_json.h +241 -0
- package/framework/src/FileSystem/filesystem_search_service.cpp +414 -0
- package/framework/src/FileSystem/filesystem_search_service.h +94 -0
- package/framework/src/Input/API/Input.ts +161 -0
- package/framework/src/Input/Linux/linux_key_utils.h +135 -0
- package/framework/src/Input/MacOS/macos_key_utils.h +137 -0
- package/framework/src/Input/Windows/win32_key_utils.h +199 -0
- package/framework/src/Input/input_bridge.h +192 -0
- package/framework/src/Input/input_service.cpp +584 -0
- package/framework/src/Input/input_service.h +21 -0
- package/framework/src/application.cpp +29 -0
- package/framework/src/common/hit_test.cpp +40 -0
- package/framework/src/common/image_loader.cpp +24 -0
- package/framework/src/common/paths.cpp +75 -0
- package/framework/src/filedrop/filedrop.cpp +316 -0
- package/framework/src/filedrop/filedrop.css +421 -0
- package/framework/src/filedrop/filedrop.hpp +92 -0
- package/framework/src/filedrop/filedrop.ts +183 -0
- package/framework/src/platform/API/App.ts +156 -0
- package/framework/src/platform/API/Window.ts +249 -0
- package/framework/src/platform/linux/app_linux.cpp +256 -0
- package/framework/src/platform/linux/app_linux.h +64 -0
- package/framework/src/platform/linux/linux_helpers.cpp +26 -0
- package/framework/src/platform/linux/linux_helpers.h +19 -0
- package/framework/src/platform/linux/tray_linux.cpp +21 -0
- package/framework/src/platform/linux/tray_linux.h +26 -0
- package/framework/src/platform/linux/window_linux.cpp +256 -0
- package/framework/src/platform/linux/window_linux.h +70 -0
- package/framework/src/platform/macos/app_macos.h +59 -0
- package/framework/src/platform/macos/app_macos.mm +223 -0
- package/framework/src/platform/macos/macos_helpers.h +21 -0
- package/framework/src/platform/macos/tray_macos.h +22 -0
- package/framework/src/platform/macos/tray_macos.mm +53 -0
- package/framework/src/platform/macos/window_macos.h +74 -0
- package/framework/src/platform/macos/window_macos.mm +318 -0
- package/framework/src/platform/platform_bridge.h +514 -0
- package/framework/src/platform/platform_factory.cpp +33 -0
- package/framework/src/platform/platform_factory.h +19 -0
- package/framework/src/platform/win32/app_win32.cpp +572 -0
- package/framework/src/platform/win32/app_win32.h +83 -0
- package/framework/src/platform/win32/tray_win32.cpp +57 -0
- package/framework/src/platform/win32/tray_win32.h +30 -0
- package/framework/src/platform/win32/win32_helpers.h +61 -0
- package/framework/src/platform/win32/window_win32.cpp +267 -0
- package/framework/src/platform/win32/window_win32.h +79 -0
- package/framework/src/renderer/webgpu.h +128 -0
- package/framework/src/renderer/webview/include/WebView2.h +48014 -0
- package/framework/src/renderer/webview/include/WebView2EnvironmentOptions.h +342 -0
- package/framework/src/renderer/webview/webview.h +13 -0
- package/framework/src/renderer/webview/webview_linux.cpp +392 -0
- package/framework/src/renderer/webview/webview_macos.mm +388 -0
- package/framework/src/renderer/webview/webview_win32.cpp +688 -0
- package/framework/src/renderer/webview/x64/WebView2Loader.dll +0 -0
- package/framework/src/renderer/webview/x64/WebView2Loader.lib +0 -0
- package/framework/src/renderer/webview/x64/WebView2LoaderStatic.lib +0 -0
- package/lib/build.js +112 -0
- package/lib/create.js +283 -0
- package/lib/dev.js +155 -0
- package/package.json +24 -0
- package/scripts/publish.js +67 -0
- package/scripts/sync-framework.js +73 -0
- package/templates/solid/CMakeLists.txt +56 -0
- package/templates/solid/frontend/package.json +22 -0
- package/templates/solid/frontend/vite.config.ts +25 -0
- package/templates/solid/main.cpp +72 -0
- package/templates/solid/package.json +12 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/build.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cli/lib/build.js — WindowPP production build
|
|
3
|
+
//
|
|
4
|
+
// Called for any WindowPP app directory. Detects the app's cmake target
|
|
5
|
+
// name from the directory's package.json ("cmakeTarget" field, or falls back
|
|
6
|
+
// to the npm package name with hyphens swapped for underscores).
|
|
7
|
+
//
|
|
8
|
+
// Usage (via CLI or directly):
|
|
9
|
+
// windowpp build [--clean] [--app-dir <path>]
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
|
|
17
|
+
function build(options = {}) {
|
|
18
|
+
const {
|
|
19
|
+
appDir = process.cwd(),
|
|
20
|
+
clean = false,
|
|
21
|
+
config = 'Release',
|
|
22
|
+
} = options;
|
|
23
|
+
|
|
24
|
+
const rootDir = findRootDir(appDir);
|
|
25
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
26
|
+
const IS_MAC = process.platform === 'darwin';
|
|
27
|
+
const platformDir = IS_WINDOWS ? 'windows' : IS_MAC ? 'macos' : 'linux';
|
|
28
|
+
const buildDir = path.join(rootDir, 'build', platformDir);
|
|
29
|
+
const frontendDir = path.join(appDir, 'frontend');
|
|
30
|
+
const target = resolveTarget(appDir);
|
|
31
|
+
|
|
32
|
+
console.log(`\n=== WindowPP Build — ${target} (${platformDir}) ===\n`);
|
|
33
|
+
|
|
34
|
+
if (clean && fs.existsSync(buildDir)) {
|
|
35
|
+
console.log(`Cleaning ${buildDir} ...`);
|
|
36
|
+
fs.rmSync(buildDir, { recursive: true, force: true });
|
|
37
|
+
}
|
|
38
|
+
fs.mkdirSync(buildDir, { recursive: true });
|
|
39
|
+
|
|
40
|
+
// Frontend
|
|
41
|
+
if (fs.existsSync(frontendDir)) {
|
|
42
|
+
console.log('Building frontend with Vite...');
|
|
43
|
+
execSync('npm install', { cwd: frontendDir, stdio: 'inherit' });
|
|
44
|
+
execSync('npm run build', { cwd: frontendDir, stdio: 'inherit' });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// CMake configure (force re-configure when cache exists but generated dir is missing)
|
|
48
|
+
forceReconfigureIfNeeded(rootDir, buildDir);
|
|
49
|
+
|
|
50
|
+
const embedScript = path.join(__dirname, '../cmake/embed_assets.py').replace(/\\/g, '/');
|
|
51
|
+
const frameworkDir = path.join(__dirname, '../framework').replace(/\\/g, '/');
|
|
52
|
+
console.log('Configuring CMake...');
|
|
53
|
+
execSync(
|
|
54
|
+
`cmake -S "${rootDir}" -B "${buildDir}"` +
|
|
55
|
+
` -DWPP_EMBED_SCRIPT="${embedScript}"` +
|
|
56
|
+
` -DWPP_FRAMEWORK_DIR="${frameworkDir}"`,
|
|
57
|
+
{ cwd: rootDir, stdio: 'inherit' }
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// CMake build
|
|
61
|
+
console.log(`\nBuilding ${target} (${config})...`);
|
|
62
|
+
const buildCmd = IS_WINDOWS
|
|
63
|
+
? `cmake --build "${buildDir}" --target ${target} --config ${config}`
|
|
64
|
+
: `cmake --build "${buildDir}" --target ${target} -- -j$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || echo 4)`;
|
|
65
|
+
execSync(buildCmd, { cwd: rootDir, stdio: 'inherit' });
|
|
66
|
+
|
|
67
|
+
const exePath = exePathFor(buildDir, target, config, IS_WINDOWS);
|
|
68
|
+
console.log(`\n✓ Build complete: ${exePath}\n`);
|
|
69
|
+
return { exePath, buildDir, rootDir, target };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
function findRootDir(appDir) {
|
|
75
|
+
// Walk up from appDir to find the directory containing CMakeLists.txt.
|
|
76
|
+
// This lets the CLI work whether invoked from the app dir or the repo root.
|
|
77
|
+
let dir = appDir;
|
|
78
|
+
for (let i = 0; i < 5; i++) {
|
|
79
|
+
if (fs.existsSync(path.join(dir, 'CMakeLists.txt'))) return dir;
|
|
80
|
+
dir = path.dirname(dir);
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Cannot find CMakeLists.txt above ${appDir}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveTarget(appDir) {
|
|
86
|
+
const pkgPath = path.join(appDir, 'package.json');
|
|
87
|
+
if (fs.existsSync(pkgPath)) {
|
|
88
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
89
|
+
if (pkg.cmakeTarget) return pkg.cmakeTarget;
|
|
90
|
+
if (pkg.name) return pkg.name.replace(/-/g, '_');
|
|
91
|
+
}
|
|
92
|
+
// Fallback: use the directory name
|
|
93
|
+
return path.basename(appDir).replace(/-/g, '_');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function forceReconfigureIfNeeded(rootDir, buildDir) {
|
|
97
|
+
const cacheFile = path.join(buildDir, 'CMakeCache.txt');
|
|
98
|
+
// Check for any generated* directory
|
|
99
|
+
const generatedExists = fs.readdirSync(buildDir).some(n => n.startsWith('generated'));
|
|
100
|
+
if (fs.existsSync(cacheFile) && !generatedExists) {
|
|
101
|
+
console.log('Stale cache detected — forcing re-configure...');
|
|
102
|
+
fs.rmSync(cacheFile, { force: true });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function exePathFor(buildDir, target, config, isWindows) {
|
|
107
|
+
return isWindows
|
|
108
|
+
? path.join(buildDir, config, `${target}.exe`)
|
|
109
|
+
: path.join(buildDir, target);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = { build, findRootDir, resolveTarget, exePathFor };
|
package/lib/create.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cli/lib/create.js — WindowPP app scaffolding
|
|
3
|
+
//
|
|
4
|
+
// Copies a template from cli/templates/<template>/ to <outDir>/<name>/,
|
|
5
|
+
// substituting {{APP_NAME}}, {{APP_TITLE}}, and {{CMAKE_TARGET}} in all
|
|
6
|
+
// text files. Each created app is a self-contained project; it does NOT
|
|
7
|
+
// need to live inside the framework repo.
|
|
8
|
+
//
|
|
9
|
+
// Usage (via CLI):
|
|
10
|
+
// windowpp create my-app [--template solid] [--out-dir .]
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
function create(name, options = {}) {
|
|
19
|
+
const {
|
|
20
|
+
template = 'solid',
|
|
21
|
+
outDir = process.cwd(),
|
|
22
|
+
installDeps = true,
|
|
23
|
+
} = options;
|
|
24
|
+
|
|
25
|
+
if (!name || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
|
|
26
|
+
console.error('Error: app name must start with a letter and contain only letters, digits, hyphens or underscores.');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const appDir = path.join(outDir, name);
|
|
31
|
+
if (fs.existsSync(appDir)) {
|
|
32
|
+
console.error(`Error: directory "${appDir}" already exists.`);
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const cliDir = path.resolve(__dirname, '..');
|
|
37
|
+
const templateDir = path.join(cliDir, 'templates', template);
|
|
38
|
+
if (!fs.existsSync(templateDir)) {
|
|
39
|
+
console.error(`Error: template "${template}" not found in ${path.join(cliDir, 'templates')}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const cmakeTarget = name.replace(/-/g, '_');
|
|
44
|
+
const appTitle = name
|
|
45
|
+
.split(/[-_]/)
|
|
46
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
47
|
+
.join(' ');
|
|
48
|
+
|
|
49
|
+
const tokens = {
|
|
50
|
+
'{{APP_NAME}}': name,
|
|
51
|
+
'{{APP_TITLE}}': appTitle,
|
|
52
|
+
'{{CMAKE_TARGET}}': cmakeTarget,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
console.log(`\n=== WindowPP Create — ${name} (template: ${template}) ===\n`);
|
|
56
|
+
console.log(`Scaffolding into: ${appDir}\n`);
|
|
57
|
+
|
|
58
|
+
// ── Copy template ─────────────────────────────────────────────────────────
|
|
59
|
+
copyDir(templateDir, appDir, tokens);
|
|
60
|
+
|
|
61
|
+
// ── Install frontend deps ─────────────────────────────────────────────────
|
|
62
|
+
const frontendDir = path.join(appDir, 'frontend');
|
|
63
|
+
if (installDeps && fs.existsSync(frontendDir)) {
|
|
64
|
+
console.log('Installing frontend dependencies...');
|
|
65
|
+
execSync('npm install', { cwd: frontendDir, stdio: 'inherit' });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log(`\n✓ Created "${name}"\n`);
|
|
69
|
+
console.log('Next steps:');
|
|
70
|
+
console.log(` cd ${name}`);
|
|
71
|
+
console.log(' windowpp dev\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
function copyDir(src, dest, tokens) {
|
|
77
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
78
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
79
|
+
const srcPath = path.join(src, entry.name);
|
|
80
|
+
const destPath = path.join(dest, entry.name);
|
|
81
|
+
if (entry.isDirectory()) {
|
|
82
|
+
copyDir(srcPath, destPath, tokens);
|
|
83
|
+
} else {
|
|
84
|
+
copyFile(srcPath, destPath, tokens);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const TEXT_EXTENSIONS = new Set([
|
|
90
|
+
'.cpp', '.h', '.ts', '.tsx', '.js', '.json', '.html',
|
|
91
|
+
'.css', '.md', '.txt', '.cmake', '.sh', '.env',
|
|
92
|
+
]);
|
|
93
|
+
|
|
94
|
+
function copyFile(src, dest, tokens) {
|
|
95
|
+
const ext = path.extname(src).toLowerCase();
|
|
96
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
97
|
+
let content = fs.readFileSync(src, 'utf8');
|
|
98
|
+
for (const [token, value] of Object.entries(tokens)) {
|
|
99
|
+
content = content.split(token).join(value);
|
|
100
|
+
}
|
|
101
|
+
fs.writeFileSync(dest, content, 'utf8');
|
|
102
|
+
} else {
|
|
103
|
+
fs.copyFileSync(src, dest);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { create };
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
function create(name, options = {}) {
|
|
111
|
+
const {
|
|
112
|
+
template = 'solid',
|
|
113
|
+
outDir = process.cwd(),
|
|
114
|
+
installDeps = true,
|
|
115
|
+
} = options;
|
|
116
|
+
|
|
117
|
+
if (!name || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
|
|
118
|
+
console.error('Error: app name must start with a letter and contain only letters, digits, hyphens or underscores.');
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const appDir = path.join(outDir, name);
|
|
123
|
+
if (fs.existsSync(appDir)) {
|
|
124
|
+
console.error(`Error: directory "${appDir}" already exists.`);
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const cliDir = path.resolve(__dirname, '..');
|
|
129
|
+
const templateDir = path.join(cliDir, 'templates', template);
|
|
130
|
+
if (!fs.existsSync(templateDir)) {
|
|
131
|
+
console.error(`Error: template "${template}" not found in ${path.join(cliDir, 'templates')}`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const rootDir = findRootDir(outDir);
|
|
136
|
+
|
|
137
|
+
// cmake target name: replace hyphens with underscores
|
|
138
|
+
const cmakeTarget = name.replace(/-/g, '_');
|
|
139
|
+
const appTitle = name
|
|
140
|
+
.split(/[-_]/)
|
|
141
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
142
|
+
.join(' ');
|
|
143
|
+
|
|
144
|
+
const tokens = {
|
|
145
|
+
'{{APP_NAME}}': name,
|
|
146
|
+
'{{APP_TITLE}}': appTitle,
|
|
147
|
+
'{{CMAKE_TARGET}}': cmakeTarget,
|
|
148
|
+
'{{REPO_ROOT}}': rootDir.replace(/\\/g, '/'),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
console.log(`\n=== WindowPP Create — ${name} (template: ${template}) ===\n`);
|
|
152
|
+
console.log(`Scaffolding into: ${appDir}\n`);
|
|
153
|
+
|
|
154
|
+
// ── Copy template ─────────────────────────────────────────────────────────
|
|
155
|
+
copyDir(templateDir, appDir, tokens);
|
|
156
|
+
|
|
157
|
+
// ── Register in CMakeLists.txt ────────────────────────────────────────────
|
|
158
|
+
const appDirRelative = path.relative(rootDir, appDir).replace(/\\/g, '/');
|
|
159
|
+
addToCMake(rootDir, name, cmakeTarget, appDirRelative);
|
|
160
|
+
|
|
161
|
+
// ── Install frontend deps ─────────────────────────────────────────────────
|
|
162
|
+
const frontendDir = path.join(appDir, 'frontend');
|
|
163
|
+
if (installDeps && fs.existsSync(frontendDir)) {
|
|
164
|
+
console.log('Installing frontend dependencies...');
|
|
165
|
+
execSync('npm install', { cwd: frontendDir, stdio: 'inherit' });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
console.log(`\n✓ Created "${name}"\n`);
|
|
169
|
+
console.log('Next steps:');
|
|
170
|
+
console.log(` cd ${name}`);
|
|
171
|
+
console.log(' windowpp dev\n');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
function copyDir(src, dest, tokens) {
|
|
177
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
178
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
179
|
+
const srcPath = path.join(src, entry.name);
|
|
180
|
+
const destPath = path.join(dest, entry.name);
|
|
181
|
+
if (entry.isDirectory()) {
|
|
182
|
+
copyDir(srcPath, destPath, tokens);
|
|
183
|
+
} else {
|
|
184
|
+
copyFile(srcPath, destPath, tokens);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const TEXT_EXTENSIONS = new Set([
|
|
190
|
+
'.cpp', '.h', '.ts', '.tsx', '.js', '.json', '.html',
|
|
191
|
+
'.css', '.md', '.txt', '.cmake', '.sh', '.env',
|
|
192
|
+
]);
|
|
193
|
+
|
|
194
|
+
function copyFile(src, dest, tokens) {
|
|
195
|
+
const ext = path.extname(src).toLowerCase();
|
|
196
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
197
|
+
let content = fs.readFileSync(src, 'utf8');
|
|
198
|
+
for (const [token, value] of Object.entries(tokens)) {
|
|
199
|
+
content = content.split(token).join(value);
|
|
200
|
+
}
|
|
201
|
+
fs.writeFileSync(dest, content, 'utf8');
|
|
202
|
+
} else {
|
|
203
|
+
fs.copyFileSync(src, dest);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function addToCMake(rootDir, appName, cmakeTarget, appDirRelative) {
|
|
208
|
+
const cmakePath = path.join(rootDir, 'CMakeLists.txt');
|
|
209
|
+
let cmake = fs.readFileSync(cmakePath, 'utf8');
|
|
210
|
+
|
|
211
|
+
// Insert before the # ─── Install section
|
|
212
|
+
const installMarker = '# ─── Install ──';
|
|
213
|
+
if (!cmake.includes(installMarker)) {
|
|
214
|
+
console.warn('Warning: could not locate Install section in CMakeLists.txt — skipping auto-registration.');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const appBlock = generateCMakeBlock(appName, cmakeTarget, appDirRelative);
|
|
219
|
+
cmake = cmake.replace(installMarker, appBlock + '\n' + installMarker);
|
|
220
|
+
fs.writeFileSync(cmakePath, cmake, 'utf8');
|
|
221
|
+
console.log(`Registered ${cmakeTarget} in CMakeLists.txt`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function generateCMakeBlock(appName, cmakeTarget, appDirRelative) {
|
|
225
|
+
const generatedVar = cmakeTarget.toUpperCase() + '_GENERATED_DIR';
|
|
226
|
+
const embeddedCppVar = cmakeTarget.toUpperCase() + '_EMBEDDED_ASSETS_CPP';
|
|
227
|
+
const embeddedHVar = cmakeTarget.toUpperCase() + '_EMBEDDED_ASSETS_H';
|
|
228
|
+
const assetsTarget = cmakeTarget + '_frontend_assets';
|
|
229
|
+
|
|
230
|
+
return `# ─── ${appName} ──────────────────────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
if(WPP_BUILD_EXAMPLES)
|
|
233
|
+
add_executable(${cmakeTarget} ${appDirRelative}/main.cpp)
|
|
234
|
+
target_link_libraries(${cmakeTarget} PRIVATE windowpp nlohmann_json::nlohmann_json)
|
|
235
|
+
if(WIN32 AND WEBVIEW2_INCLUDE_DIR)
|
|
236
|
+
target_include_directories(${cmakeTarget} PRIVATE \${WEBVIEW2_INCLUDE_DIR})
|
|
237
|
+
if(WEBVIEW2_STATIC)
|
|
238
|
+
target_link_libraries(${cmakeTarget} PRIVATE
|
|
239
|
+
\${CMAKE_CURRENT_SOURCE_DIR}/src/renderer/webview/x64/WebView2LoaderStatic.lib
|
|
240
|
+
version)
|
|
241
|
+
else()
|
|
242
|
+
target_link_libraries(${cmakeTarget} PRIVATE
|
|
243
|
+
\${CMAKE_CURRENT_SOURCE_DIR}/src/renderer/webview/x64/WebView2Loader.lib)
|
|
244
|
+
endif()
|
|
245
|
+
endif()
|
|
246
|
+
|
|
247
|
+
set(${generatedVar} \${CMAKE_BINARY_DIR}/generated_${cmakeTarget})
|
|
248
|
+
set(${embeddedCppVar} \${${generatedVar}}/embedded_assets.cpp)
|
|
249
|
+
set(${embeddedHVar} \${${generatedVar}}/embedded_assets.h)
|
|
250
|
+
|
|
251
|
+
add_custom_command(
|
|
252
|
+
OUTPUT \${${embeddedCppVar}} \${${embeddedHVar}}
|
|
253
|
+
COMMAND \${CMAKE_COMMAND} -E make_directory \${${generatedVar}}
|
|
254
|
+
COMMAND \${Python3_EXECUTABLE}
|
|
255
|
+
\${WPP_EMBED_SCRIPT}
|
|
256
|
+
\${CMAKE_CURRENT_SOURCE_DIR}/${appDirRelative}/frontend/dist
|
|
257
|
+
\${${embeddedCppVar}}
|
|
258
|
+
\${${embeddedHVar}}
|
|
259
|
+
DEPENDS \${CMAKE_CURRENT_SOURCE_DIR}/${appDirRelative}/frontend/dist/index.html
|
|
260
|
+
COMMENT "Embedding ${appName} frontend assets into C++ byte arrays..."
|
|
261
|
+
VERBATIM
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
set_source_files_properties(
|
|
265
|
+
\${${embeddedCppVar}}
|
|
266
|
+
\${${embeddedHVar}}
|
|
267
|
+
PROPERTIES GENERATED TRUE
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
add_custom_target(${assetsTarget} ALL
|
|
271
|
+
DEPENDS \${${embeddedCppVar}} \${${embeddedHVar}}
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
target_sources(${cmakeTarget} PRIVATE \${${embeddedCppVar}})
|
|
275
|
+
target_include_directories(${cmakeTarget} PRIVATE \${${generatedVar}})
|
|
276
|
+
target_compile_definitions(${cmakeTarget} PRIVATE WPP_EMBEDDED_ASSETS)
|
|
277
|
+
add_dependencies(${cmakeTarget} ${assetsTarget})
|
|
278
|
+
endif()
|
|
279
|
+
|
|
280
|
+
`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = { create };
|
package/lib/dev.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cli/lib/dev.js — WindowPP dev mode
|
|
3
|
+
//
|
|
4
|
+
// Builds the native binary (Debug), starts the Vite dev server, then launches
|
|
5
|
+
// the binary with WPP_DEV_URL pointing at Vite. Keeps both alive and cleans
|
|
6
|
+
// up either when the app closes or Vite stops.
|
|
7
|
+
//
|
|
8
|
+
// Usage (via CLI or directly):
|
|
9
|
+
// windowpp dev [--clean] [--app-dir <path>] [--port 3000]
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const { spawn, execSync } = require('child_process');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const { build, findRootDir, resolveTarget, exePathFor } = require('./build');
|
|
17
|
+
|
|
18
|
+
function dev(options = {}) {
|
|
19
|
+
const {
|
|
20
|
+
appDir = process.cwd(),
|
|
21
|
+
clean = false,
|
|
22
|
+
port = null, // auto-detect from vite config; can be overridden
|
|
23
|
+
} = options;
|
|
24
|
+
|
|
25
|
+
const rootDir = findRootDir(appDir);
|
|
26
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
27
|
+
const IS_MAC = process.platform === 'darwin';
|
|
28
|
+
const platformDir = IS_WINDOWS ? 'windows' : IS_MAC ? 'macos' : 'linux';
|
|
29
|
+
const buildDir = path.join(rootDir, 'build', platformDir);
|
|
30
|
+
const frontendDir = path.join(appDir, 'frontend');
|
|
31
|
+
const target = resolveTarget(appDir);
|
|
32
|
+
|
|
33
|
+
console.log(`\n=== WindowPP Dev — ${target} (${platformDir}) ===\n`);
|
|
34
|
+
|
|
35
|
+
if (clean && fs.existsSync(buildDir)) {
|
|
36
|
+
console.log('Cleaning build directory...');
|
|
37
|
+
fs.rmSync(buildDir, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
fs.mkdirSync(buildDir, { recursive: true });
|
|
40
|
+
|
|
41
|
+
// ── Ensure a frontend dist exists for the embed step ─────────────────────
|
|
42
|
+
const hasDist = fs.existsSync(path.join(frontendDir, 'dist', 'index.html'));
|
|
43
|
+
if (fs.existsSync(frontendDir) && (!hasDist || clean)) {
|
|
44
|
+
console.log('Building frontend with Vite (for embed step)...');
|
|
45
|
+
execSync('npm install', { cwd: frontendDir, stdio: 'inherit' });
|
|
46
|
+
execSync('npm run build', { cwd: frontendDir, stdio: 'inherit' });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── CMake configure + Debug build ─────────────────────────────────────────
|
|
50
|
+
const cacheFile = path.join(buildDir, 'CMakeCache.txt');
|
|
51
|
+
const generatedExists = fs.existsSync(buildDir) &&
|
|
52
|
+
fs.readdirSync(buildDir).some(n => n.startsWith('generated'));
|
|
53
|
+
if (fs.existsSync(cacheFile) && !generatedExists) {
|
|
54
|
+
console.log('Stale cache detected — forcing re-configure...');
|
|
55
|
+
fs.rmSync(cacheFile, { force: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const embedScript = path.join(__dirname, '../cmake/embed_assets.py').replace(/\\/g, '/');
|
|
59
|
+
const frameworkDir = path.join(__dirname, '../framework').replace(/\\/g, '/');
|
|
60
|
+
console.log('Configuring CMake...');
|
|
61
|
+
execSync(
|
|
62
|
+
`cmake -S "${rootDir}" -B "${buildDir}"` +
|
|
63
|
+
` -DWPP_EMBED_SCRIPT="${embedScript}"` +
|
|
64
|
+
` -DWPP_FRAMEWORK_DIR="${frameworkDir}"`,
|
|
65
|
+
{ cwd: rootDir, stdio: 'inherit' }
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
console.log(`\nBuilding ${target} (Debug)...`);
|
|
69
|
+
const buildArgs = ['cmake', '--build', `"${buildDir}"`, '--target', target];
|
|
70
|
+
if (IS_WINDOWS) buildArgs.push('--config', 'Debug');
|
|
71
|
+
execSync(buildArgs.join(' '), { cwd: rootDir, stdio: 'inherit' });
|
|
72
|
+
|
|
73
|
+
const exePath = exePathFor(buildDir, target, 'Debug', IS_WINDOWS);
|
|
74
|
+
console.log(`\nBuilt: ${exePath}\n`);
|
|
75
|
+
|
|
76
|
+
// ── Detect Vite port from vite.config or fallback ─────────────────────────
|
|
77
|
+
const devPort = port || detectVitePort(frontendDir) || 3000;
|
|
78
|
+
const devUrl = `http://localhost:${devPort}`;
|
|
79
|
+
|
|
80
|
+
// ── Start Vite dev server ─────────────────────────────────────────────────
|
|
81
|
+
console.log(`Starting Vite dev server on port ${devPort}...`);
|
|
82
|
+
const vite = spawn('npm', ['run', 'dev'], {
|
|
83
|
+
cwd: frontendDir,
|
|
84
|
+
stdio: 'inherit',
|
|
85
|
+
shell: IS_WINDOWS,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let app = null;
|
|
89
|
+
|
|
90
|
+
function killProc(proc, name) {
|
|
91
|
+
if (!proc) return;
|
|
92
|
+
if (IS_WINDOWS) {
|
|
93
|
+
try { spawn('taskkill', ['/pid', proc.pid.toString(), '/f', '/t'], { shell: true }); } catch {}
|
|
94
|
+
} else {
|
|
95
|
+
try { proc.kill('SIGTERM'); } catch {}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function cleanup(signal) {
|
|
100
|
+
console.log(`\n${signal || 'Exit'} — cleaning up...`);
|
|
101
|
+
killProc(app, 'app'); app = null;
|
|
102
|
+
killProc(vite, 'vite');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
process.on('SIGINT', () => { cleanup('SIGINT'); process.exit(0); });
|
|
106
|
+
process.on('SIGTERM', () => { cleanup('SIGTERM'); process.exit(0); });
|
|
107
|
+
|
|
108
|
+
// Launch app 2 s after Vite starts (gives the dev server time to bind)
|
|
109
|
+
setTimeout(() => {
|
|
110
|
+
console.log(`\nLaunching ${target}...\n`);
|
|
111
|
+
app = spawn(exePath, [], {
|
|
112
|
+
stdio: 'inherit',
|
|
113
|
+
shell: IS_WINDOWS,
|
|
114
|
+
detached: false,
|
|
115
|
+
env: { ...process.env, WPP_DEV_URL: devUrl },
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
app.on('close', (code) => {
|
|
119
|
+
console.log(`\nApp exited (code ${code})`);
|
|
120
|
+
killProc(vite, 'vite');
|
|
121
|
+
process.exit(code ?? 0);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
app.on('error', (err) => {
|
|
125
|
+
console.error(`\nApp error: ${err.message}`);
|
|
126
|
+
cleanup();
|
|
127
|
+
process.exit(1);
|
|
128
|
+
});
|
|
129
|
+
}, 2000);
|
|
130
|
+
|
|
131
|
+
vite.on('close', (code) => {
|
|
132
|
+
console.log(`Vite stopped (code ${code})`);
|
|
133
|
+
if (app) {
|
|
134
|
+
console.log('Vite closed — killing app...');
|
|
135
|
+
killProc(app, 'app');
|
|
136
|
+
app = null;
|
|
137
|
+
}
|
|
138
|
+
process.exit(code ?? 0);
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function detectVitePort(frontendDir) {
|
|
143
|
+
// Try to read the port from vite.config.ts/js
|
|
144
|
+
const candidates = ['vite.config.ts', 'vite.config.js', 'vite.config.mjs'];
|
|
145
|
+
for (const c of candidates) {
|
|
146
|
+
const file = path.join(frontendDir, c);
|
|
147
|
+
if (!fs.existsSync(file)) continue;
|
|
148
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
149
|
+
const m = text.match(/port\s*:\s*(\d+)/);
|
|
150
|
+
if (m) return parseInt(m[1], 10);
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
module.exports = { dev };
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "windowpp",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "WindowPP CLI — build, dev, and scaffold for WindowPP apps",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"bin": {
|
|
7
|
+
"windowpp": "./bin/windowpp.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"lib/",
|
|
12
|
+
"templates/",
|
|
13
|
+
"cmake/",
|
|
14
|
+
"framework/",
|
|
15
|
+
"scripts/"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"sync": "node scripts/sync-framework.js",
|
|
19
|
+
"prepublishOnly": "node scripts/sync-framework.js"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=18"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cli/scripts/publish.js
|
|
3
|
+
//
|
|
4
|
+
// Syncs framework source, bumps the patch version in cli/package.json,
|
|
5
|
+
// then runs `npm publish` from the cli/ directory.
|
|
6
|
+
//
|
|
7
|
+
// Prerequisites: npm login (once per machine)
|
|
8
|
+
//
|
|
9
|
+
// Run from repo root: npm run package
|
|
10
|
+
// Or directly: node cli/scripts/publish.js [--minor] [--major]
|
|
11
|
+
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const { execSync } = require('child_process');
|
|
17
|
+
|
|
18
|
+
const CLI_DIR = path.join(__dirname, '..');
|
|
19
|
+
const PKG_PATH = path.join(CLI_DIR, 'package.json');
|
|
20
|
+
|
|
21
|
+
// ── Check npm login ───────────────────────────────────────────────────────────
|
|
22
|
+
try {
|
|
23
|
+
execSync('npm whoami', { stdio: 'pipe' });
|
|
24
|
+
} catch {
|
|
25
|
+
console.error('\nError: not logged in to npm. Run:\n\n npm login\n\nthen try again.\n');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── Parse bump type ───────────────────────────────────────────────────────────
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
const bumpType = args.includes('--major') ? 'major'
|
|
32
|
+
: args.includes('--minor') ? 'minor'
|
|
33
|
+
: 'patch';
|
|
34
|
+
|
|
35
|
+
// ── Read current version ──────────────────────────────────────────────────────
|
|
36
|
+
const pkg = JSON.parse(fs.readFileSync(PKG_PATH, 'utf8'));
|
|
37
|
+
const [major, minor, patch] = pkg.version.split('.').map(Number);
|
|
38
|
+
|
|
39
|
+
let nextVersion;
|
|
40
|
+
if (bumpType === 'major') nextVersion = `${major + 1}.0.0`;
|
|
41
|
+
else if (bumpType === 'minor') nextVersion = `${major}.${minor + 1}.0`;
|
|
42
|
+
else nextVersion = `${major}.${minor}.${patch + 1}`;
|
|
43
|
+
|
|
44
|
+
console.log(`\n=== windowpp publish: ${pkg.version} → ${nextVersion} (${bumpType}) ===\n`);
|
|
45
|
+
|
|
46
|
+
// ── Sync framework source ─────────────────────────────────────────────────────
|
|
47
|
+
console.log('Syncing framework source...');
|
|
48
|
+
execSync('node scripts/sync-framework.js', { cwd: CLI_DIR, stdio: 'inherit' });
|
|
49
|
+
|
|
50
|
+
// ── Publish with the new version (without writing it yet) ─────────────────────
|
|
51
|
+
// Temporarily write the new version so npm publish picks it up, then revert on failure.
|
|
52
|
+
pkg.version = nextVersion;
|
|
53
|
+
fs.writeFileSync(PKG_PATH, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
console.log('\nPublishing to npm...\n');
|
|
57
|
+
execSync('npm publish', { cwd: CLI_DIR, stdio: 'inherit' });
|
|
58
|
+
} catch (err) {
|
|
59
|
+
// Revert the version bump so the file stays clean
|
|
60
|
+
pkg.version = `${major}.${minor}.${patch}`;
|
|
61
|
+
fs.writeFileSync(PKG_PATH, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
62
|
+
console.error(`\nPublish failed — reverted version back to ${pkg.version}\n`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
console.log(`\n✓ Published windowpp@${nextVersion}\n`);
|
|
67
|
+
|