windowpp 0.1.2 → 0.1.4
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 +4 -1
- package/lib/create.js +63 -2
- package/package.json +4 -2
- package/scripts/publish.js +4 -0
- package/scripts/sync-templates.js +238 -0
- package/templates/example/CMakeLists.txt +59 -0
- package/templates/example/frontend/index.html +20 -0
- package/templates/example/frontend/src/API.ts +56 -0
- package/templates/example/frontend/src/App.tsx +781 -0
- package/templates/example/frontend/src/Layout.tsx +5 -0
- package/templates/example/frontend/src/components/ClipboardToast.tsx +23 -0
- package/templates/example/frontend/src/components/FileSearch.tsx +936 -0
- package/templates/example/frontend/src/components/InfiniteScrollList.tsx +267 -0
- package/templates/example/frontend/src/components/index.ts +13 -0
- package/templates/example/frontend/src/filedrop.css +421 -0
- package/templates/example/frontend/src/index.css +1 -0
- package/templates/example/frontend/src/index.tsx +24 -0
- package/templates/example/frontend/src/pages/About.tsx +47 -0
- package/templates/example/frontend/src/pages/Settings.tsx +37 -0
- package/templates/example/frontend/tsconfig.json +20 -0
- package/templates/example/frontend/vite.config.ts +27 -0
- package/templates/example/main.cpp +224 -0
- package/templates/example/package.json +12 -0
- package/templates/solid/CMakeLists.txt +4 -1
- package/templates/solid/frontend/index.html +16 -0
- package/templates/solid/frontend/src/App.tsx +8 -0
- package/templates/solid/frontend/src/index.css +1 -0
- package/templates/solid/frontend/src/index.tsx +12 -0
- package/templates/solid/frontend/tsconfig.json +15 -0
- package/templates/solid/frontend/vite.config.ts +3 -1
package/bin/windowpp.js
CHANGED
|
@@ -51,7 +51,10 @@ switch (command) {
|
|
|
51
51
|
}
|
|
52
52
|
const flags = parseFlags(args.slice(2));
|
|
53
53
|
const { create } = require('../lib/create');
|
|
54
|
-
create(name, { template: flags.template, outDir: flags.outDir })
|
|
54
|
+
create(name, { template: flags.template, outDir: flags.outDir }).catch((err) => {
|
|
55
|
+
console.error(err.message || err);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
55
58
|
break;
|
|
56
59
|
}
|
|
57
60
|
|
package/lib/create.js
CHANGED
|
@@ -5,8 +5,45 @@
|
|
|
5
5
|
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const fs = require('fs');
|
|
8
|
+
const readline = require('readline');
|
|
8
9
|
const { execSync } = require('child_process');
|
|
9
10
|
|
|
11
|
+
// ─── Interactive template picker ─────────────────────────────────────────────
|
|
12
|
+
function listTemplates(cliDir) {
|
|
13
|
+
const templatesDir = path.join(cliDir, 'templates');
|
|
14
|
+
if (!fs.existsSync(templatesDir)) return [];
|
|
15
|
+
return fs.readdirSync(templatesDir, { withFileTypes: true })
|
|
16
|
+
.filter(d => d.isDirectory())
|
|
17
|
+
.map(d => d.name)
|
|
18
|
+
.sort();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function promptTemplate(templates) {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
24
|
+
|
|
25
|
+
console.log('\nAvailable templates:');
|
|
26
|
+
templates.forEach((t, i) => console.log(` [${i + 1}] ${t}`));
|
|
27
|
+
console.log();
|
|
28
|
+
|
|
29
|
+
const ask = () => {
|
|
30
|
+
rl.question(`Pick a template [1-${templates.length}] (default: 1): `, (answer) => {
|
|
31
|
+
const trimmed = answer.trim();
|
|
32
|
+
if (trimmed === '') { rl.close(); resolve(templates[0]); return; }
|
|
33
|
+
const idx = parseInt(trimmed, 10) - 1;
|
|
34
|
+
if (idx >= 0 && idx < templates.length) {
|
|
35
|
+
rl.close();
|
|
36
|
+
resolve(templates[idx]);
|
|
37
|
+
} else {
|
|
38
|
+
console.log(` Please enter a number between 1 and ${templates.length}.`);
|
|
39
|
+
ask();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
ask();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
10
47
|
const TEXT_EXTENSIONS = new Set([
|
|
11
48
|
'.cpp', '.h', '.ts', '.tsx', '.js', '.json', '.html',
|
|
12
49
|
'.css', '.md', '.txt', '.cmake', '.sh', '.env',
|
|
@@ -38,9 +75,8 @@ function copyDir(src, dest, tokens) {
|
|
|
38
75
|
}
|
|
39
76
|
}
|
|
40
77
|
|
|
41
|
-
function create(name, options = {}) {
|
|
78
|
+
async function create(name, options = {}) {
|
|
42
79
|
const {
|
|
43
|
-
template = 'solid',
|
|
44
80
|
outDir = process.cwd(),
|
|
45
81
|
installDeps = true,
|
|
46
82
|
} = options;
|
|
@@ -50,6 +86,28 @@ function create(name, options = {}) {
|
|
|
50
86
|
process.exit(1);
|
|
51
87
|
}
|
|
52
88
|
|
|
89
|
+
const cliDir = path.resolve(__dirname, '..');
|
|
90
|
+
const templates = listTemplates(cliDir);
|
|
91
|
+
|
|
92
|
+
// Resolve template — prompt if none given or the name doesn't exist yet
|
|
93
|
+
let template = options.template;
|
|
94
|
+
if (!template) {
|
|
95
|
+
if (templates.length === 0) {
|
|
96
|
+
console.error('Error: no templates found in ' + path.join(cliDir, 'templates'));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
if (templates.length === 1) {
|
|
100
|
+
template = templates[0];
|
|
101
|
+
} else {
|
|
102
|
+
template = await promptTemplate(templates);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!name || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name)) {
|
|
107
|
+
console.error('Error: app name must start with a letter and contain only letters, digits, hyphens or underscores.');
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
53
111
|
const appDir = path.join(outDir, name);
|
|
54
112
|
if (fs.existsSync(appDir)) {
|
|
55
113
|
console.error(`Error: directory "${appDir}" already exists.`);
|
|
@@ -73,6 +131,9 @@ function create(name, options = {}) {
|
|
|
73
131
|
'{{APP_NAME}}': name,
|
|
74
132
|
'{{APP_TITLE}}': appTitle,
|
|
75
133
|
'{{CMAKE_TARGET}}': cmakeTarget,
|
|
134
|
+
// Resolved at create-time so vite.config / API.ts can import from the
|
|
135
|
+
// installed framework's src/ tree via the @wpp alias.
|
|
136
|
+
'{{REPO_ROOT}}': path.join(cliDir, 'framework').replace(/\\/g, '/'),
|
|
76
137
|
};
|
|
77
138
|
|
|
78
139
|
console.log(`\n=== WindowPP Create --- ${name} (template: ${template}) ===\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "windowpp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "WindowPP CLI — build, dev, and scaffold for WindowPP apps",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
],
|
|
17
17
|
"scripts": {
|
|
18
18
|
"sync": "node scripts/sync-framework.js",
|
|
19
|
-
"
|
|
19
|
+
"sync:templates": "node scripts/sync-templates.js",
|
|
20
|
+
"sync:all": "node scripts/sync-framework.js && node scripts/sync-templates.js",
|
|
21
|
+
"prepublishOnly": "node scripts/sync-framework.js && node scripts/sync-templates.js"
|
|
20
22
|
},
|
|
21
23
|
"engines": {
|
|
22
24
|
"node": ">=18"
|
package/scripts/publish.js
CHANGED
|
@@ -47,6 +47,10 @@ console.log(`\n=== windowpp publish: ${pkg.version} → ${nextVersion} (${bumpTy
|
|
|
47
47
|
console.log('Syncing framework source...');
|
|
48
48
|
execSync('node scripts/sync-framework.js', { cwd: CLI_DIR, stdio: 'inherit' });
|
|
49
49
|
|
|
50
|
+
// ── Sync templates from monorepo apps ────────────────────────────────────────
|
|
51
|
+
console.log('\nSyncing templates...');
|
|
52
|
+
execSync('node scripts/sync-templates.js', { cwd: CLI_DIR, stdio: 'inherit' });
|
|
53
|
+
|
|
50
54
|
// ── Publish with the new version (without writing it yet) ─────────────────────
|
|
51
55
|
// Temporarily write the new version so npm publish picks it up, then revert on failure.
|
|
52
56
|
pkg.version = nextVersion;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cli/scripts/sync-templates.js
|
|
3
|
+
//
|
|
4
|
+
// Syncs app directories from the monorepo into cli/templates/ as reusable
|
|
5
|
+
// project templates. Called manually or as part of the publish flow.
|
|
6
|
+
//
|
|
7
|
+
// Usage:
|
|
8
|
+
// node cli/scripts/sync-templates.js — sync all templates
|
|
9
|
+
// node cli/scripts/sync-templates.js example — sync one by name
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
|
|
16
|
+
const SCRIPT_DIR = __dirname;
|
|
17
|
+
const CLI_DIR = path.join(SCRIPT_DIR, '..');
|
|
18
|
+
const REPO_ROOT = path.join(CLI_DIR, '..');
|
|
19
|
+
const TEMPLATES_DIR = path.join(CLI_DIR, 'templates');
|
|
20
|
+
const BASE_TEMPLATE = path.join(TEMPLATES_DIR, 'solid');
|
|
21
|
+
|
|
22
|
+
// ─── Template source configuration ───────────────────────────────────────────
|
|
23
|
+
//
|
|
24
|
+
// Each entry describes one template that can be used with `windowpp create`.
|
|
25
|
+
// To register a new app as a template, add an entry here and run this script.
|
|
26
|
+
//
|
|
27
|
+
// Fields:
|
|
28
|
+
// name — template dir name → cli/templates/<name>/
|
|
29
|
+
// source — source dir relative to repo root
|
|
30
|
+
// appName — concrete npm package name in source (replaced → {{APP_NAME}})
|
|
31
|
+
// cmakeTarget — concrete cmake target in source (replaced → {{CMAKE_TARGET}})
|
|
32
|
+
// appTitle — human-readable title in source (replaced → {{APP_TITLE}})
|
|
33
|
+
// useBase — paths taken from the solid base template instead of source
|
|
34
|
+
// (these need standalone-specific content not in the monorepo app)
|
|
35
|
+
// skip — relative paths / bare file names to skip when copying from source
|
|
36
|
+
|
|
37
|
+
const TEMPLATE_SOURCES = [
|
|
38
|
+
{
|
|
39
|
+
name: 'example',
|
|
40
|
+
source: 'example-app',
|
|
41
|
+
appName: 'windowpp-example',
|
|
42
|
+
cmakeTarget: 'wpp_example',
|
|
43
|
+
appTitle: 'WindowPP Example',
|
|
44
|
+
// Use the standalone-compatible versions from the solid base template
|
|
45
|
+
useBase: [
|
|
46
|
+
'CMakeLists.txt',
|
|
47
|
+
'package.json',
|
|
48
|
+
'frontend/vite.config.ts',
|
|
49
|
+
],
|
|
50
|
+
// Skip monorepo-specific / generated / lock files
|
|
51
|
+
skip: [
|
|
52
|
+
'build.js',
|
|
53
|
+
'dev.js',
|
|
54
|
+
'frontend/node_modules',
|
|
55
|
+
'frontend/dist',
|
|
56
|
+
'frontend/pnpm-lock.yaml',
|
|
57
|
+
'frontend/package-lock.json',
|
|
58
|
+
'frontend/.gitignore',
|
|
59
|
+
'frontend/README.md',
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
// ── To add more templates, copy an entry like this: ───────────────────────
|
|
63
|
+
// {
|
|
64
|
+
// name: 'icup',
|
|
65
|
+
// source: 'ICUP',
|
|
66
|
+
// appName: 'wpp-icup',
|
|
67
|
+
// cmakeTarget: 'wpp_icup',
|
|
68
|
+
// appTitle: 'WindowPP ICUP',
|
|
69
|
+
// useBase: ['CMakeLists.txt', 'package.json', 'frontend/vite.config.ts'],
|
|
70
|
+
// skip: ['build.js', 'dev.js', 'frontend/node_modules', 'frontend/dist'],
|
|
71
|
+
// },
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
// ─── File extensions that get text/token substitution ────────────────────────
|
|
75
|
+
const TEXT_EXTENSIONS = new Set([
|
|
76
|
+
'.cpp', '.h', '.ts', '.tsx', '.js', '.json', '.html',
|
|
77
|
+
'.css', '.md', '.txt', '.cmake', '.sh', '.env',
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
// ─── Build the list of [from, to] string substitution pairs for an entry ─────
|
|
81
|
+
//
|
|
82
|
+
// Transforms applied to all text files:
|
|
83
|
+
//
|
|
84
|
+
// 1. Concrete app name / cmake target / title → template tokens
|
|
85
|
+
//
|
|
86
|
+
// 2. TypeScript relative imports from frontend/src/ depth (3 levels up to src/):
|
|
87
|
+
// '../../../src/Foo' → '@wpp/Foo'
|
|
88
|
+
// Covered by the vite alias: '@wpp' → resolve(REPO_ROOT, 'src')
|
|
89
|
+
//
|
|
90
|
+
// 3. TypeScript relative imports from frontend/ depth (2 levels up):
|
|
91
|
+
// '../../src/Foo' → '@wpp/Foo'
|
|
92
|
+
//
|
|
93
|
+
// 4. C++ #include relative back to src/:
|
|
94
|
+
// "#include "../src/Foo/bar.h"" → "#include "Foo/bar.h""
|
|
95
|
+
// Works because CMakeLists adds target_include_directories(...PRIVATE "${WPP_FRAMEWORK_DIR}/src")
|
|
96
|
+
|
|
97
|
+
function buildTokens(entry) {
|
|
98
|
+
return [
|
|
99
|
+
// ── Name tokens (longer strings first to avoid partial matches) ─────
|
|
100
|
+
[entry.appName, '{{APP_NAME}}'],
|
|
101
|
+
[entry.cmakeTarget, '{{CMAKE_TARGET}}'],
|
|
102
|
+
[entry.appTitle, '{{APP_TITLE}}'],
|
|
103
|
+
|
|
104
|
+
// ── TypeScript/TSX: monorepo-relative imports from frontend/src/ ────
|
|
105
|
+
["'../../../src/", "'@wpp/"],
|
|
106
|
+
['"../../../src/', '"@wpp/'],
|
|
107
|
+
|
|
108
|
+
// ── TypeScript/TSX: from frontend/ level ───────────────────────────
|
|
109
|
+
["'../../src/", "'@wpp/"],
|
|
110
|
+
['"../../src/', '"@wpp/'],
|
|
111
|
+
|
|
112
|
+
// ── C++ includes: "../src/SubDir/file.h" → "SubDir/file.h" ─────────
|
|
113
|
+
// (CMakeLists adds ${WPP_FRAMEWORK_DIR}/src as a private include dir)
|
|
114
|
+
['"../src/', '"'],
|
|
115
|
+
];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function applyTokens(content, tokens) {
|
|
119
|
+
for (const [from, to] of tokens) {
|
|
120
|
+
content = content.split(from).join(to);
|
|
121
|
+
}
|
|
122
|
+
return content;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ─── File copy helpers ───────────────────────────────────────────────────────
|
|
126
|
+
function copyFileTransformed(src, dest, tokens) {
|
|
127
|
+
const ext = path.extname(src).toLowerCase();
|
|
128
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
129
|
+
let content = fs.readFileSync(src, 'utf8');
|
|
130
|
+
content = applyTokens(content, tokens);
|
|
131
|
+
fs.writeFileSync(dest, content, 'utf8');
|
|
132
|
+
} else {
|
|
133
|
+
fs.copyFileSync(src, dest);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Copy a directory recursively, respecting a per-directory skip set.
|
|
138
|
+
// relPath tracks the path relative to the template root for skip matching.
|
|
139
|
+
function copySourceDir(src, dest, tokens, skipRelPaths, relPrefix) {
|
|
140
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
141
|
+
for (const dirent of fs.readdirSync(src, { withFileTypes: true })) {
|
|
142
|
+
const relPath = relPrefix ? `${relPrefix}/${dirent.name}` : dirent.name;
|
|
143
|
+
// Skip if matched by bare name OR by relative path
|
|
144
|
+
if (skipRelPaths.has(dirent.name) || skipRelPaths.has(relPath)) continue;
|
|
145
|
+
|
|
146
|
+
const srcPath = path.join(src, dirent.name);
|
|
147
|
+
const destPath = path.join(dest, dirent.name);
|
|
148
|
+
|
|
149
|
+
if (dirent.isDirectory()) {
|
|
150
|
+
copySourceDir(srcPath, destPath, tokens, skipRelPaths, relPath);
|
|
151
|
+
} else {
|
|
152
|
+
copyFileTransformed(srcPath, destPath, tokens);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ─── Sync a single template entry ────────────────────────────────────────────
|
|
158
|
+
function syncTemplate(entry) {
|
|
159
|
+
const srcDir = path.join(REPO_ROOT, entry.source);
|
|
160
|
+
const destDir = path.join(TEMPLATES_DIR, entry.name);
|
|
161
|
+
|
|
162
|
+
if (!fs.existsSync(srcDir)) {
|
|
163
|
+
console.warn(` ⚠ source not found: ${entry.source} — skipping`);
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Clear and recreate the destination template directory
|
|
168
|
+
if (fs.existsSync(destDir)) {
|
|
169
|
+
fs.rmSync(destDir, { recursive: true, force: true });
|
|
170
|
+
}
|
|
171
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
172
|
+
|
|
173
|
+
const tokens = buildTokens(entry);
|
|
174
|
+
const useBaseSet = new Set(entry.useBase ?? []);
|
|
175
|
+
const skipRelPaths = new Set(entry.skip ?? []);
|
|
176
|
+
|
|
177
|
+
// Also skip the "useBase" paths when copying from source
|
|
178
|
+
for (const rel of useBaseSet) skipRelPaths.add(rel);
|
|
179
|
+
|
|
180
|
+
let count = 0;
|
|
181
|
+
|
|
182
|
+
// 1. Overlay "base" files from cli/templates/solid/ (with this entry's tokens)
|
|
183
|
+
for (const rel of useBaseSet) {
|
|
184
|
+
const baseSrc = path.join(BASE_TEMPLATE, rel);
|
|
185
|
+
const baseDest = path.join(destDir, rel);
|
|
186
|
+
if (!fs.existsSync(baseSrc)) {
|
|
187
|
+
console.warn(` ⚠ base file not found: ${rel}`);
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
fs.mkdirSync(path.dirname(baseDest), { recursive: true });
|
|
191
|
+
copyFileTransformed(baseSrc, baseDest, tokens);
|
|
192
|
+
count++;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 2. Copy everything else from the source app
|
|
196
|
+
copySourceDir(srcDir, destDir, tokens, skipRelPaths, '');
|
|
197
|
+
|
|
198
|
+
// Count total destination files
|
|
199
|
+
count = countFiles(destDir);
|
|
200
|
+
return count;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function countFiles(dir) {
|
|
204
|
+
let n = 0;
|
|
205
|
+
for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
206
|
+
if (dirent.isDirectory()) n += countFiles(path.join(dir, dirent.name));
|
|
207
|
+
else n++;
|
|
208
|
+
}
|
|
209
|
+
return n;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ─── Entry point ─────────────────────────────────────────────────────────────
|
|
213
|
+
function run() {
|
|
214
|
+
const filter = process.argv[2]; // optional: name of one template to sync
|
|
215
|
+
const targets = filter
|
|
216
|
+
? TEMPLATE_SOURCES.filter(e => e.name === filter)
|
|
217
|
+
: TEMPLATE_SOURCES;
|
|
218
|
+
|
|
219
|
+
if (filter && targets.length === 0) {
|
|
220
|
+
const names = TEMPLATE_SOURCES.map(e => e.name).join(', ');
|
|
221
|
+
console.error(`\nNo template named "${filter}". Available: ${names || '(none)'}\n`);
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
console.log('\n=== windowpp: syncing templates ===\n');
|
|
226
|
+
|
|
227
|
+
let total = 0;
|
|
228
|
+
for (const entry of targets) {
|
|
229
|
+
process.stdout.write(` ${entry.source} → templates/${entry.name}/ ...`);
|
|
230
|
+
const n = syncTemplate(entry);
|
|
231
|
+
process.stdout.write(` ${n} file(s)\n`);
|
|
232
|
+
total += n;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
console.log(`\n✓ Synced ${targets.length} template(s) (${total} total files)\n`);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
run();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.16)
|
|
2
|
+
project({{CMAKE_TARGET}} VERSION 0.1.0 LANGUAGES CXX)
|
|
3
|
+
|
|
4
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
5
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
6
|
+
|
|
7
|
+
find_package(Python3 REQUIRED COMPONENTS Interpreter)
|
|
8
|
+
|
|
9
|
+
# ─── WindowPP Framework ───────────────────────────────────────────────────────
|
|
10
|
+
# WPP_FRAMEWORK_DIR is injected by `windowpp build` / `windowpp dev`.
|
|
11
|
+
# To build manually without the CLI:
|
|
12
|
+
# cmake -S . -B build -DWPP_FRAMEWORK_DIR=/path/to/windowpp/framework
|
|
13
|
+
if(NOT DEFINED WPP_FRAMEWORK_DIR)
|
|
14
|
+
message(FATAL_ERROR
|
|
15
|
+
"WPP_FRAMEWORK_DIR is not set.\n"
|
|
16
|
+
" Build via: windowpp build\n"
|
|
17
|
+
" Or pass: -DWPP_FRAMEWORK_DIR=<path/to/windowpp/cli/framework>"
|
|
18
|
+
)
|
|
19
|
+
endif()
|
|
20
|
+
|
|
21
|
+
add_subdirectory("${WPP_FRAMEWORK_DIR}" "${CMAKE_BINARY_DIR}/windowpp_lib")
|
|
22
|
+
|
|
23
|
+
# ─── App Target ───────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
set(GENERATED_DIR ${CMAKE_BINARY_DIR}/generated)
|
|
26
|
+
set(EMBEDDED_ASSETS_CPP ${GENERATED_DIR}/embedded_assets.cpp)
|
|
27
|
+
set(EMBEDDED_ASSETS_H ${GENERATED_DIR}/embedded_assets.h)
|
|
28
|
+
|
|
29
|
+
add_custom_command(
|
|
30
|
+
OUTPUT ${EMBEDDED_ASSETS_CPP} ${EMBEDDED_ASSETS_H}
|
|
31
|
+
COMMAND ${CMAKE_COMMAND} -E make_directory ${GENERATED_DIR}
|
|
32
|
+
COMMAND ${Python3_EXECUTABLE}
|
|
33
|
+
${WPP_EMBED_SCRIPT}
|
|
34
|
+
${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist
|
|
35
|
+
${EMBEDDED_ASSETS_CPP}
|
|
36
|
+
${EMBEDDED_ASSETS_H}
|
|
37
|
+
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/frontend/dist/index.html
|
|
38
|
+
COMMENT "Embedding {{APP_TITLE}} frontend assets into C++ byte arrays..."
|
|
39
|
+
VERBATIM
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
set_source_files_properties(
|
|
43
|
+
${EMBEDDED_ASSETS_CPP}
|
|
44
|
+
${EMBEDDED_ASSETS_H}
|
|
45
|
+
PROPERTIES GENERATED TRUE
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
add_custom_target({{CMAKE_TARGET}}_frontend_assets ALL
|
|
49
|
+
DEPENDS ${EMBEDDED_ASSETS_CPP} ${EMBEDDED_ASSETS_H}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
add_executable({{CMAKE_TARGET}} main.cpp ${EMBEDDED_ASSETS_CPP})
|
|
53
|
+
target_include_directories({{CMAKE_TARGET}} PRIVATE
|
|
54
|
+
${GENERATED_DIR}
|
|
55
|
+
"${WPP_FRAMEWORK_DIR}/src" # bridge headers: AppData/, FileSystem/, Input/, platform/
|
|
56
|
+
)
|
|
57
|
+
target_compile_definitions({{CMAKE_TARGET}} PRIVATE WPP_EMBEDDED_ASSETS)
|
|
58
|
+
target_link_libraries({{CMAKE_TARGET}} PRIVATE windowpp windowpp_deps nlohmann_json::nlohmann_json)
|
|
59
|
+
add_dependencies({{CMAKE_TARGET}} {{CMAKE_TARGET}}_frontend_assets)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="theme-color" content="#000000" />
|
|
7
|
+
<title>Solid App</title>
|
|
8
|
+
<script>
|
|
9
|
+
// Kill WebView2/browser default "open file" on drag-and-drop
|
|
10
|
+
window.addEventListener('dragover', function(e) { e.preventDefault(); }, false);
|
|
11
|
+
window.addEventListener('drop', function(e) { e.preventDefault(); }, false);
|
|
12
|
+
</script>
|
|
13
|
+
</head>
|
|
14
|
+
<body>
|
|
15
|
+
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
16
|
+
<div id="root"></div>
|
|
17
|
+
|
|
18
|
+
<script src="/src/index.tsx" type="module"></script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// ─── AppData ─────────────────────────────────────────────────────────────────
|
|
2
|
+
export type { AppDataEntry, SeedEntry } from '@wpp/AppData/API/AppData';
|
|
3
|
+
export { default as appData } from '@wpp/AppData/API/AppData';
|
|
4
|
+
|
|
5
|
+
// ─── FileSystem ───────────────────────────────────────────────────────────────
|
|
6
|
+
export type {
|
|
7
|
+
FileStat,
|
|
8
|
+
DirEntry,
|
|
9
|
+
ApplicationEntry,
|
|
10
|
+
SearchOptions,
|
|
11
|
+
SearchStreamSummary,
|
|
12
|
+
SearchStreamCallbacks,
|
|
13
|
+
SearchStreamHandle,
|
|
14
|
+
} from '@wpp/FileSystem/API/FileSystem';
|
|
15
|
+
export { default as filesystem } from '@wpp/FileSystem/API/FileSystem';
|
|
16
|
+
|
|
17
|
+
// ─── FileDrop ─────────────────────────────────────────────────────────────────
|
|
18
|
+
export type { FileInfo, DropZone } from '@wpp/filedrop/filedrop';
|
|
19
|
+
export {
|
|
20
|
+
fileDrop,
|
|
21
|
+
formatFileSize,
|
|
22
|
+
createDropZone,
|
|
23
|
+
filterFilesByExtension,
|
|
24
|
+
filterFilesByMimeType,
|
|
25
|
+
isImageFile,
|
|
26
|
+
isVideoFile,
|
|
27
|
+
isAudioFile,
|
|
28
|
+
isTextFile,
|
|
29
|
+
} from '@wpp/filedrop/filedrop';
|
|
30
|
+
|
|
31
|
+
// ─── Input ────────────────────────────────────────────────────────────────────
|
|
32
|
+
export type { InputKeyEvent, ShortcutEvent } from '@wpp/Input/API/Input';
|
|
33
|
+
export { default as input } from '@wpp/Input/API/Input';
|
|
34
|
+
|
|
35
|
+
// ─── App (platform) ──────────────────────────────────────────────────────────
|
|
36
|
+
export type {
|
|
37
|
+
AppInfo,
|
|
38
|
+
Point,
|
|
39
|
+
Size,
|
|
40
|
+
Rect,
|
|
41
|
+
MonitorInfo,
|
|
42
|
+
ClipboardChangedEvent,
|
|
43
|
+
AppEvent,
|
|
44
|
+
} from '@wpp/platform/API/App';
|
|
45
|
+
export { default as appApi } from '@wpp/platform/API/App';
|
|
46
|
+
|
|
47
|
+
// ─── Window (platform) ───────────────────────────────────────────────────────
|
|
48
|
+
export type {
|
|
49
|
+
WindowState,
|
|
50
|
+
WindowInfo,
|
|
51
|
+
ResizeDetail,
|
|
52
|
+
MoveDetail,
|
|
53
|
+
WindowEvent,
|
|
54
|
+
CreateWindowOptions,
|
|
55
|
+
} from '@wpp/platform/API/Window';
|
|
56
|
+
export { default as windowApi } from '@wpp/platform/API/Window';
|