nexfep 0.6.1 → 0.7.0
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-CN.md +144 -26
- package/README.md +144 -26
- package/package.json +5 -4
- package/src/Application.d.ts +17 -4
- package/src/Application.js +10 -3
- package/src/{Icon.d.ts → Basics.d.ts} +18 -1
- package/src/{Icon.js → Basics.js} +27 -1
- package/src/Tray.d.ts +1 -1
- package/src/WindowManager.d.ts +14 -8
- package/src/WindowManager.js +53 -12
package/README-CN.md
CHANGED
|
@@ -63,19 +63,22 @@ const app = new Application();
|
|
|
63
63
|
const app = new Application({ WindowsWebview2UserDataFolder: "C:\\custom\\webview2-data" });
|
|
64
64
|
// 或指定日志文件路径
|
|
65
65
|
const app = new Application({ LogFilePath: "./app.log" });
|
|
66
|
+
// 或注册自定义协议代理
|
|
67
|
+
const app = new Application({ localProxys: [{ protocolName: "app", localPath: "./public" }] });
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
**构造函数参数**
|
|
69
71
|
|
|
70
|
-
| 选项 | 类型
|
|
71
|
-
| ------------------------------- |
|
|
72
|
-
| `WindowsWebview2UserDataFolder` | `string`(可选)
|
|
73
|
-
| `LogFilePath` | `string`(可选)
|
|
72
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
73
|
+
| ------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------ | ----------------------------------- |
|
|
74
|
+
| `WindowsWebview2UserDataFolder` | `string`(可选) | `%LOCALAPPDATA%\NexfepDevelopment.webview2-data` | WebView2 用户数据目录(仅 Windows) |
|
|
75
|
+
| `LogFilePath` | `string`(可选) | 无 | 日志文件输出路径 |
|
|
76
|
+
| `localProxys` | `Array<{ protocolName: string; localPath: string }>`(可选) | 无 | 自定义协议代理,用于提供本地文件服务 |
|
|
74
77
|
|
|
75
78
|
**属性**
|
|
76
79
|
|
|
77
80
|
- `windows` — `WindowPool` 实例,用于管理浏览器窗口
|
|
78
|
-
- `utils` —
|
|
81
|
+
- `utils` — 工具方法(如桌面通知、文件选择弹窗)
|
|
79
82
|
- `logger` — `Logger` 实例,用于日志记录
|
|
80
83
|
|
|
81
84
|
**方法**
|
|
@@ -84,7 +87,69 @@ const app = new Application({ LogFilePath: "./app.log" });
|
|
|
84
87
|
- `createLocker(appName)` — 创建应用实例锁,防止多个实例运行
|
|
85
88
|
- `exit()` — 退出应用
|
|
86
89
|
|
|
87
|
-
###
|
|
90
|
+
### 基础类型
|
|
91
|
+
|
|
92
|
+
Nexfep 提供了几个贯穿框架的基础类型。
|
|
93
|
+
|
|
94
|
+
#### Size
|
|
95
|
+
|
|
96
|
+
`Size` 表示窗口尺寸,包含宽度和高度,支持逻辑像素和物理像素。
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { Size } from "nexfep";
|
|
100
|
+
|
|
101
|
+
const size = new Size(800, 600);
|
|
102
|
+
const size = new Size(800, 600, true); // 逻辑像素(默认)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**属性**
|
|
106
|
+
|
|
107
|
+
| 属性 | 类型 | 说明 |
|
|
108
|
+
| -------- | --------- | ----------------------------- |
|
|
109
|
+
| `width` | `number` | 宽度(像素) |
|
|
110
|
+
| `height` | `number` | 高度(像素) |
|
|
111
|
+
| `logical`| `boolean` | 是否使用逻辑像素,默认 `true` |
|
|
112
|
+
|
|
113
|
+
#### Position
|
|
114
|
+
|
|
115
|
+
`Position` 表示窗口在屏幕上的位置,支持逻辑像素和物理像素。
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { Position } from "nexfep";
|
|
119
|
+
|
|
120
|
+
const pos = new Position(100, 100);
|
|
121
|
+
const pos = new Position(100, 100, false); // 物理像素(默认)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**属性**
|
|
125
|
+
|
|
126
|
+
| 属性 | 类型 | 说明 |
|
|
127
|
+
| -------- | --------- | ------------------------------ |
|
|
128
|
+
| `x` | `number` | X 坐标(像素) |
|
|
129
|
+
| `y` | `number` | Y 坐标(像素) |
|
|
130
|
+
| `logical`| `boolean` | 是否使用逻辑像素,默认 `false` |
|
|
131
|
+
|
|
132
|
+
#### WindowLevel
|
|
133
|
+
|
|
134
|
+
`WindowLevel` 是一个枚举,表示窗口的 Z 轴层级。
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { WindowLevel } from "nexfep";
|
|
138
|
+
|
|
139
|
+
window.setLevel(WindowLevel.Bottommost);
|
|
140
|
+
window.setLevel(WindowLevel.Normal);
|
|
141
|
+
window.setLevel(WindowLevel.Topmost);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**值**
|
|
145
|
+
|
|
146
|
+
| 值 | 数值 | 说明 |
|
|
147
|
+
| -------------------------- | ---- | -------- |
|
|
148
|
+
| `WindowLevel.Bottommost` | `-1` | 置底 |
|
|
149
|
+
| `WindowLevel.Normal` | `0` | 正常层级 |
|
|
150
|
+
| `WindowLevel.Topmost` | `1` | 置顶 |
|
|
151
|
+
|
|
152
|
+
#### Icon
|
|
88
153
|
|
|
89
154
|
`Icon` 用于表示图片资源,可被用作窗口图标、托盘图标等。
|
|
90
155
|
|
|
@@ -233,6 +298,55 @@ const notification = app.utils.notify("标题", { body: "通知内容" });
|
|
|
233
298
|
- `options`(可选)— 配置对象
|
|
234
299
|
- `body` — 通知正文
|
|
235
300
|
|
|
301
|
+
### 选择文件弹窗
|
|
302
|
+
|
|
303
|
+
通过 `app.utils.openFileDialog()` 打开选择文件弹窗。
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
const files = await app.utils.openFileDialog({
|
|
307
|
+
multiple: true,
|
|
308
|
+
title: "选择文件",
|
|
309
|
+
filters: [
|
|
310
|
+
{ name: "所有文件", extensions: ["*"] },
|
|
311
|
+
{ name: "图片文件", extensions: ["jpg", "jpeg", "png"] },
|
|
312
|
+
],
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**参数**
|
|
317
|
+
|
|
318
|
+
- `multiple`(可选)— 是否允许选择多个文件
|
|
319
|
+
- `title`(可选)— 弹窗标题
|
|
320
|
+
- `filters`(可选)— 文件类型过滤器数组,每个元素为一个对象,包含 `name`(过滤器名称)和 `extensions`(支持的文件扩展名数组)
|
|
321
|
+
|
|
322
|
+
**返回值**
|
|
323
|
+
|
|
324
|
+
- `Array<string>`: 选择的文件路径数组
|
|
325
|
+
|
|
326
|
+
### 本地协议代理
|
|
327
|
+
|
|
328
|
+
Nexfep 支持注册自定义协议处理器,用于提供本地文件服务,从而可以通过自定义协议(如 `app://`)加载本地资源。
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
const app = new Application({
|
|
332
|
+
localProxys: [
|
|
333
|
+
{ protocolName: "app", localPath: "./public" },
|
|
334
|
+
],
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
注册后,即可使用自定义协议加载页面:
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
await window.loadURL("app://index.html");
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
对 `app://` 的请求会被拦截,并从 `./public` 目录中提供对应的文件。MIME 类型会根据文件扩展名自动检测。
|
|
345
|
+
|
|
346
|
+
**安全**
|
|
347
|
+
|
|
348
|
+
协议代理使用路径解析来防止目录遍历攻击。如果请求试图访问配置的 `localPath` 之外的文件,将返回 `403 Forbidden` 响应。
|
|
349
|
+
|
|
236
350
|
### 窗口池
|
|
237
351
|
|
|
238
352
|
`WindowPool` 是框架的核心管理类,负责窗口的创建和回收。
|
|
@@ -260,22 +374,22 @@ const win = await pool.createWindow({
|
|
|
260
374
|
title: "我的应用", // 窗口标题,默认 "Nexfep Window"
|
|
261
375
|
icon: iconInstance, // 窗口图标,Icon 实例,可选
|
|
262
376
|
resizable: true, // 是否可调整大小,默认 true
|
|
263
|
-
|
|
264
|
-
|
|
377
|
+
size: new Size(800, 600), // 窗口尺寸,可选
|
|
378
|
+
position: new Position(100, 100), // 窗口位置,可选
|
|
265
379
|
});
|
|
266
380
|
```
|
|
267
381
|
|
|
268
382
|
**参数说明**
|
|
269
383
|
|
|
270
|
-
| 选项 | 类型
|
|
271
|
-
| ------------ |
|
|
272
|
-
| `visible` | `boolean`
|
|
273
|
-
| `decoration` | `boolean`
|
|
274
|
-
| `title` | `string`
|
|
275
|
-
| `icon` | `Icon`
|
|
276
|
-
| `resizable` | `boolean`
|
|
277
|
-
| `
|
|
278
|
-
| `
|
|
384
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
385
|
+
| ------------ | ----------- | ----------------- | ------------------------------------------------------------------- |
|
|
386
|
+
| `visible` | `boolean` | `true` | 是否立即显示窗口 |
|
|
387
|
+
| `decoration` | `boolean` | `true` | 是否使用系统窗口装饰。设为 `false` 时,窗口无边框,需要自定义标题栏 |
|
|
388
|
+
| `title` | `string` | `"Nexfep Window"` | 窗口标题 |
|
|
389
|
+
| `icon` | `Icon` | 无 | 窗口图标 |
|
|
390
|
+
| `resizable` | `boolean` | `true` | 窗口是否可调整大小 |
|
|
391
|
+
| `size` | `Size` | 无 | 窗口尺寸,可选 |
|
|
392
|
+
| `position` | `Position` | 无 | 窗口位置,可选 |
|
|
279
393
|
|
|
280
394
|
### 窗口操作
|
|
281
395
|
|
|
@@ -287,7 +401,8 @@ window.minimize();
|
|
|
287
401
|
window.close();
|
|
288
402
|
window.focus();
|
|
289
403
|
window.setTitle("新标题");
|
|
290
|
-
window.setSize(800, 600);
|
|
404
|
+
window.setSize(new Size(800, 600));
|
|
405
|
+
window.setPosition(new Position(100, 100));
|
|
291
406
|
window.openDevTools();
|
|
292
407
|
```
|
|
293
408
|
|
|
@@ -640,11 +755,12 @@ nexfep build -u 7
|
|
|
640
755
|
|
|
641
756
|
| 方法/属性 | 参数 | 返回值 | 说明 |
|
|
642
757
|
| ----------------------- | -------------------------------------------------- | ----------- | ---------------- |
|
|
643
|
-
| `constructor(options?)` | `{ WindowsWebview2UserDataFolder?, LogFilePath? }` | Application | 创建应用实例 |
|
|
758
|
+
| `constructor(options?)` | `{ WindowsWebview2UserDataFolder?, LogFilePath?, localProxys? }` | Application | 创建应用实例 |
|
|
644
759
|
| `windows` | / | WindowPool | 窗口池实例 |
|
|
645
|
-
| `utils` | / | \_\_Utils |
|
|
760
|
+
| `utils` | / | \_\_Utils | 工具方法(通知、文件选择弹窗) |
|
|
646
761
|
| `logger` | / | Logger | 日志实例 |
|
|
647
762
|
| `createTray(options)` | 见 Tray 章节 | Tray | 创建系统托盘图标 |
|
|
763
|
+
| `createLocker(appName)` | `appName`: string | Locker | 创建应用实例锁 |
|
|
648
764
|
| `exit()` | 无 | void | 退出应用 |
|
|
649
765
|
|
|
650
766
|
### WindowPool
|
|
@@ -656,7 +772,8 @@ nexfep build -u 7
|
|
|
656
772
|
| `unhandle(event, callback)` | `event`: string, `callback`: (data: any) => any | 无 | 取消监听指定事件 |
|
|
657
773
|
| `global` | / | Map\<string, any> | 全局变量 Map |
|
|
658
774
|
| `closeWindow(window)` | `window`: Window | Promise\<void> | 关闭指定窗口并回收至池中 |
|
|
659
|
-
| `onCustomMessage` | `(window: Window, data:
|
|
775
|
+
| `onCustomMessage` | `(window: Window, message: string, data: any) => void` | 无 | 自定义消息回调函数 |
|
|
776
|
+
| `broadcast(event, data)` | `event`: string, `data`: any | 无 | 向所有打开的窗口发送事件 |
|
|
660
777
|
|
|
661
778
|
### Window
|
|
662
779
|
|
|
@@ -674,19 +791,20 @@ nexfep build -u 7
|
|
|
674
791
|
| `setTitle(title)` | `title`: string | void | 设置窗口标题 |
|
|
675
792
|
| `setDecorated(isDecorated)` | `isDecorated`: boolean | void | 设置窗口是否带边框和标题栏 |
|
|
676
793
|
| `setResizable(resizable)` | `resizable`: boolean | void | 设置窗口是否可调整大小 |
|
|
677
|
-
| `setLevel(level)`
|
|
794
|
+
| `setLevel(level)` | `level`: `WindowLevel` | void | 设置窗口层级:Bottommost、Normal、Topmost |
|
|
678
795
|
| `setFullScreen(isFullScreen, options?)` | `isFullScreen`: `boolean`, `options?`: `{ borderless?: boolean }` | void | 设置全屏模式。`borderless: true` 为无边框全屏,否则为独占全屏 |
|
|
679
796
|
| `setIcon(icon)` | `icon`: `Icon` | void | 设置窗口图标 |
|
|
680
|
-
| `setSize(
|
|
681
|
-
| `getSize()`
|
|
682
|
-
| `setPosition(
|
|
683
|
-
| `getPosition()`
|
|
797
|
+
| `setSize(size)` | `size`: `Size` | void | 设置窗口尺寸 |
|
|
798
|
+
| `getSize(logical?)` | `logical?`: `boolean` | { width: number, height: number } | 获取窗口尺寸(像素) |
|
|
799
|
+
| `setPosition(position)` | `position`: `Position` | void | 设置窗口位置 |
|
|
800
|
+
| `getPosition(logical?)` | `logical?`: `boolean` | { x: number, y: number } | 获取窗口位置(像素) |
|
|
684
801
|
| `focus()` | 无 | void | 窗口获取焦点 |
|
|
685
802
|
| `isFocused()` | 无 | boolean | 是否有焦点 |
|
|
686
803
|
| `isMaximized()` | 无 | boolean | 是否最大化 |
|
|
687
804
|
| `isMinimized()` | 无 | boolean | 是否最小化 |
|
|
688
805
|
| `toggleMaximize()` | 无 | void | 切换最大化状态 |
|
|
689
806
|
| `toggleMinimize()` | 无 | void | 切换最小化状态 |
|
|
807
|
+
| `tell(message, data)` | `message`: string, `data`: any | void | 从主进程向该窗口发送消息 |
|
|
690
808
|
| `openDevTools()` | 无 | void | 打开开发者工具 |
|
|
691
809
|
| `closeDevTools()` | 无 | void | 关闭开发者工具 |
|
|
692
810
|
| `id` | 无 | number | 窗口唯一标识,自增编号 |
|
package/README.md
CHANGED
|
@@ -63,19 +63,22 @@ const app = new Application();
|
|
|
63
63
|
const app = new Application({ WindowsWebview2UserDataFolder: "C:\\custom\\webview2-data" });
|
|
64
64
|
// or with log file path
|
|
65
65
|
const app = new Application({ LogFilePath: "./app.log" });
|
|
66
|
+
// or with custom protocol proxy
|
|
67
|
+
const app = new Application({ localProxys: [{ protocolName: "app", localPath: "./public" }] });
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
**Constructor Options**
|
|
69
71
|
|
|
70
|
-
| Option | Type
|
|
71
|
-
| ------------------------------- |
|
|
72
|
-
| `WindowsWebview2UserDataFolder` | `string` (optional)
|
|
73
|
-
| `LogFilePath` | `string` (optional)
|
|
72
|
+
| Option | Type | Default | Description |
|
|
73
|
+
| ------------------------------- | --------------------------------------------------------------------- | ------------------------------------------------ | ------------------------------------------- |
|
|
74
|
+
| `WindowsWebview2UserDataFolder` | `string` (optional) | `%LOCALAPPDATA%\NexfepDevelopment.webview2-data` | WebView2 user data directory (Windows only) |
|
|
75
|
+
| `LogFilePath` | `string` (optional) | none | File path for log output |
|
|
76
|
+
| `localProxys` | `Array<{ protocolName: string; localPath: string }>` (optional) | none | Custom protocol proxies for serving local files |
|
|
74
77
|
|
|
75
78
|
**Properties**
|
|
76
79
|
|
|
77
80
|
- `windows` — The `WindowPool` instance for managing browser windows
|
|
78
|
-
- `utils` — Utility methods (e.g., desktop notifications)
|
|
81
|
+
- `utils` — Utility methods (e.g., desktop notifications, file dialogs)
|
|
79
82
|
- `logger` — The `Logger` instance for logging
|
|
80
83
|
|
|
81
84
|
**Methods**
|
|
@@ -84,7 +87,69 @@ const app = new Application({ LogFilePath: "./app.log" });
|
|
|
84
87
|
- `createLocker(appName)` — Create an application instance lock to prevent multiple instances
|
|
85
88
|
- `exit()` — Exit the application
|
|
86
89
|
|
|
87
|
-
###
|
|
90
|
+
### Basic Types
|
|
91
|
+
|
|
92
|
+
Nexfep provides several basic types that are used throughout the framework.
|
|
93
|
+
|
|
94
|
+
#### Size
|
|
95
|
+
|
|
96
|
+
`Size` represents a window size with width and height, supporting both logical and physical pixels.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { Size } from "nexfep";
|
|
100
|
+
|
|
101
|
+
const size = new Size(800, 600);
|
|
102
|
+
const size = new Size(800, 600, true); // logical pixels (default)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Properties**
|
|
106
|
+
|
|
107
|
+
| Property | Type | Description |
|
|
108
|
+
| -------- | -------- | ------------------------------------------------------- |
|
|
109
|
+
| `width` | `number` | Width in pixels |
|
|
110
|
+
| `height` | `number` | Height in pixels |
|
|
111
|
+
| `logical`| `boolean`| Whether to use logical pixels (DPI-aware), default `true` |
|
|
112
|
+
|
|
113
|
+
#### Position
|
|
114
|
+
|
|
115
|
+
`Position` represents a window position on screen, supporting both logical and physical pixels.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { Position } from "nexfep";
|
|
119
|
+
|
|
120
|
+
const pos = new Position(100, 100);
|
|
121
|
+
const pos = new Position(100, 100, false); // physical pixels (default)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Properties**
|
|
125
|
+
|
|
126
|
+
| Property | Type | Description |
|
|
127
|
+
| -------- | -------- | -------------------------------------------------------- |
|
|
128
|
+
| `x` | `number` | X position in pixels |
|
|
129
|
+
| `y` | `number` | Y position in pixels |
|
|
130
|
+
| `logical`| `boolean`| Whether to use logical pixels (DPI-aware), default `false` |
|
|
131
|
+
|
|
132
|
+
#### WindowLevel
|
|
133
|
+
|
|
134
|
+
`WindowLevel` is an enum representing the window z-order level.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { WindowLevel } from "nexfep";
|
|
138
|
+
|
|
139
|
+
window.setLevel(WindowLevel.Bottommost);
|
|
140
|
+
window.setLevel(WindowLevel.Normal);
|
|
141
|
+
window.setLevel(WindowLevel.Topmost);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Values**
|
|
145
|
+
|
|
146
|
+
| Value | Numeric | Description |
|
|
147
|
+
| --------------------------- | ------- | ----------------- |
|
|
148
|
+
| `WindowLevel.Bottommost` | `-1` | Always on bottom |
|
|
149
|
+
| `WindowLevel.Normal` | `0` | Normal level |
|
|
150
|
+
| `WindowLevel.Topmost` | `1` | Always on top |
|
|
151
|
+
|
|
152
|
+
#### Icon
|
|
88
153
|
|
|
89
154
|
`Icon` represents an image resource that can be used as a window icon, tray icon, etc.
|
|
90
155
|
|
|
@@ -231,6 +296,55 @@ const notification = app.utils.notify("Title", { body: "Notification body" });
|
|
|
231
296
|
- `options` (optional) — Configuration object
|
|
232
297
|
- `body` — Notification body text
|
|
233
298
|
|
|
299
|
+
### File Selection Dialog
|
|
300
|
+
|
|
301
|
+
Open the file selection dialog using `app.utils.openFileDialog()`.
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
const files = await app.utils.openFileDialog({
|
|
305
|
+
multiple: true,
|
|
306
|
+
title: "Select Files",
|
|
307
|
+
filters: [
|
|
308
|
+
{ name: "All Files", extensions: ["*"] },
|
|
309
|
+
{ name: "Image Files", extensions: ["jpg", "jpeg", "png"] },
|
|
310
|
+
],
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**Parameters**
|
|
315
|
+
|
|
316
|
+
- `multiple` (Optional) — Allow selection of multiple files
|
|
317
|
+
- `title` (Optional) — Title of the dialog
|
|
318
|
+
- `filters` (Optional) — Array of file type filters, each object has `name` (filter name) and `extensions` (supported file extensions array)
|
|
319
|
+
|
|
320
|
+
**Return Value**
|
|
321
|
+
|
|
322
|
+
- `Array<string>`: Array of selected file paths
|
|
323
|
+
|
|
324
|
+
### Local Protocol Proxy
|
|
325
|
+
|
|
326
|
+
Nexfep supports registering custom protocol handlers to serve local files, allowing you to load local resources using custom protocols like `app://`.
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
const app = new Application({
|
|
330
|
+
localProxys: [
|
|
331
|
+
{ protocolName: "app", localPath: "./public" },
|
|
332
|
+
],
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
After registration, you can load pages using the custom protocol:
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
await window.loadURL("app://index.html");
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Requests to `app://` will be intercepted and the corresponding files from the `./public` directory will be served. The MIME type is automatically detected based on the file extension.
|
|
343
|
+
|
|
344
|
+
**Security**
|
|
345
|
+
|
|
346
|
+
The proxy uses path resolution to prevent directory traversal attacks. If a request tries to access files outside the configured `localPath`, it will return a `403 Forbidden` response.
|
|
347
|
+
|
|
234
348
|
### Window Pool
|
|
235
349
|
|
|
236
350
|
`WindowPool` is the core management class of the framework, responsible for window creation and recycling.
|
|
@@ -258,22 +372,22 @@ const win = await pool.createWindow({
|
|
|
258
372
|
title: "My App", // Window title, default "Nexfep Window"
|
|
259
373
|
icon: iconInstance, // Window icon, Icon instance, optional
|
|
260
374
|
resizable: true, // Whether the window is resizable, default true
|
|
261
|
-
|
|
262
|
-
|
|
375
|
+
size: new Size(800, 600), // Window size, optional
|
|
376
|
+
position: new Position(100, 100), // Window position, optional
|
|
263
377
|
});
|
|
264
378
|
```
|
|
265
379
|
|
|
266
380
|
**Parameters**
|
|
267
381
|
|
|
268
|
-
| Option | Type
|
|
269
|
-
| ------------ |
|
|
270
|
-
| `visible` | `boolean`
|
|
271
|
-
| `decoration` | `boolean`
|
|
272
|
-
| `title` | `string`
|
|
273
|
-
| `icon` | `Icon`
|
|
274
|
-
| `resizable` | `boolean`
|
|
275
|
-
| `
|
|
276
|
-
| `
|
|
382
|
+
| Option | Type | Default | Description |
|
|
383
|
+
| ------------ | ----------- | ----------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
384
|
+
| `visible` | `boolean` | `true` | Whether to immediately show the window |
|
|
385
|
+
| `decoration` | `boolean` | `true` | Whether to use system window decorations. When `false`, the window has no border and requires a custom title bar |
|
|
386
|
+
| `title` | `string` | `"Nexfep Window"` | Window title |
|
|
387
|
+
| `icon` | `Icon` | none | Window icon |
|
|
388
|
+
| `resizable` | `boolean` | `true` | Whether the window is resizable |
|
|
389
|
+
| `size` | `Size` | none | Window size, optional |
|
|
390
|
+
| `position` | `Position` | none | Window position, optional |
|
|
277
391
|
|
|
278
392
|
### Window Operations
|
|
279
393
|
|
|
@@ -285,7 +399,8 @@ window.minimize();
|
|
|
285
399
|
window.close();
|
|
286
400
|
window.focus();
|
|
287
401
|
window.setTitle("New Title");
|
|
288
|
-
window.setSize(800, 600);
|
|
402
|
+
window.setSize(new Size(800, 600));
|
|
403
|
+
window.setPosition(new Position(100, 100));
|
|
289
404
|
window.openDevTools();
|
|
290
405
|
```
|
|
291
406
|
|
|
@@ -638,11 +753,12 @@ Please do not include the outer `metadata` field, just the internal fields. Like
|
|
|
638
753
|
|
|
639
754
|
| Method/Property | Parameters | Return Value | Description |
|
|
640
755
|
| ----------------------- | -------------------------------------------------- | ------------ | -------------------------------- |
|
|
641
|
-
| `constructor(options?)` | `{ WindowsWebview2UserDataFolder?, LogFilePath? }` | Application | Creates the application instance |
|
|
756
|
+
| `constructor(options?)` | `{ WindowsWebview2UserDataFolder?, LogFilePath?, localProxys? }` | Application | Creates the application instance |
|
|
642
757
|
| `windows` | / | WindowPool | The window pool instance |
|
|
643
|
-
| `utils` | / | \_\_Utils | Utility methods (notifications)
|
|
758
|
+
| `utils` | / | \_\_Utils | Utility methods (notifications, file dialogs) |
|
|
644
759
|
| `logger` | / | Logger | The logger instance |
|
|
645
760
|
| `createTray(options)` | see Tray section | Tray | Creates a system tray icon |
|
|
761
|
+
| `createLocker(appName)` | `appName`: string | Locker | Creates an application lock |
|
|
646
762
|
| `exit()` | None | void | Exits the application |
|
|
647
763
|
|
|
648
764
|
### WindowPool
|
|
@@ -654,7 +770,8 @@ Please do not include the outer `metadata` field, just the internal fields. Like
|
|
|
654
770
|
| `unhandle(event, callback)` | `event`: string, `callback`: (data: any) => any | None | Removes the specified event listener |
|
|
655
771
|
| `global` | / | Map\<string, any> | A global variable map |
|
|
656
772
|
| `closeWindow(window)` | `window`: Window | Promise\<void> | Closes the specified window and returns it to the pool |
|
|
657
|
-
| `onCustomMessage` | `(window: Window, data:
|
|
773
|
+
| `onCustomMessage` | `(window: Window, message: string, data: any) => void` | None | Custom message callback |
|
|
774
|
+
| `broadcast(event, data)` | `event`: string, `data`: any | None | Sends an event to all open windows |
|
|
658
775
|
|
|
659
776
|
### Window
|
|
660
777
|
|
|
@@ -672,19 +789,20 @@ Please do not include the outer `metadata` field, just the internal fields. Like
|
|
|
672
789
|
| `setTitle(title)` | `title`: string | void | Sets the window title |
|
|
673
790
|
| `setDecorated(isDecorated)` | `isDecorated`: boolean | void | Sets whether the window has borders and title bar |
|
|
674
791
|
| `setResizable(resizable)` | `resizable`: boolean | void | Sets whether the window is resizable |
|
|
675
|
-
| `setLevel(level)`
|
|
792
|
+
| `setLevel(level)` | `level`: `WindowLevel` | void | Sets window level: Bottommost, Normal, Topmost |
|
|
676
793
|
| `setFullScreen(isFullScreen, options?)` | `isFullScreen`: `boolean`, `options?`: `{ borderless?: boolean }` | void | Sets fullscreen mode. `borderless: true` for borderless fullscreen, otherwise exclusive fullscreen |
|
|
677
794
|
| `setIcon(icon)` | `icon`: `Icon` | void | Sets the window icon |
|
|
678
|
-
| `setSize(
|
|
679
|
-
| `getSize()`
|
|
680
|
-
| `setPosition(
|
|
681
|
-
| `getPosition()`
|
|
795
|
+
| `setSize(size)` | `size`: `Size` | void | Sets the window size |
|
|
796
|
+
| `getSize(logical?)` | `logical?`: `boolean` | { width: number, height: number } | Gets the window size in pixels |
|
|
797
|
+
| `setPosition(position)` | `position`: `Position` | void | Sets the window position |
|
|
798
|
+
| `getPosition(logical?)` | `logical?`: `boolean` | { x: number, y: number } | Gets the window position in pixels |
|
|
682
799
|
| `focus()` | None | void | Focuses the window |
|
|
683
800
|
| `isFocused()` | None | boolean | Whether the window has focus |
|
|
684
801
|
| `isMaximized()` | None | boolean | Whether the window is maximized |
|
|
685
802
|
| `isMinimized()` | None | boolean | Whether the window is minimized |
|
|
686
803
|
| `toggleMaximize()` | None | void | Toggles the window maximized state |
|
|
687
804
|
| `toggleMinimize()` | None | void | Toggles the window minimized state |
|
|
805
|
+
| `tell(message, data)` | `message`: string, `data`: any | void | Sends a message to the window from the main process |
|
|
688
806
|
| `openDevTools()` | None | void | Opens developer tools |
|
|
689
807
|
| `closeDevTools()` | None | void | Closes developer tools |
|
|
690
808
|
| `id` | None | number | Unique window identifier, auto-incrementing |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nexfep",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A desktop application framework based on @webviewjs/webview",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"@webviewjs/webview",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"src/Logger.d.ts",
|
|
42
42
|
"src/SingleInstance.js",
|
|
43
43
|
"src/SingleInstance.d.ts",
|
|
44
|
-
"src/
|
|
45
|
-
"src/
|
|
44
|
+
"src/Basics.js",
|
|
45
|
+
"src/Basics.d.ts",
|
|
46
46
|
"frontend/index.d.ts"
|
|
47
47
|
],
|
|
48
48
|
"type": "module",
|
|
@@ -66,7 +66,8 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@nexfteam/single-instance": "0.0.8",
|
|
69
|
-
"@webviewjs/webview": "0.4.
|
|
69
|
+
"@webviewjs/webview": "0.4.3",
|
|
70
|
+
"mrmime": "^2.0.1"
|
|
70
71
|
},
|
|
71
72
|
"devDependencies": {
|
|
72
73
|
"@types/node": "^22.0.0",
|
package/src/Application.d.ts
CHANGED
|
@@ -3,13 +3,21 @@ import { WindowPool } from "./WindowManager.js";
|
|
|
3
3
|
import { Tray } from "./Tray.js";
|
|
4
4
|
import { Logger } from "./Logger.js";
|
|
5
5
|
import { Locker } from "./SingleInstance.js";
|
|
6
|
-
import { Icon } from "./
|
|
6
|
+
import { Icon } from "./Basics.js";
|
|
7
7
|
declare class __Utils {
|
|
8
|
-
app:
|
|
9
|
-
constructor(app:
|
|
8
|
+
app: Application;
|
|
9
|
+
constructor(app: Application);
|
|
10
10
|
notify(title: string, options?: {
|
|
11
11
|
body?: string;
|
|
12
12
|
}): Notification;
|
|
13
|
+
openFileDialog(options?: {
|
|
14
|
+
multiple?: boolean;
|
|
15
|
+
title?: string;
|
|
16
|
+
filters?: Array<{
|
|
17
|
+
name: string;
|
|
18
|
+
extensions: Array<string>;
|
|
19
|
+
}>;
|
|
20
|
+
}): Promise<string[]>;
|
|
13
21
|
}
|
|
14
22
|
declare class Application {
|
|
15
23
|
app: WebviewApplication;
|
|
@@ -19,6 +27,10 @@ declare class Application {
|
|
|
19
27
|
constructor(options?: {
|
|
20
28
|
WindowsWebview2UserDataFolder?: string;
|
|
21
29
|
LogFilePath?: string;
|
|
30
|
+
localProxys?: Array<{
|
|
31
|
+
protocolName: string;
|
|
32
|
+
localPath: string;
|
|
33
|
+
}>;
|
|
22
34
|
});
|
|
23
35
|
createTray(options: {
|
|
24
36
|
id: string;
|
|
@@ -32,4 +44,5 @@ declare class Application {
|
|
|
32
44
|
createLocker(appName: string): Locker;
|
|
33
45
|
exit(): void;
|
|
34
46
|
}
|
|
35
|
-
export { Application
|
|
47
|
+
export { Application };
|
|
48
|
+
export * from "./Basics.js";
|
package/src/Application.js
CHANGED
|
@@ -3,7 +3,6 @@ import { WindowPool } from "./WindowManager.js";
|
|
|
3
3
|
import { Tray } from "./Tray.js";
|
|
4
4
|
import { Logger } from "./Logger.js";
|
|
5
5
|
import { Locker } from "./SingleInstance.js";
|
|
6
|
-
import { Icon } from "./Icon.js";
|
|
7
6
|
class __Utils {
|
|
8
7
|
app;
|
|
9
8
|
constructor(app) {
|
|
@@ -13,6 +12,12 @@ class __Utils {
|
|
|
13
12
|
const notification = new Notification(title, { body: options?.body });
|
|
14
13
|
return notification;
|
|
15
14
|
}
|
|
15
|
+
async openFileDialog(options) {
|
|
16
|
+
const tempWin = await this.app.windows.createWindow({ visible: false });
|
|
17
|
+
const result = tempWin.window.openFileDialog(options);
|
|
18
|
+
tempWin.close();
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
16
21
|
}
|
|
17
22
|
class Application {
|
|
18
23
|
app;
|
|
@@ -21,11 +26,12 @@ class Application {
|
|
|
21
26
|
utils;
|
|
22
27
|
constructor(options = {}) {
|
|
23
28
|
this.app = new WebviewApplication();
|
|
24
|
-
this.utils = new __Utils(this
|
|
29
|
+
this.utils = new __Utils(this);
|
|
25
30
|
this.logger = new Logger(options.LogFilePath);
|
|
26
31
|
const poolOptions = {
|
|
27
32
|
WindowsWebview2UserDataFolder: options.WindowsWebview2UserDataFolder,
|
|
28
33
|
logger: this.logger,
|
|
34
|
+
localProxys: options.localProxys,
|
|
29
35
|
};
|
|
30
36
|
this.windows = new WindowPool(this.app, poolOptions);
|
|
31
37
|
this.app.whenReady();
|
|
@@ -40,4 +46,5 @@ class Application {
|
|
|
40
46
|
this.app.exit();
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
|
-
export { Application
|
|
49
|
+
export { Application };
|
|
50
|
+
export * from "./Basics.js";
|
|
@@ -16,4 +16,21 @@ declare class Icon {
|
|
|
16
16
|
toTrayIconImage(): TrayIconImage;
|
|
17
17
|
toBuffer(): Buffer;
|
|
18
18
|
}
|
|
19
|
-
|
|
19
|
+
declare class Size {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
logical: boolean;
|
|
23
|
+
constructor(width: number, height: number, logical?: boolean);
|
|
24
|
+
}
|
|
25
|
+
declare class Position {
|
|
26
|
+
x: number;
|
|
27
|
+
y: number;
|
|
28
|
+
logical: boolean;
|
|
29
|
+
constructor(x: number, y: number, logical?: boolean);
|
|
30
|
+
}
|
|
31
|
+
declare enum WindowLevel {
|
|
32
|
+
Bottommost = -1,
|
|
33
|
+
Normal = 0,
|
|
34
|
+
Topmost = 1
|
|
35
|
+
}
|
|
36
|
+
export { Icon, Size, Position, WindowLevel };
|
|
@@ -33,4 +33,30 @@ class Icon {
|
|
|
33
33
|
return Buffer.from(this.data);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
class Size {
|
|
37
|
+
width;
|
|
38
|
+
height;
|
|
39
|
+
logical;
|
|
40
|
+
constructor(width, height, logical) {
|
|
41
|
+
this.width = width;
|
|
42
|
+
this.height = height;
|
|
43
|
+
this.logical = logical ?? true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
class Position {
|
|
47
|
+
x;
|
|
48
|
+
y;
|
|
49
|
+
logical;
|
|
50
|
+
constructor(x, y, logical) {
|
|
51
|
+
this.x = x;
|
|
52
|
+
this.y = y;
|
|
53
|
+
this.logical = logical ?? false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
var WindowLevel;
|
|
57
|
+
(function (WindowLevel) {
|
|
58
|
+
WindowLevel[WindowLevel["Bottommost"] = -1] = "Bottommost";
|
|
59
|
+
WindowLevel[WindowLevel["Normal"] = 0] = "Normal";
|
|
60
|
+
WindowLevel[WindowLevel["Topmost"] = 1] = "Topmost";
|
|
61
|
+
})(WindowLevel || (WindowLevel = {}));
|
|
62
|
+
export { Icon, Size, Position, WindowLevel };
|
package/src/Tray.d.ts
CHANGED
package/src/WindowManager.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Application, BrowserWindow, Webview } from "@webviewjs/webview";
|
|
2
2
|
import { Logger } from "./Logger.js";
|
|
3
|
-
import { Icon } from "./
|
|
3
|
+
import { Icon, Size, Position, WindowLevel } from "./Basics.js";
|
|
4
4
|
declare class Window {
|
|
5
5
|
window: BrowserWindow;
|
|
6
6
|
webview: Webview;
|
|
@@ -10,7 +10,7 @@ declare class Window {
|
|
|
10
10
|
id: number;
|
|
11
11
|
private myPool;
|
|
12
12
|
constructor(window: BrowserWindow, webview: Webview, id: number, myPool: WindowPool);
|
|
13
|
-
setLevel(level:
|
|
13
|
+
setLevel(level: WindowLevel): void;
|
|
14
14
|
setFullScreen(isFullScreen: boolean, options?: {
|
|
15
15
|
borderless?: boolean;
|
|
16
16
|
}): void;
|
|
@@ -28,10 +28,10 @@ declare class Window {
|
|
|
28
28
|
openDevTools(): void;
|
|
29
29
|
closeDevTools(): void;
|
|
30
30
|
setIcon(icon: Icon): void;
|
|
31
|
-
setSize(
|
|
32
|
-
getSize(): import("@webviewjs/webview").Dimensions;
|
|
33
|
-
setPosition(
|
|
34
|
-
getPosition(): import("@webviewjs/webview").Position;
|
|
31
|
+
setSize(size: Size): void;
|
|
32
|
+
getSize(logical?: boolean): import("@webviewjs/webview").Dimensions;
|
|
33
|
+
setPosition(position: Position): void;
|
|
34
|
+
getPosition(logical?: boolean): import("@webviewjs/webview").Position;
|
|
35
35
|
isFocused(): boolean;
|
|
36
36
|
focus(): void;
|
|
37
37
|
tell(message: string, data: any): void;
|
|
@@ -50,11 +50,17 @@ declare class WindowPool {
|
|
|
50
50
|
private injectCount;
|
|
51
51
|
private windowCount;
|
|
52
52
|
private freeWindowCount;
|
|
53
|
+
private localProxys;
|
|
53
54
|
global: Map<string, any>;
|
|
54
55
|
constructor(app: Application, options: {
|
|
55
56
|
WindowsWebview2UserDataFolder?: string;
|
|
56
57
|
logger?: Logger;
|
|
58
|
+
localProxys?: Array<{
|
|
59
|
+
protocolName: string;
|
|
60
|
+
localPath: string;
|
|
61
|
+
}>;
|
|
57
62
|
});
|
|
63
|
+
__registryLocalProxy(window: BrowserWindow, protocolName: string, localPath: string): void;
|
|
58
64
|
__injectCode(window: Window, code: string): Promise<void>;
|
|
59
65
|
__injectControlFunctions(windowObj: Window): Promise<void>;
|
|
60
66
|
__createNewWindowObj(): Promise<Window>;
|
|
@@ -64,8 +70,8 @@ declare class WindowPool {
|
|
|
64
70
|
title?: string;
|
|
65
71
|
icon?: Icon;
|
|
66
72
|
resizable?: boolean;
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
size?: Size;
|
|
74
|
+
position?: Position;
|
|
69
75
|
}): Promise<Window>;
|
|
70
76
|
closeWindow(window: Window): Promise<void>;
|
|
71
77
|
handle(event: string, callback: (data: any) => any): Promise<void>;
|
package/src/WindowManager.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Logger } from "./Logger.js";
|
|
2
|
+
import { WindowLevel } from "./Basics.js";
|
|
2
3
|
import os from "os";
|
|
3
4
|
import path from "path";
|
|
4
5
|
import fs from "fs";
|
|
6
|
+
import { lookup } from "mrmime";
|
|
5
7
|
class Window {
|
|
6
8
|
window;
|
|
7
9
|
webview;
|
|
@@ -20,17 +22,17 @@ class Window {
|
|
|
20
22
|
this.id = id;
|
|
21
23
|
}
|
|
22
24
|
setLevel(level) {
|
|
23
|
-
if (level ==
|
|
25
|
+
if (level == WindowLevel.Bottommost) {
|
|
24
26
|
this.window.setAlwaysOnTop(false);
|
|
25
27
|
this.window.setAlwaysOnBottom(true);
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
|
-
else if (level ==
|
|
30
|
+
else if (level == WindowLevel.Normal) {
|
|
29
31
|
this.window.setAlwaysOnTop(false);
|
|
30
32
|
this.window.setAlwaysOnBottom(false);
|
|
31
33
|
return;
|
|
32
34
|
}
|
|
33
|
-
else if (level ==
|
|
35
|
+
else if (level == WindowLevel.Topmost) {
|
|
34
36
|
this.window.setAlwaysOnTop(true);
|
|
35
37
|
this.window.setAlwaysOnBottom(false);
|
|
36
38
|
return;
|
|
@@ -102,17 +104,17 @@ class Window {
|
|
|
102
104
|
setIcon(icon) {
|
|
103
105
|
this.window.setWindowIcon(icon.data, icon.width, icon.height);
|
|
104
106
|
}
|
|
105
|
-
setSize(
|
|
106
|
-
this.window.setSize(width, height);
|
|
107
|
+
setSize(size) {
|
|
108
|
+
this.window.setSize(size.width, size.height, size.logical);
|
|
107
109
|
}
|
|
108
|
-
getSize() {
|
|
109
|
-
return this.window.getInnerSize();
|
|
110
|
+
getSize(logical) {
|
|
111
|
+
return this.window.getInnerSize(logical);
|
|
110
112
|
}
|
|
111
|
-
setPosition(
|
|
112
|
-
this.window.setPosition(x, y);
|
|
113
|
+
setPosition(position) {
|
|
114
|
+
this.window.setPosition(position.x, position.y, position.logical);
|
|
113
115
|
}
|
|
114
|
-
getPosition() {
|
|
115
|
-
return this.window.getPosition();
|
|
116
|
+
getPosition(logical) {
|
|
117
|
+
return this.window.getPosition(logical);
|
|
116
118
|
}
|
|
117
119
|
isFocused() {
|
|
118
120
|
return this.window.isFocused();
|
|
@@ -153,6 +155,7 @@ class WindowPool {
|
|
|
153
155
|
injectCount;
|
|
154
156
|
windowCount;
|
|
155
157
|
freeWindowCount;
|
|
158
|
+
localProxys;
|
|
156
159
|
global;
|
|
157
160
|
constructor(app, options) {
|
|
158
161
|
if (os.platform() === "win32") {
|
|
@@ -174,6 +177,36 @@ class WindowPool {
|
|
|
174
177
|
this.freeWindowCount = 0;
|
|
175
178
|
this.global = new Map();
|
|
176
179
|
this.logger = options.logger || new Logger();
|
|
180
|
+
this.localProxys = options.localProxys || [];
|
|
181
|
+
}
|
|
182
|
+
__registryLocalProxy(window, protocolName, localPath) {
|
|
183
|
+
const basePath = path.resolve(localPath);
|
|
184
|
+
console.log(window.registerProtocol);
|
|
185
|
+
window.registerProtocol(protocolName, async (request) => {
|
|
186
|
+
try {
|
|
187
|
+
const url = new URL(request.url);
|
|
188
|
+
let relativePath = url.pathname;
|
|
189
|
+
if (relativePath === "/" || relativePath === "") {
|
|
190
|
+
relativePath = "/index.html";
|
|
191
|
+
}
|
|
192
|
+
const absolutePath = path.join(basePath, relativePath);
|
|
193
|
+
const resolvedPath = path.resolve(absolutePath);
|
|
194
|
+
if (!resolvedPath.startsWith(basePath)) {
|
|
195
|
+
return new Response("Forbidden", { status: 403 });
|
|
196
|
+
}
|
|
197
|
+
const content = fs.readFileSync(resolvedPath);
|
|
198
|
+
const mimeType = lookup(resolvedPath) || "application/octet-stream";
|
|
199
|
+
return new Response(content, {
|
|
200
|
+
status: 200,
|
|
201
|
+
headers: {
|
|
202
|
+
"Content-Type": mimeType,
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return new Response("Not Found", { status: 404 });
|
|
208
|
+
}
|
|
209
|
+
});
|
|
177
210
|
}
|
|
178
211
|
async __injectCode(window, code) {
|
|
179
212
|
await window.webview.evaluateScript(code);
|
|
@@ -365,6 +398,9 @@ class WindowPool {
|
|
|
365
398
|
visible: false,
|
|
366
399
|
focused: false,
|
|
367
400
|
});
|
|
401
|
+
this.localProxys.forEach((proxy) => {
|
|
402
|
+
this.__registryLocalProxy(window, proxy.protocolName, proxy.localPath);
|
|
403
|
+
});
|
|
368
404
|
this.freeWindowCount++;
|
|
369
405
|
const webview = window.createWebview();
|
|
370
406
|
const windowObj = new Window(window, webview, ++this.windowCount, this);
|
|
@@ -486,7 +522,12 @@ class WindowPool {
|
|
|
486
522
|
window.setIcon(options?.icon);
|
|
487
523
|
}
|
|
488
524
|
window.setResizable(options?.resizable ?? true);
|
|
489
|
-
|
|
525
|
+
if (options?.size) {
|
|
526
|
+
window.setSize(options?.size);
|
|
527
|
+
}
|
|
528
|
+
if (options?.position) {
|
|
529
|
+
window.setPosition(options?.position);
|
|
530
|
+
}
|
|
490
531
|
}
|
|
491
532
|
if (this.freeWindowCount == 0) {
|
|
492
533
|
this.__createNewWindowObj();
|