ewvjs 1.0.1 → 1.0.2

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/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ <img width="786" height="593" alt="image" src="https://github.com/user-attachments/assets/94957de5-1abd-458f-84cf-43174e05015c" />
2
+
1
3
  # ewvjs
2
4
 
3
5
  **Embedded WebView for JavaScript**
@@ -31,7 +33,6 @@ const { create_window, start } = require('ewvjs');
31
33
  const win = create_window('My App', 'https://www.google.com', {
32
34
  width: 1024,
33
35
  height: 768,
34
- dark_mode: true
35
36
  });
36
37
 
37
38
  // Run the window
@@ -98,9 +99,6 @@ Exposes a Node.js function to the frontend.
98
99
 
99
100
  ```typescript
100
101
  {
101
- title?: string; // Window title (overrides create_window arg)
102
- url?: string; // URL to load (overrides create_window arg)
103
- html?: string; // HTML content to load directly
104
102
  width?: number; // Window width
105
103
  height?: number; // Window height
106
104
  x?: number; // X position
@@ -135,13 +133,12 @@ Exposes a Node.js function to the frontend.
135
133
  Once a window is created, you can control it using the returned `Window` instance:
136
134
 
137
135
  * **Lifecycle**: `run()`, `close()`, `destroy()`
138
- * **State**: `maximize()`, `minimize()`, `restore()`, `show()`, `hide()`, `focus()`, `blur()`
136
+ * **State**: `maximize()`, `minimize()`, `restore()`, `hide()`, `focus()`, `show()`
139
137
  * **Size & Position**:
140
138
  * `getSize()`, `setSize(w, h)`, `resize(w, h)`
141
139
  * `getPosition()`, `setPosition(x, y)`, `move(x, y)`
142
140
  * **Interaction**:
143
141
  * `setTitle(title)`
144
- * `navigate(url)`
145
142
  * `evaluate(script)`: Execute JavaScript in the WebView.
146
143
  * `setIcon(path)`
147
144
  * **Cookies**: `get_cookies()`, `set_cookie(...)`, `clear_cookies()`
package/dist/js/api.js CHANGED
@@ -231,6 +231,15 @@ window.ewvjs = {
231
231
  }
232
232
  }, 10);
233
233
  },
234
+
235
+ _callWindowMethod: function (methodName) {
236
+ var __id = (Math.random() + "").substring(2);
237
+ window.chrome.webview.postMessage([
238
+ 'window_' + methodName,
239
+ '[]',
240
+ __id
241
+ ]);
242
+ },
234
243
  };
235
244
 
236
245
  window.ewvjs._hookConsole = function () {
@@ -254,3 +263,40 @@ window.ewvjs._hookConsole = function () {
254
263
 
255
264
  window.ewvjs._hookConsole();
256
265
  window.ewvjs._hookDrag();
266
+
267
+ // Add window state methods directly to window object
268
+ window.close = function () {
269
+ window.ewvjs._callWindowMethod('close');
270
+ };
271
+
272
+ window.maximize = function () {
273
+ window.ewvjs._callWindowMethod('maximize');
274
+ };
275
+
276
+ window.restore = function () {
277
+ window.ewvjs._callWindowMethod('restore');
278
+ };
279
+
280
+ window.minimize = function () {
281
+ window.ewvjs._callWindowMethod('minimize');
282
+ };
283
+
284
+ window.focus = function () {
285
+ window.ewvjs._callWindowMethod('focus');
286
+ };
287
+
288
+ window.show = function () {
289
+ window.ewvjs._callWindowMethod('show');
290
+ };
291
+
292
+ window.hide = function () {
293
+ window.ewvjs._callWindowMethod('hide');
294
+ };
295
+
296
+ window.resize = function () {
297
+ window.ewvjs._callWindowMethod('resize');
298
+ };
299
+
300
+ window.move = function () {
301
+ window.ewvjs._callWindowMethod('move');
302
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"webview.d.ts","sourceRoot":"","sources":["../src/webview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,qBAAa,OAAO;IAChB,QAAQ,EAAE,GAAG,CAAC;IACd,iBAAiB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE,CAAM;IACpD,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAAkB;;IAUtC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,GAAE,MAAW,EAAE,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,MAAM;IAgB9F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,OAAO,CAAC,QAAQ;IAkBhB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI;IAIpC,eAAe,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,OAAO,CAAC,mBAAmB;CA2B9B"}
1
+ {"version":3,"file":"webview.d.ts","sourceRoot":"","sources":["../src/webview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,qBAAa,OAAO;IAChB,QAAQ,EAAE,GAAG,CAAC;IACd,iBAAiB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE,CAAM;IACpD,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,UAAU,CAA+B;IACjD,OAAO,CAAC,YAAY,CAAkB;;IAUtC,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,GAAE,MAAW,EAAE,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAAG,MAAM;IAgB9F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,OAAO,CAAC,QAAQ;IAkBhB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI;IAIpC,eAAe,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlD,OAAO,CAAC,mBAAmB;CAgC9B"}
package/dist/webview.js CHANGED
@@ -84,7 +84,11 @@ class WebView {
84
84
  path: (_b = options.session) === null || _b === void 0 ? void 0 : _b.path,
85
85
  envname: (_c = options.session) === null || _c === void 0 ? void 0 : _c.envname
86
86
  }, jsCallback: this._handle_message.bind(this) }, options);
87
- if (url_or_html.startsWith('http') || url_or_html.startsWith('data:')) {
87
+ const urlRegex = /^(https?|file|data):/i;
88
+ const isUrl = urlRegex.test(url_or_html) ||
89
+ /^localhost(:\d+)?$/i.test(url_or_html) ||
90
+ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(:\d+)?$/i.test(url_or_html);
91
+ if (isUrl) {
88
92
  opts.url = url_or_html;
89
93
  }
90
94
  else {
package/dist/window.d.ts CHANGED
@@ -24,7 +24,6 @@ export declare class Window {
24
24
  restore(): Promise<any>;
25
25
  minimize(): Promise<any>;
26
26
  focus(): Promise<any>;
27
- blur(): Promise<any>;
28
27
  show(): Promise<any>;
29
28
  hide(): Promise<any>;
30
29
  getSize(): Promise<any>;
@@ -1 +1 @@
1
- {"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGzD,qBAAa,MAAM;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;IAEvB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,iBAAiB,CAA8B;IACvD,OAAO,CAAC,SAAS,CAAkB;IAEnC,eAAe,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,eAAe,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,eAAe,EAAE,GAAG,IAAI,CAAC,CAAc;gBAEjG,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE;IAiBhG,IAAI,MAAM,kBAET;IAED,IAAI,SAAS,YAEZ;IAEK,GAAG;YAkBK,KAAK;IA+Bb,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIzC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAItC,KAAK;IAQL,OAAO;IAKP,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,IAAI;IAGJ,OAAO;IACP,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAGrC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAIpC,WAAW;IACX,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAGhC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAIzB,YAAY;IACZ,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAIjC,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAK5B,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAM7B,QAAQ,CAAC,KAAK,EAAE,MAAM;IAGtB,SAAS,CAAC,KAAK,EAAE,MAAM;IAKvB,aAAa;IACb,aAAa;IAGb,OAAO,CAAC,QAAQ,EAAE,MAAM;IAKxB,WAAW;IACX,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAW,EAAE,IAAI,GAAE,MAAY;IAG/E,aAAa;YAEL,WAAW;IAsCzB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,gBAAgB;YAOV,kBAAkB;YAUlB,sBAAsB;YAmBtB,oBAAoB;YAMpB,kBAAkB;IAMhC,OAAO,CAAC,YAAY;CAkBvB"}
1
+ {"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../src/window.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGzD,qBAAa,MAAM;IACf,QAAQ,EAAE,GAAG,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;IAEvB,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,cAAc,CAAc;IACpC,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,iBAAiB,CAA8B;IACvD,OAAO,CAAC,SAAS,CAAkB;IAEnC,eAAe,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,eAAe,EAAE,GAAG,IAAI,GAAG,OAAO,CAAC,eAAe,EAAE,GAAG,IAAI,CAAC,CAAc;gBAEjG,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAA;KAAE;IAiBhG,IAAI,MAAM,kBAET;IAED,IAAI,SAAS,YAEZ;IAEK,GAAG;YAkBK,KAAK;IA+Bb,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAIzC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAItC,KAAK;IAQL,OAAO;IAKP,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,KAAK;IACL,IAAI;IACJ,IAAI;IAGJ,OAAO;IACP,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAGrC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAIpC,WAAW;IACX,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAGhC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAIzB,YAAY;IACZ,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAIjC,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAK5B,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAM7B,QAAQ,CAAC,KAAK,EAAE,MAAM;IAGtB,SAAS,CAAC,KAAK,EAAE,MAAM;IAKvB,aAAa;IACb,aAAa;IAGb,OAAO,CAAC,QAAQ,EAAE,MAAM;IAKxB,WAAW;IACX,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAE,MAAW,EAAE,IAAI,GAAE,MAAY;IAG/E,aAAa;YAEL,WAAW;IA+CzB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,gBAAgB;YAOV,kBAAkB;YAUlB,sBAAsB;YAmBtB,oBAAoB;YAMpB,kBAAkB;IAMhC,OAAO,CAAC,YAAY;CAkBvB"}
package/dist/window.js CHANGED
@@ -121,9 +121,6 @@ class Window {
121
121
  focus() {
122
122
  return __awaiter(this, void 0, void 0, function* () { return this._call('focus'); });
123
123
  }
124
- blur() {
125
- return __awaiter(this, void 0, void 0, function* () { return this._call('blur'); });
126
- }
127
124
  show() {
128
125
  return __awaiter(this, void 0, void 0, function* () { return this._call('show'); });
129
126
  }
@@ -239,6 +236,14 @@ class Window {
239
236
  if (funcName === 'context_menu_requested') {
240
237
  return this._handleContextMenu(params);
241
238
  }
239
+ // Handle window state methods
240
+ if (funcName.startsWith('window_')) {
241
+ const method = funcName.substring(7); // Remove 'window_' prefix
242
+ if (typeof this[method] === 'function') {
243
+ yield this[method]();
244
+ }
245
+ return null;
246
+ }
242
247
  // Handle exposed function calls
243
248
  return this._handleExposedFunction(funcName, params, id, exposedFunctions);
244
249
  }
Binary file
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ewvjs",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Embedded WebView for JavaScript - Edge WebView2 bindings for Node.js",
5
+ "workspaces": [
6
+ "packages/*"
7
+ ],
5
8
  "main": "./dist/index.js",
6
9
  "types": "./dist/index.d.ts",
7
- "bin": {
8
- "ewvjs": "./bin/ewvjs-cli.js"
9
- },
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
@@ -16,8 +16,6 @@
16
16
  "files": [
17
17
  "dist/**/*",
18
18
  "native/**/*",
19
- "bin/**/*",
20
- "lib/**/*",
21
19
  "src/csharp/bin/Release/**/*",
22
20
  "README.md",
23
21
  "LICENSE"
@@ -52,10 +50,6 @@
52
50
  "typescript": "^5.9.3"
53
51
  },
54
52
  "dependencies": {
55
- "@yao-pkg/pkg": "^6.12.0",
56
- "archiver": "^7.0.1",
57
- "commander": "^12.1.0",
58
- "node-api-dotnet": "^0.9.19",
59
- "resedit": "^2.0.2"
53
+ "node-api-dotnet": "^0.9.19"
60
54
  }
61
55
  }
package/bin/ewvjs-cli.js DELETED
@@ -1,318 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { Command } = require('commander');
4
- const path = require('path');
5
- const fs = require('fs');
6
- const packageApp = require('../lib/packager');
7
- const { setIcon } = require('../lib/icon');
8
-
9
- const program = new Command();
10
-
11
- program
12
- .name('ewvjs')
13
- .description('CLI tool for packaging ewvjs applications')
14
- .version(require('../package.json').version);
15
-
16
- program
17
- .command('package')
18
- .description('Package your ewvjs application into a standalone executable')
19
- .argument('<entry>', 'Entry point JavaScript file (e.g., app.js)')
20
- .option('-o, --output <name>', 'Output executable name', 'app')
21
- .option('-a, --assets <dir>', 'Assets directory to include (e.g., ./assets)', './assets')
22
- .option('-i, --icon <file>', 'Application icon (.ico file)')
23
- .option('-n, --name <name>', 'Application name', 'My App')
24
- .option('-t, --target <target>', 'Target platform', 'node18-win-x64')
25
- .option('-m, --modules <modules>', 'Additional node modules to bundle (comma-separated, e.g., "axios,lodash")')
26
- .option('--compress', 'Compress the executable with UPX', false)
27
- .option('--no-native', 'Skip bundling native DLLs (use if already included)')
28
- .action(async (entry, options) => {
29
- try {
30
- console.log('📦 Packaging ewvjs application...\n');
31
-
32
- // Validate entry file exists
33
- const entryPath = path.resolve(process.cwd(), entry);
34
- if (!fs.existsSync(entryPath)) {
35
- console.error(`❌ Error: Entry file not found: ${entry}`);
36
- process.exit(1);
37
- }
38
-
39
- // Validate icon if provided
40
- if (options.icon) {
41
- const iconPath = path.resolve(process.cwd(), options.icon);
42
- if (!fs.existsSync(iconPath)) {
43
- console.error(`❌ Error: Icon file not found: ${options.icon}`);
44
- process.exit(1);
45
- }
46
- if (!iconPath.endsWith('.ico')) {
47
- console.error('❌ Error: Icon must be a .ico file');
48
- process.exit(1);
49
- }
50
- }
51
-
52
- // Parse additional modules if provided
53
- const additionalModules = options.modules
54
- ? options.modules.split(',').map(m => m.trim()).filter(m => m)
55
- : [];
56
-
57
- const config = {
58
- entry: entryPath,
59
- output: options.output,
60
- assets: options.assets ? path.resolve(process.cwd(), options.assets) : null,
61
- icon: options.icon ? path.resolve(process.cwd(), options.icon) : null,
62
- name: options.name,
63
- target: options.target,
64
- compress: options.compress,
65
- includeNative: options.native,
66
- additionalModules: additionalModules
67
- };
68
-
69
- await packageApp(config);
70
-
71
- console.log('\n✅ Packaging complete!');
72
- console.log(`📁 Output: ${path.join(process.cwd(), 'dist', options.output + '.exe')}`);
73
-
74
- } catch (error) {
75
- console.error('\n❌ Packaging failed:', error.message);
76
- process.exit(1);
77
- }
78
- });
79
-
80
- program
81
- .command('init')
82
- .description('Initialize a new ewvjs project')
83
- .argument('[name]', 'Project name', 'my-ewvjs-app')
84
- .action((name) => {
85
- const projectDir = path.join(process.cwd(), name);
86
-
87
- if (fs.existsSync(projectDir)) {
88
- console.error(`❌ Error: Directory ${name} already exists`);
89
- process.exit(1);
90
- }
91
-
92
- console.log(`📂 Creating new ewvjs project: ${name}\n`);
93
-
94
- // Create directory structure
95
- fs.mkdirSync(projectDir, { recursive: true });
96
- fs.mkdirSync(path.join(projectDir, 'assets'), { recursive: true });
97
-
98
- // Create package.json
99
- const packageJson = {
100
- name: name,
101
- version: '1.0.0',
102
- description: 'My ewvjs application',
103
- main: 'app.js',
104
- scripts: {
105
- start: 'node app.js',
106
- package: 'ewvjs package app.js -o myapp -n "My App"'
107
- },
108
- dependencies: {
109
- ewvjs: '^1.0.0'
110
- }
111
- };
112
-
113
- fs.writeFileSync(
114
- path.join(projectDir, 'package.json'),
115
- JSON.stringify(packageJson, null, 2)
116
- );
117
-
118
- // Create sample app.js
119
- const appJs = `const { create_window, start, expose } = require('ewvjs');
120
-
121
- expose('greet', (name) => {
122
- return \`Hello, \${name}! This is from Node.js 🚀\`;
123
- });
124
-
125
- expose('getSystemInfo', () => {
126
- return {
127
- platform: process.platform,
128
- arch: process.arch,
129
- nodeVersion: process.version,
130
- uptime: process.uptime()
131
- };
132
- });
133
-
134
- // Create main window
135
- const window = create_window('Hello ewvjs', \`
136
- <!DOCTYPE html>
137
- <html>
138
- <head>
139
- <meta charset="UTF-8">
140
- <title>Hello ewvjs</title>
141
- <style>
142
- * {
143
- margin: 0;
144
- padding: 0;
145
- box-sizing: border-box;
146
- }
147
-
148
- body {
149
- font-family: sans-serif;
150
- color: white;
151
- display: flex;
152
- align-items: center;
153
- justify-content: center;
154
- min-height: 100vh;
155
- padding: 20px;
156
- }
157
-
158
- .container {
159
- text-align: center;
160
- max-width: 600px;
161
- }
162
-
163
- h1 {
164
- font-size: 3em;
165
- margin-bottom: 20px;
166
- text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
167
- }
168
-
169
- p {
170
- font-size: 1.2em;
171
- margin-bottom: 30px;
172
- opacity: 0.9;
173
- }
174
-
175
- input {
176
- width: 100%;
177
- padding: 15px;
178
- font-size: 16px;
179
- border: none;
180
- margin-bottom: 15px;
181
- background: rgba(255, 255, 255, 0.9);
182
- color: #333;
183
- }
184
-
185
- button {
186
- background: white;
187
- color: #000000;
188
- border: none;
189
- padding: 15px 30px;
190
- font-size: 18px;
191
- font-weight: bold;
192
- cursor: pointer;
193
- transition: all 0.3s ease;
194
- margin: 5px;
195
- }
196
-
197
- button:hover {
198
- transform: translateY(-2px);
199
- box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
200
- }
201
-
202
- button:active {
203
- transform: translateY(0);
204
- }
205
-
206
- .result {
207
- margin-top: 20px;
208
- padding: 20px;
209
- background: rgba(255, 255, 255, 0.15);
210
- min-height: 60px;
211
- display: flex;
212
- align-items: center;
213
- justify-content: center;
214
- }
215
-
216
- .info {
217
- text-align: left;
218
- font-size: 0.9em;
219
- line-height: 1.6;
220
- }
221
- </style>
222
- </head>
223
- <body>
224
- <div class="container">
225
- <h1>🚀 Hello ewvjs!</h1>
226
- <p>A lightweight WebView2 application for Windows</p>
227
-
228
- <input type="text" id="nameInput" placeholder="Enter your name..." value="World">
229
-
230
- <div>
231
- <button onclick="sayHello()">Greet Me</button>
232
- <button onclick="showSystemInfo()">System Info</button>
233
- </div>
234
-
235
- <div class="result" id="result">
236
- Click a button to see the result...
237
- </div>
238
- </div>
239
-
240
- <script>
241
- async function sayHello() {
242
- const name = document.getElementById('nameInput').value || 'World';
243
- const result = await window.ewvjs.api.greet(name);
244
- document.getElementById('result').innerHTML = '<strong>' + result + '</strong>';
245
- }
246
-
247
- async function showSystemInfo() {
248
- const info = await window.ewvjs.api.getSystemInfo();
249
- document.getElementById('result').innerHTML =
250
- '<div class="info">' +
251
- '<strong>System Information:</strong><br>' +
252
- 'Platform: ' + info.platform + '<br>' +
253
- 'Architecture: ' + info.arch + '<br>' +
254
- 'Node.js: ' + info.nodeVersion + '<br>' +
255
- 'Uptime: ' + Math.floor(info.uptime) + ' seconds' +
256
- '</div>';
257
- }
258
- </script>
259
- </body>
260
- </html>
261
- \`, {
262
- width: 800,
263
- height: 600,
264
- vibrancy: true,
265
- debug: true
266
- });
267
-
268
- window.run();
269
-
270
- // Start the event loop
271
- start();
272
- `;
273
-
274
- fs.writeFileSync(path.join(projectDir, 'app.js'), appJs);
275
-
276
- // Create README
277
- const readme = `# ${name}
278
-
279
- A ewvjs application.
280
-
281
- ## Getting Started
282
-
283
- 1. Install dependencies:
284
- \`\`\`bash
285
- npm install
286
- \`\`\`
287
-
288
- 2. Run the application:
289
- \`\`\`bash
290
- npm start
291
- \`\`\`
292
-
293
- 3. Package the application:
294
- \`\`\`bash
295
- npm run package
296
- \`\`\`
297
-
298
- ## Project Structure
299
-
300
- - \`app.js\` - Main application entry point
301
- - \`assets/\` - Static assets (images, fonts, etc.)
302
- - \`package.json\` - Project configuration
303
-
304
- ## Documentation
305
-
306
- Visit [ewvjs documentation](https://github.com/your-repo/ewvjs) for more information.
307
- `;
308
-
309
- fs.writeFileSync(path.join(projectDir, 'README.md'), readme);
310
-
311
- console.log('✅ Project created successfully!\n');
312
- console.log('Next steps:');
313
- console.log(` cd ${name}`);
314
- console.log(' npm install');
315
- console.log(' npm start\n');
316
- });
317
-
318
- program.parse();
package/lib/assets.js DELETED
@@ -1,129 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const archiver = require('archiver');
4
-
5
- /**
6
- * Bundle assets directory
7
- * @param {string} assetsDir - Source assets directory
8
- * @param {string} outputDir - Destination directory
9
- * @returns {Promise<void>}
10
- */
11
- async function bundleAssets(assetsDir, outputDir) {
12
- if (!fs.existsSync(assetsDir)) {
13
- throw new Error(`Assets directory not found: ${assetsDir}`);
14
- }
15
-
16
- // Create output directory
17
- if (!fs.existsSync(outputDir)) {
18
- fs.mkdirSync(outputDir, { recursive: true });
19
- }
20
-
21
- // Copy assets directory
22
- await copyDirectory(assetsDir, outputDir);
23
-
24
- // Get stats
25
- const stats = getDirectoryStats(outputDir);
26
- console.log(` Copied ${stats.files} files (${formatBytes(stats.size)})`);
27
- }
28
-
29
- /**
30
- * Copy directory recursively
31
- * @param {string} src - Source directory
32
- * @param {string} dest - Destination directory
33
- */
34
- async function copyDirectory(src, dest) {
35
- if (!fs.existsSync(dest)) {
36
- fs.mkdirSync(dest, { recursive: true });
37
- }
38
-
39
- const entries = fs.readdirSync(src, { withFileTypes: true });
40
-
41
- for (const entry of entries) {
42
- const srcPath = path.join(src, entry.name);
43
- const destPath = path.join(dest, entry.name);
44
-
45
- if (entry.isDirectory()) {
46
- await copyDirectory(srcPath, destPath);
47
- } else {
48
- fs.copyFileSync(srcPath, destPath);
49
- }
50
- }
51
- }
52
-
53
- /**
54
- * Create a zip archive of assets (alternative to direct copy)
55
- * @param {string} assetsDir - Source assets directory
56
- * @param {string} outputPath - Output zip file path
57
- * @returns {Promise<void>}
58
- */
59
- async function createAssetsArchive(assetsDir, outputPath) {
60
- return new Promise((resolve, reject) => {
61
- const output = fs.createWriteStream(outputPath);
62
- const archive = archiver('zip', {
63
- zlib: { level: 9 } // Maximum compression
64
- });
65
-
66
- output.on('close', () => {
67
- console.log(` Created archive: ${formatBytes(archive.pointer())}`);
68
- resolve();
69
- });
70
-
71
- archive.on('error', (err) => {
72
- reject(err);
73
- });
74
-
75
- archive.pipe(output);
76
- archive.directory(assetsDir, false);
77
- archive.finalize();
78
- });
79
- }
80
-
81
- /**
82
- * Get directory statistics
83
- * @param {string} dirPath - Directory path
84
- * @returns {{files: number, size: number}}
85
- */
86
- function getDirectoryStats(dirPath) {
87
- let fileCount = 0;
88
- let totalSize = 0;
89
-
90
- function traverse(dir) {
91
- const entries = fs.readdirSync(dir, { withFileTypes: true });
92
-
93
- for (const entry of entries) {
94
- const fullPath = path.join(dir, entry.name);
95
-
96
- if (entry.isDirectory()) {
97
- traverse(fullPath);
98
- } else {
99
- fileCount++;
100
- totalSize += fs.statSync(fullPath).size;
101
- }
102
- }
103
- }
104
-
105
- traverse(dirPath);
106
- return { files: fileCount, size: totalSize };
107
- }
108
-
109
- /**
110
- * Format bytes to human-readable string
111
- * @param {number} bytes - Number of bytes
112
- * @returns {string}
113
- */
114
- function formatBytes(bytes) {
115
- if (bytes === 0) return '0 Bytes';
116
-
117
- const k = 1024;
118
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
119
- const i = Math.floor(Math.log(bytes) / Math.log(k));
120
-
121
- return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
122
- }
123
-
124
- module.exports = {
125
- bundleAssets,
126
- createAssetsArchive,
127
- copyDirectory,
128
- formatBytes
129
- };
package/lib/icon.js DELETED
@@ -1,150 +0,0 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const NtExecutable = require('resedit').NtExecutable;
4
- const NtExecutableResource = require('resedit').NtExecutableResource;
5
- const Resource = require('resedit').Resource;
6
-
7
- /**
8
- * Set icon and metadata for Windows executable
9
- * @param {string} exePath - Path to the executable
10
- * @param {string} iconPath - Path to the .ico file
11
- * @param {string} appName - Application name
12
- * @param {Object} options - Additional metadata options
13
- * @returns {Promise<void>}
14
- */
15
- async function setIcon(exePath, iconPath, appName, options = {}) {
16
- try {
17
- // Read the executable
18
- const exeBuffer = fs.readFileSync(exePath);
19
- const exe = NtExecutable.from(exeBuffer);
20
- const res = NtExecutableResource.from(exe);
21
-
22
- // Read the icon file
23
- if (iconPath && fs.existsSync(iconPath)) {
24
- const iconBuffer = fs.readFileSync(iconPath);
25
-
26
- // Parse icon data
27
- const iconFile = Resource.IconFile.from(iconBuffer);
28
-
29
- // Replace icon in executable
30
- Resource.IconGroupEntry.replaceIconsForResource(
31
- res.entries,
32
- 1, // Icon group ID
33
- 1033, // Language (English - United States)
34
- iconFile.icons.map((icon) => icon.data)
35
- );
36
-
37
- console.log(` Applied icon: ${path.basename(iconPath)}`);
38
- }
39
-
40
- // Set version info
41
- const viList = Resource.VersionInfo.fromEntries(res.entries);
42
- const vi = viList[0] || Resource.VersionInfo.createEmpty();
43
-
44
- const {
45
- version = '1.0.0.0',
46
- companyName = '',
47
- fileDescription = appName,
48
- copyright = `Copyright © ${new Date().getFullYear()}`,
49
- productName = appName,
50
- internalName = appName.replace(/\s+/g, ''),
51
- } = options;
52
-
53
- // Parse version string
54
- const [major = 1, minor = 0, patch = 0, build = 0] = version.split('.').map(Number);
55
-
56
- // Set version numbers
57
- vi.setFileVersion(major, minor, patch, build, 1033);
58
- vi.setProductVersion(major, minor, patch, build, 1033);
59
-
60
- // Set string values
61
- vi.setStringValues(
62
- { lang: 1033, codepage: 1200 },
63
- {
64
- ProductName: productName,
65
- FileDescription: fileDescription,
66
- CompanyName: companyName,
67
- LegalCopyright: copyright,
68
- FileVersion: version,
69
- ProductVersion: version,
70
- InternalName: internalName,
71
- OriginalFilename: path.basename(exePath)
72
- }
73
- );
74
-
75
- vi.outputToResourceEntries(res.entries);
76
-
77
- // Write back to executable
78
- res.outputResource(exe);
79
- const newBuffer = exe.generate();
80
- fs.writeFileSync(exePath, Buffer.from(newBuffer));
81
-
82
- console.log(` Applied metadata: ${appName} v${version}`);
83
- } catch (error) {
84
- console.warn(` ⚠ Warning: Could not set icon/metadata: ${error.message}`);
85
- console.warn(' The executable was created but icon/metadata may be missing.');
86
- }
87
- }
88
-
89
- /**
90
- * Validate icon file
91
- * @param {string} iconPath - Path to icon file
92
- * @returns {boolean}
93
- */
94
- function validateIcon(iconPath) {
95
- if (!fs.existsSync(iconPath)) {
96
- throw new Error(`Icon file not found: ${iconPath}`);
97
- }
98
-
99
- if (!iconPath.toLowerCase().endsWith('.ico')) {
100
- throw new Error('Icon file must be in .ico format');
101
- }
102
-
103
- const stats = fs.statSync(iconPath);
104
- if (stats.size === 0) {
105
- throw new Error('Icon file is empty');
106
- }
107
-
108
- if (stats.size > 1024 * 1024) {
109
- console.warn('Warning: Icon file is larger than 1MB, consider optimizing it');
110
- }
111
-
112
- return true;
113
- }
114
-
115
- /**
116
- * Extract icon from executable
117
- * @param {string} exePath - Path to executable
118
- * @param {string} outputPath - Output path for icon file
119
- * @returns {Promise<void>}
120
- */
121
- async function extractIcon(exePath, outputPath) {
122
- try {
123
- const exeBuffer = fs.readFileSync(exePath);
124
- const exe = NtExecutable.from(exeBuffer);
125
- const res = NtExecutableResource.from(exe);
126
-
127
- // Find icon group
128
- const iconGroups = Resource.IconGroupEntry.fromEntries(res.entries);
129
-
130
- if (iconGroups.length === 0) {
131
- throw new Error('No icon found in executable');
132
- }
133
-
134
- // Get first icon group
135
- const iconGroup = iconGroups[0];
136
- const iconFile = Resource.IconFile.from(iconGroup, res.entries);
137
-
138
- // Write icon file
139
- fs.writeFileSync(outputPath, Buffer.from(iconFile.data));
140
- console.log(`Extracted icon to: ${outputPath}`);
141
- } catch (error) {
142
- throw new Error(`Failed to extract icon: ${error.message}`);
143
- }
144
- }
145
-
146
- module.exports = {
147
- setIcon,
148
- validateIcon,
149
- extractIcon
150
- };
package/lib/packager.js DELETED
@@ -1,347 +0,0 @@
1
- const { spawn } = require('child_process');
2
- const path = require('path');
3
- const fs = require('fs');
4
- const { bundleAssets } = require('./assets');
5
- const { setIcon } = require('./icon');
6
-
7
- /**
8
- * Convert console application to GUI application (hide console window)
9
- * @param {string} exePath - Path to the executable
10
- */
11
- function convertToGuiApp(exePath) {
12
- try {
13
- // Read the executable as a buffer
14
- const exeBuffer = fs.readFileSync(exePath);
15
-
16
- // Get PE signature offset (at 0x3C in DOS header)
17
- const peOffset = exeBuffer.readUInt32LE(0x3C);
18
-
19
- // PE signature is 4 bytes ("PE\0\0")
20
- // COFF header is 20 bytes
21
- // Optional header starts at peOffset + 24
22
- // Subsystem is at offset 68 (0x44) in optional header
23
- const subsystemOffset = peOffset + 24 + 68;
24
-
25
- // Read current subsystem value
26
- const currentSubsystem = exeBuffer.readUInt16LE(subsystemOffset);
27
- console.log(` Current subsystem: ${currentSubsystem} (3=CONSOLE, 2=GUI)`);
28
-
29
- // Set to IMAGE_SUBSYSTEM_WINDOWS_GUI (2) instead of CONSOLE (3)
30
- if (currentSubsystem === 3) {
31
- exeBuffer.writeUInt16LE(2, subsystemOffset);
32
- console.log(` Changed subsystem to GUI (2)`);
33
-
34
- // Write the modified executable
35
- fs.writeFileSync(exePath, exeBuffer);
36
- }
37
- } catch (error) {
38
- console.warn(` ⚠ Warning: Could not convert to GUI app: ${error.message}`);
39
- console.warn(' The executable will show a console window.');
40
- }
41
- }
42
-
43
- /**
44
- * Run pkg command using spawn
45
- * @param {string[]} args - Arguments for pkg
46
- * @returns {Promise<void>}
47
- */
48
- function runPkg(args) {
49
- return new Promise((resolve, reject) => {
50
- // Try to find pkg binary in node_modules
51
- let pkgBin;
52
- try {
53
- const pkgPackageJson = require.resolve('@yao-pkg/pkg/package.json');
54
- const pkgDir = path.dirname(pkgPackageJson);
55
-
56
- // Check for different possible bin locations
57
- const possibleBins = [
58
- path.join(pkgDir, 'lib-es5', 'bin.js'),
59
- path.join(pkgDir, 'lib', 'bin.js'),
60
- path.join(pkgDir, 'bin', 'pkg.js')
61
- ];
62
-
63
- for (const bin of possibleBins) {
64
- if (fs.existsSync(bin)) {
65
- pkgBin = bin;
66
- break;
67
- }
68
- }
69
-
70
- if (!pkgBin) {
71
- throw new Error('pkg binary not found');
72
- }
73
- } catch (error) {
74
- reject(new Error(`Cannot find @yao-pkg/pkg installation: ${error.message}`));
75
- return;
76
- }
77
-
78
- console.log(` Running: node ${pkgBin} ${args.join(' ')}`);
79
-
80
- // Spawn pkg process
81
- let stdout = '';
82
- let stderr = '';
83
-
84
- const pkgProcess = spawn(process.execPath, [pkgBin, ...args], {
85
- stdio: ['inherit', 'pipe', 'pipe'],
86
- shell: false
87
- });
88
-
89
- pkgProcess.stdout.on('data', (data) => {
90
- const output = data.toString();
91
- stdout += output;
92
- process.stdout.write(output);
93
- });
94
-
95
- pkgProcess.stderr.on('data', (data) => {
96
- const output = data.toString();
97
- stderr += output;
98
- process.stderr.write(output);
99
- });
100
-
101
- pkgProcess.on('close', (code) => {
102
- if (code === 0) {
103
- resolve();
104
- } else {
105
- const errorMsg = stderr || stdout || `pkg exited with code ${code}`;
106
- reject(new Error(errorMsg));
107
- }
108
- });
109
-
110
- pkgProcess.on('error', (error) => {
111
- reject(error);
112
- });
113
- });
114
- }
115
-
116
- /**
117
- * Package an ewvjs application into a standalone executable
118
- * @param {Object} config - Packaging configuration
119
- * @param {string} config.entry - Entry point file path
120
- * @param {string} config.output - Output executable name (without .exe)
121
- * @param {string} config.assets - Assets directory path
122
- * @param {string} config.icon - Icon file path (.ico)
123
- * @param {string} config.name - Application name
124
- * @param {string} config.target - Target platform (e.g., node18-win-x64)
125
- * @param {boolean} config.compress - Whether to compress with UPX
126
- * @param {boolean} config.includeNative - Whether to include native DLLs
127
- * @param {string[]} config.additionalModules - Additional node modules to bundle
128
- */
129
- async function packageApp(config) {
130
- const {
131
- entry,
132
- output,
133
- assets,
134
- icon,
135
- name,
136
- target,
137
- compress,
138
- includeNative = true,
139
- additionalModules = []
140
- } = config;
141
-
142
- const outputDir = path.join(process.cwd(), 'dist');
143
- const outputPath = path.join(outputDir, `${output}.exe`);
144
-
145
- // Create output directory
146
- if (!fs.existsSync(outputDir)) {
147
- fs.mkdirSync(outputDir, { recursive: true });
148
- }
149
-
150
- console.log('📝 Configuration:');
151
- console.log(` Entry: ${entry}`);
152
- console.log(` Output: ${outputPath}`);
153
- console.log(` Target: ${target}`);
154
- console.log(` Compress: ${compress ? 'Yes' : 'No'}`);
155
- if (assets && fs.existsSync(assets)) {
156
- console.log(` Assets: ${assets}`);
157
- }
158
- if (icon) {
159
- console.log(` Icon: ${icon}`);
160
- }
161
- if (additionalModules.length > 0) {
162
- console.log(` Additional Modules: ${additionalModules.join(', ')}`);
163
- }
164
- console.log('');
165
-
166
- // Step 1: Package with @yao-pkg/pkg
167
- console.log('🔨 Step 1: Creating executable with pkg...');
168
-
169
- const pkgArgs = [
170
- entry,
171
- '--target', target,
172
- '--output', outputPath,
173
- '--public', // Faster, includes sources
174
- '--no-bytecode' // Skip bytecode generation for faster packaging
175
- ];
176
-
177
- if (compress) {
178
- pkgArgs.push('--compress', 'GZip');
179
- }
180
-
181
- // Add pkg configuration for native modules
182
- const pkgConfig = {
183
- assets: []
184
- };
185
-
186
- // Include native DLLs if requested
187
- if (includeNative) {
188
- const ewvjsPath = require.resolve('ewvjs');
189
- const ewvjsRoot = path.dirname(path.dirname(ewvjsPath));
190
- const nativePath = path.join(ewvjsRoot, 'native');
191
-
192
- if (fs.existsSync(nativePath)) {
193
- console.log(' Including native DLLs from ewvjs...');
194
- pkgConfig.assets.push(`${nativePath}/**/*`);
195
- }
196
- }
197
-
198
- // Write temporary pkg config
199
- const pkgConfigPath = path.join(process.cwd(), '.pkg-config.json');
200
- fs.writeFileSync(pkgConfigPath, JSON.stringify(pkgConfig, null, 2));
201
-
202
- try {
203
- // Execute pkg using spawn
204
- await runPkg(pkgArgs);
205
- console.log(' ✓ Executable created');
206
-
207
- // Convert to GUI application (hide console window)
208
- console.log(' Converting to GUI application...');
209
- convertToGuiApp(outputPath);
210
- console.log(' ✓ Converted to GUI app (no console window)');
211
-
212
- // Clean up temp config
213
- if (fs.existsSync(pkgConfigPath)) {
214
- fs.unlinkSync(pkgConfigPath);
215
- }
216
- } catch (error) {
217
- // Clean up temp config on error
218
- if (fs.existsSync(pkgConfigPath)) {
219
- fs.unlinkSync(pkgConfigPath);
220
- }
221
- throw new Error(`pkg failed: ${error.message}`);
222
- }
223
-
224
- // Step 2: Copy native DLLs next to executable
225
- if (includeNative) {
226
- console.log('\n🔧 Step 2: Copying native dependencies...');
227
- const ewvjsPath = require.resolve('ewvjs');
228
- const ewvjsRoot = path.dirname(path.dirname(ewvjsPath));
229
- const nativePath = path.join(ewvjsRoot, 'native');
230
-
231
- if (fs.existsSync(nativePath)) {
232
- const targetNativePath = path.join(outputDir, 'native');
233
-
234
- // Copy directory recursively
235
- copyRecursive(nativePath, targetNativePath);
236
- console.log(` ✓ Native DLLs copied to ${targetNativePath}`);
237
-
238
- // Copy additional node modules if specified
239
- if (additionalModules.length > 0) {
240
- console.log('\n 📦 Copying additional node modules...');
241
- const targetNodeModulesPath = path.join(targetNativePath, 'node_modules');
242
- const copiedModules = new Set();
243
-
244
- /**
245
- * Recursively copy module and its dependencies
246
- */
247
- function copyModuleWithDependencies(moduleName, depth = 0) {
248
- const indent = ' ' + ' '.repeat(depth);
249
-
250
- // Avoid copying the same module twice
251
- if (copiedModules.has(moduleName)) {
252
- return;
253
- }
254
-
255
- try {
256
- // Try to resolve the module from the current project
257
- const modulePath = require.resolve(moduleName + '/package.json', {
258
- paths: [process.cwd()]
259
- });
260
- const moduleRoot = path.dirname(modulePath);
261
- const moduleDestPath = path.join(targetNodeModulesPath, moduleName);
262
-
263
- // Mark as copied before processing to avoid circular dependencies
264
- copiedModules.add(moduleName);
265
-
266
- // Copy the module
267
- copyRecursive(moduleRoot, moduleDestPath);
268
- console.log(`${indent}✓ Copied ${moduleName}`);
269
-
270
- // Read package.json to get dependencies
271
- const packageJsonPath = path.join(moduleRoot, 'package.json');
272
- if (fs.existsSync(packageJsonPath)) {
273
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
274
- const dependencies = {
275
- ...packageJson.dependencies,
276
- ...packageJson.optionalDependencies
277
- };
278
-
279
- // Recursively copy dependencies
280
- if (dependencies && Object.keys(dependencies).length > 0) {
281
- for (const depName of Object.keys(dependencies)) {
282
- copyModuleWithDependencies(depName, depth + 1);
283
- }
284
- }
285
- }
286
- } catch (error) {
287
- if (depth === 0) {
288
- // Only warn for top-level modules
289
- console.warn(`${indent}⚠ Warning: Could not find module "${moduleName}": ${error.message}`);
290
- }
291
- // Skip missing optional dependencies silently
292
- }
293
- }
294
-
295
- // Copy each requested module with its dependencies
296
- for (const moduleName of additionalModules) {
297
- copyModuleWithDependencies(moduleName, 0);
298
- }
299
-
300
- console.log(` Total modules copied: ${copiedModules.size}`);
301
- }
302
- } else {
303
- console.warn(' ⚠ Warning: Native DLLs not found in ewvjs installation');
304
- }
305
- }
306
-
307
- // Step 3: Bundle assets if provided
308
- if (assets && fs.existsSync(assets)) {
309
- console.log('\n📦 Step 3: Bundling assets...');
310
- const assetsOutput = path.join(outputDir, 'assets');
311
- await bundleAssets(assets, assetsOutput);
312
- console.log(` ✓ Assets bundled to ${assetsOutput}`);
313
- }
314
-
315
- // Step 4: Set icon if provided
316
- if (icon && fs.existsSync(icon)) {
317
- console.log('\n🎨 Step 4: Setting application icon...');
318
- await setIcon(outputPath, icon, name);
319
- console.log(' ✓ Icon applied');
320
- }
321
-
322
- return outputPath;
323
- }
324
-
325
- /**
326
- * Copy directory recursively
327
- */
328
- function copyRecursive(src, dest) {
329
- if (!fs.existsSync(dest)) {
330
- fs.mkdirSync(dest, { recursive: true });
331
- }
332
-
333
- const entries = fs.readdirSync(src, { withFileTypes: true });
334
-
335
- for (const entry of entries) {
336
- const srcPath = path.join(src, entry.name);
337
- const destPath = path.join(dest, entry.name);
338
-
339
- if (entry.isDirectory()) {
340
- copyRecursive(srcPath, destPath);
341
- } else {
342
- fs.copyFileSync(srcPath, destPath);
343
- }
344
- }
345
- }
346
-
347
- module.exports = packageApp;