ewvjs 1.0.12 → 1.0.14

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/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ export { Window } from './window.js';
4
4
  import { WebView } from './webview.js';
5
5
  declare const ewvjs: WebView;
6
6
  export default ewvjs;
7
- export declare const create_window: (title: string, url_or_html?: string, options?: Partial<import("./types.js").WindowOptions>) => import("./window.js").Window;
7
+ export declare const create_window: (title: string, url_or_html?: string, options?: Partial<import("./types.js").WindowOptions>) => Promise<import("./window.js").Window>;
8
8
  export declare const start: () => Promise<void>;
9
9
  export declare const expose: (name: string, func: Function) => void;
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA+BA,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,QAAA,MAAM,KAAK,SAAgB,CAAC;AAC5B,eAAe,KAAK,CAAC;AAGrB,eAAO,MAAM,aAAa,8HAAkC,CAAC;AAC7D,eAAO,MAAM,KAAK,qBAA0B,CAAC;AAC7C,eAAO,MAAM,MAAM,wCAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA+BA,cAAc,YAAY,CAAC;AAG3B,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,QAAA,MAAM,KAAK,SAAgB,CAAC;AAC5B,eAAe,KAAK,CAAC;AAGrB,eAAO,MAAM,aAAa,uIAAkC,CAAC;AAC7D,eAAO,MAAM,KAAK,qBAA0B,CAAC;AAC7C,eAAO,MAAM,MAAM,wCAA2B,CAAC"}
package/dist/js/api.js CHANGED
@@ -6,15 +6,113 @@ window.ewvjs = {
6
6
  _returnValuesCallbacks: {},
7
7
 
8
8
  _hookDrag: function () {
9
+ var lastClickTime = 0;
9
10
  window.addEventListener('mousedown', function (e) {
10
11
  if (e.target.classList.contains('ewvjs-drag-region') || e.target.closest('.ewvjs-drag-region')) {
11
12
  if (e.button === 0) { // Left click
12
- window.chrome.webview.postMessage("drag");
13
+ var now = e.timeStamp;
14
+ if (now - lastClickTime < 500) {
15
+ if (window.__isWindowMaximized) {
16
+ window.restore();
17
+ } else {
18
+ window.maximize();
19
+ }
20
+ lastClickTime = 0;
21
+ } else {
22
+ window.chrome.webview.postMessage("drag");
23
+ lastClickTime = now;
24
+ }
13
25
  }
14
26
  }
15
27
  });
16
28
  },
17
29
 
30
+ _hookResize: function () {
31
+ var resizeBorder = 8;
32
+ var styleEl = null;
33
+
34
+ function getDirection(e) {
35
+ if (!window.__isTitleBarDisabled || window.__isWindowMaximized) return null;
36
+
37
+ var x = e.clientX;
38
+ var y = e.clientY;
39
+ var w = window.innerWidth;
40
+ var h = window.innerHeight;
41
+
42
+ var onLeft = x < resizeBorder;
43
+ var onRight = x > w - resizeBorder;
44
+ var onTop = y < resizeBorder;
45
+ var onBottom = y > h - resizeBorder;
46
+
47
+ if (onTop && onLeft) return "topleft";
48
+ if (onTop && onRight) return "topright";
49
+ if (onBottom && onLeft) return "bottomleft";
50
+ if (onBottom && onRight) return "bottomright";
51
+ if (onTop) return "top";
52
+ if (onBottom) return "bottom";
53
+ if (onLeft) return "left";
54
+ if (onRight) return "right";
55
+ return null;
56
+ }
57
+
58
+ function setOverrideCursor(cursor) {
59
+ if (!styleEl) {
60
+ styleEl = document.getElementById("ewvjs-cursor-override-style");
61
+ if (!styleEl) {
62
+ styleEl = document.createElement("style");
63
+ styleEl.id = "ewvjs-cursor-override-style";
64
+ styleEl.type = "text/css";
65
+ (document.head || document.documentElement).appendChild(styleEl);
66
+ }
67
+ }
68
+ var css = "* { cursor: " + cursor + " !important; }";
69
+ if (styleEl.textContent !== css) {
70
+ styleEl.textContent = css;
71
+ }
72
+ if (document.documentElement.style.getPropertyValue("cursor") !== cursor) {
73
+ document.documentElement.style.setProperty("cursor", cursor, "important");
74
+ }
75
+ }
76
+
77
+ function clearOverrideCursor() {
78
+ if (styleEl) {
79
+ styleEl.textContent = "";
80
+ } else {
81
+ var el = document.getElementById("ewvjs-cursor-override-style");
82
+ if (el) {
83
+ el.textContent = "";
84
+ }
85
+ }
86
+ if (document.documentElement.style.getPropertyValue("cursor")) {
87
+ document.documentElement.style.removeProperty("cursor");
88
+ }
89
+ }
90
+
91
+ window.addEventListener('mousemove', function (e) {
92
+ var dir = getDirection(e);
93
+ if (dir) {
94
+ var cursor = "";
95
+ if (dir === "top" || dir === "bottom") cursor = "ns-resize";
96
+ else if (dir === "left" || dir === "right") cursor = "ew-resize";
97
+ else if (dir === "topleft" || dir === "bottomright") cursor = "nwse-resize";
98
+ else if (dir === "topright" || dir === "bottomleft") cursor = "nesw-resize";
99
+
100
+ setOverrideCursor(cursor);
101
+ } else {
102
+ clearOverrideCursor();
103
+ }
104
+ }, true);
105
+
106
+ window.addEventListener('mousedown', function (e) {
107
+ var dir = getDirection(e);
108
+ if (dir && e.button === 0) {
109
+ e.preventDefault();
110
+ e.stopPropagation();
111
+ window.chrome.webview.postMessage("resize:" + dir);
112
+ }
113
+ }, true);
114
+ },
115
+
18
116
  _createApi: function (funcList) {
19
117
  function sanitize_params(params) {
20
118
  var reservedWords = [
@@ -263,6 +361,7 @@ window.ewvjs._hookConsole = function () {
263
361
 
264
362
  window.ewvjs._hookConsole();
265
363
  window.ewvjs._hookDrag();
364
+ window.ewvjs._hookResize();
266
365
 
267
366
  // Add window state methods directly to window object
268
367
  window.close = function () {
@@ -1 +1 @@
1
- {"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../../src/platforms/windows.ts"],"names":[],"mappings":"AAUA,qBAAa,eAAe;;IAKxB,YAAY,CAAC,OAAO,EAAE,GAAG;CA6G5B"}
1
+ {"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../../src/platforms/windows.ts"],"names":[],"mappings":"AAaA,qBAAa,eAAe;;IAKxB,YAAY,CAAC,OAAO,EAAE,GAAG;CA0G5B"}
@@ -6,6 +6,8 @@ import { createRequire } from 'module';
6
6
  const __filename = fileURLToPath(import.meta.url);
7
7
  const __dirname = path.dirname(__filename);
8
8
  const require = createRequire(import.meta.url);
9
+ // Cache the injected API script so it's only read from disk once
10
+ let _cachedApiScript = null;
9
11
  export class WindowsPlatform {
10
12
  constructor() {
11
13
  }
@@ -56,20 +58,17 @@ export class WindowsPlatform {
56
58
  console.error("Failed to load native module from " + dllDir, e);
57
59
  throw e;
58
60
  }
59
- // Prepare initScript
61
+ // Prepare initScript — read & cache once, then reuse
60
62
  const token = Math.random().toString(36).substring(2, 15);
61
- let apiScript = fs.readFileSync(apiPath, 'utf8');
62
- apiScript = apiScript.replace('%(token)s', token);
63
+ if (_cachedApiScript === null) {
64
+ _cachedApiScript = fs.readFileSync(apiPath, 'utf8');
65
+ }
66
+ const apiScript = _cachedApiScript.replace('%(token)s', token);
63
67
  options.initScript = apiScript;
64
68
  // Handle messages from WebView
65
- // options.onMessage will be called by C#
66
- // We must keep the Node process alive while the window is open, similar to how edge-js performed.
67
- const keepAlive = setInterval(() => { }, 5000);
68
69
  options.onMessage = (message, callback) => {
69
- // Check for close message to clear keepAlive
70
- // Message from C# on close is explicit string: "[\"closed\", \"\"]"
70
+ // Check for close message
71
71
  if (message === '["closed", ""]' || (Array.isArray(message) && message[0] === 'closed')) {
72
- clearInterval(keepAlive);
73
72
  }
74
73
  if (typeof message === 'string') {
75
74
  try {
package/dist/webview.d.ts CHANGED
@@ -12,13 +12,12 @@ export declare class WebView {
12
12
  private _resolveOnce;
13
13
  private _httpServers;
14
14
  constructor();
15
- create_window(title: string, url_or_html?: string, options?: Partial<WindowOptions>): Window;
15
+ create_window(title: string, url_or_html?: string, options?: Partial<WindowOptions>): Promise<Window>;
16
16
  start(): Promise<void>;
17
17
  private _cleanup;
18
18
  expose(name: string, func: Function): void;
19
19
  _handle_message(message: any): Promise<void>;
20
20
  private _getMimeType;
21
- private _findFreePort;
22
21
  private _createHttpServer;
23
22
  private _buildWindowOptions;
24
23
  }
@@ -1 +1 @@
1
- {"version":3,"file":"webview.d.ts","sourceRoot":"","sources":["../src/webview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAW3C,qBAAa,OAAO;IACnB,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;IACtC,OAAO,CAAC,YAAY,CAAuC;;IAU3D,aAAa,CACZ,KAAK,EAAE,MAAM,EACb,WAAW,GAAE,MAAW,EACxB,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,MAAM;IAgBH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,OAAO,CAAC,QAAQ;IAyBhB,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,YAAY;IAKpB,OAAO,CAAC,aAAa;IA8BrB,OAAO,CAAC,iBAAiB;IA6DzB,OAAO,CAAC,mBAAmB;CAuC3B"}
1
+ {"version":3,"file":"webview.d.ts","sourceRoot":"","sources":["../src/webview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAU3C,qBAAa,OAAO;IACnB,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;IACtC,OAAO,CAAC,YAAY,CAAuC;;IAUrD,aAAa,CAClB,KAAK,EAAE,MAAM,EACb,WAAW,GAAE,MAAW,EACxB,OAAO,GAAE,OAAO,CAAC,aAAa,CAAM,GAClC,OAAO,CAAC,MAAM,CAAC;IAgBZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAa5B,OAAO,CAAC,QAAQ;IAyBhB,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,YAAY;IAKpB,OAAO,CAAC,iBAAiB;YAiDX,mBAAmB;CAwCjC"}
package/dist/webview.js CHANGED
@@ -4,7 +4,6 @@ import * as http from "http";
4
4
  import * as fs from "fs";
5
5
  import * as path from "path";
6
6
  import mime from "mime-types";
7
- import { execSync } from "child_process";
8
7
  import { fileURLToPath } from 'url';
9
8
  const __filename = fileURLToPath(import.meta.url);
10
9
  const __dirname = path.dirname(__filename);
@@ -25,8 +24,8 @@ export class WebView {
25
24
  throw new Error("Platform not supported: " + process.platform);
26
25
  }
27
26
  }
28
- create_window(title, url_or_html = "", options = {}) {
29
- const opts = this._buildWindowOptions(title, url_or_html, options);
27
+ async create_window(title, url_or_html = "", options = {}) {
28
+ const opts = await this._buildWindowOptions(title, url_or_html, options);
30
29
  const window = new Window(this.platform, opts, this.exposed_functions);
31
30
  this._windows.add(window);
32
31
  window.closed.then(() => {
@@ -80,59 +79,22 @@ export class WebView {
80
79
  const ext = path.extname(filePath).toLowerCase();
81
80
  return mime.lookup(ext) || "application/octet-stream";
82
81
  }
83
- _findFreePort() {
84
- // Use execSync to find a free port synchronously
85
- const script = `
86
- import net from 'net';
87
- const server = net.createServer();
88
- server.listen(0, 'localhost', () => {
89
- console.log(server.address().port);
90
- server.close();
91
- });
92
- `;
93
- try {
94
- const output = execSync(`node --input-type=module -e "${script.replace(/"/g, '\\"').replace(/\n/g, " ")}"`, {
95
- encoding: "utf-8",
96
- timeout: 5000,
97
- });
98
- const port = parseInt(output.trim(), 10);
99
- if (isNaN(port)) {
100
- throw new Error("Failed to parse port");
101
- }
102
- return port;
103
- }
104
- catch (err) {
105
- // Fallback to port range
106
- return 3000 + Math.floor(Math.random() * 1000);
107
- }
108
- }
109
82
  _createHttpServer(filePath) {
110
- // Check if server already exists for this file
83
+ // Return cached server URL if already serving this file
111
84
  if (this._httpServers.has(filePath)) {
112
- const existingPort = Array.from(this._httpServers.entries()).find(([path, _]) => path === filePath);
113
- if (existingPort) {
114
- const addr = existingPort[1].address();
115
- if (addr && typeof addr === "object") {
116
- return `http://localhost:${addr.port}`;
117
- }
85
+ const existing = this._httpServers.get(filePath);
86
+ const addr = existing.address();
87
+ if (addr && typeof addr === "object") {
88
+ return Promise.resolve(`http://localhost:${addr.port}`);
118
89
  }
119
90
  }
120
- // Resolve relative paths from the main script's directory, not CWD
121
- let absolutePath;
122
- // In ESM, use process.argv[1] to get the main script path
123
- let mainDir;
124
- if (process.argv[1]) {
125
- mainDir = path.dirname(process.argv[1]);
126
- }
127
- else {
128
- mainDir = process.cwd();
129
- }
130
- absolutePath = path.resolve(mainDir, filePath);
131
- console.log(`Serving ${absolutePath} at ${filePath}`);
91
+ // Resolve the file path relative to the main script directory
92
+ const mainDir = process.argv[1]
93
+ ? path.dirname(process.argv[1])
94
+ : process.cwd();
95
+ const absolutePath = path.resolve(mainDir, filePath);
132
96
  const dir = path.dirname(absolutePath);
133
97
  const fileName = path.basename(absolutePath);
134
- // Find a free port first
135
- const port = this._findFreePort();
136
98
  const server = http.createServer((req, res) => {
137
99
  const requestPath = req.url === "/" ? fileName : req.url.substring(1);
138
100
  const fullPath = path.join(dir, requestPath);
@@ -152,12 +114,17 @@ export class WebView {
152
114
  res.end(data);
153
115
  });
154
116
  });
155
- // Use the pre-allocated port
156
- server.listen(port, "localhost");
157
- this._httpServers.set(filePath, server);
158
- return `http://localhost:${port}`;
117
+ // listen(0) asks the OS to assign a free port instantly — no external process needed
118
+ return new Promise((resolve, reject) => {
119
+ server.on("error", reject);
120
+ server.listen(0, "localhost", () => {
121
+ const addr = server.address();
122
+ this._httpServers.set(filePath, server);
123
+ resolve(`http://localhost:${addr.port}`);
124
+ });
125
+ });
159
126
  }
160
- _buildWindowOptions(title, url_or_html, options) {
127
+ async _buildWindowOptions(title, url_or_html, options) {
161
128
  const opts = {
162
129
  title: title,
163
130
  width: options.width || 800,
@@ -181,8 +148,9 @@ export class WebView {
181
148
  opts.url = url_or_html;
182
149
  }
183
150
  else if (url_or_html.toLowerCase().endsWith(".html")) {
184
- // If it's an HTML file path, serve it via HTTP server
185
- opts.url = this._createHttpServer(url_or_html);
151
+ // Serve the HTML file via a local HTTP server;
152
+ // listen(0) lets the OS pick a free port with zero overhead
153
+ opts.url = await this._createHttpServer(url_or_html);
186
154
  }
187
155
  else {
188
156
  opts.html = url_or_html;
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ewvjs",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "Embedded WebView for JavaScript - Edge WebView2 bindings for Node.js",
5
5
  "workspaces": [
6
6
  "packages/*"