nexfep 0.1.7 → 0.3.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/LICENSE +1 -1
- package/README-CN.md +218 -39
- package/README.md +219 -40
- package/cli/index.mjs +20 -0
- package/index.d.ts +1 -1
- package/index.js +1 -1
- package/package.json +10 -3
- package/src/Application.d.ts +30 -0
- package/src/Application.js +35 -0
- package/src/Tray.d.ts +36 -0
- package/src/Tray.js +65 -0
- package/src/WindowManager.d.ts +9 -5
- package/src/WindowManager.js +55 -10
package/LICENSE
CHANGED
package/README-CN.md
CHANGED
|
@@ -22,6 +22,10 @@ Nexfep 是一个基于 [@webviewjs/webview](https://github.com/webviewjs/webview
|
|
|
22
22
|
- **IPC 通信** — 支持主进程与 WebView 之间的双向消息通信,通过注入的函数进行调用
|
|
23
23
|
- **窗口控制** — 提供最大化、最小化、关闭、标题设置、开发者工具等完整窗口操作 API
|
|
24
24
|
- **拖拽区域** — 内置 HTML 属性支持,方便定义窗口拖拽区域(`nexfep-area-drag` 等)
|
|
25
|
+
- **系统托盘** — 创建和管理系统托盘图标及右键菜单
|
|
26
|
+
- **桌面通知** — 通过 `app.utils.notify()` 发送桌面通知
|
|
27
|
+
- **日志系统** — 内置 Logger,支持文件日志和彩色控制台输出,自动拦截页面 console 消息
|
|
28
|
+
- **CLI 构建工具** — 通过 `nexfep build` 将应用打包为独立可执行文件
|
|
25
29
|
- **TypeScript 支持** — 完整的类型定义,开发体验优秀
|
|
26
30
|
|
|
27
31
|
## 安装
|
|
@@ -33,30 +37,151 @@ pnpm add nexfep
|
|
|
33
37
|
## 快速开始
|
|
34
38
|
|
|
35
39
|
```typescript
|
|
36
|
-
import {
|
|
40
|
+
import { Application } from 'nexfep';
|
|
37
41
|
|
|
38
|
-
const
|
|
42
|
+
const app = new Application();
|
|
39
43
|
|
|
40
|
-
const window = await
|
|
44
|
+
const window = await app.windows.createWindow(true, false);
|
|
41
45
|
|
|
42
46
|
await window.loadHTML('<h1 nexfep-area-drag>Hello Nexfep!</h1>');
|
|
43
|
-
|
|
44
|
-
pool.mainloop();
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
## 使用指南
|
|
48
50
|
|
|
49
|
-
###
|
|
51
|
+
### Application
|
|
50
52
|
|
|
51
|
-
`
|
|
53
|
+
`Application` 是框架的主入口,负责管理应用生命周期,提供窗口、系统托盘和日志的访问。
|
|
52
54
|
|
|
53
55
|
```typescript
|
|
54
|
-
|
|
56
|
+
import { Application } from 'nexfep';
|
|
57
|
+
|
|
58
|
+
const app = new Application();
|
|
59
|
+
// 或指定自定义 WebView2 用户数据目录(仅 Windows 生效)
|
|
60
|
+
const app = new Application({ WindowsWebview2UserDataFolder: 'C:\\custom\\webview2-data' });
|
|
61
|
+
// 或指定日志文件路径
|
|
62
|
+
const app = new Application({ LogFilePath: './app.log' });
|
|
55
63
|
```
|
|
56
64
|
|
|
57
65
|
**构造函数参数**
|
|
58
66
|
|
|
59
|
-
|
|
67
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
68
|
+
|------|------|--------|------|
|
|
69
|
+
| `WindowsWebview2UserDataFolder` | `string`(可选) | `%LOCALAPPDATA%\NexfepDevelopment.webview2-data` | WebView2 用户数据目录(仅 Windows) |
|
|
70
|
+
| `LogFilePath` | `string`(可选) | 无 | 日志文件输出路径 |
|
|
71
|
+
|
|
72
|
+
**属性**
|
|
73
|
+
|
|
74
|
+
- `windows` — `WindowPool` 实例,用于管理浏览器窗口
|
|
75
|
+
- `utils` — 工具方法(如桌面通知)
|
|
76
|
+
- `logger` — `Logger` 实例,用于日志记录
|
|
77
|
+
|
|
78
|
+
**方法**
|
|
79
|
+
|
|
80
|
+
- `createTray(options)` — 创建系统托盘图标和右键菜单
|
|
81
|
+
- `exit()` — 退出应用
|
|
82
|
+
|
|
83
|
+
### Logger
|
|
84
|
+
|
|
85
|
+
Logger 支持文件输出和彩色控制台输出,可通过 `app.logger` 访问。
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
app.logger.log('Hello World');
|
|
89
|
+
app.logger.error('发生错误');
|
|
90
|
+
app.logger.warn('警告信息');
|
|
91
|
+
app.logger.info('提示信息');
|
|
92
|
+
app.logger.debug('调试信息');
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**方法**
|
|
96
|
+
|
|
97
|
+
| 方法 | 说明 |
|
|
98
|
+
|------|------|
|
|
99
|
+
| `log(message)` | 记录日志 |
|
|
100
|
+
| `error(message)` | 记录错误日志(红色) |
|
|
101
|
+
| `warn(message)` | 记录警告日志(黄色) |
|
|
102
|
+
| `info(message)` | 记录提示日志(蓝色) |
|
|
103
|
+
| `debug(message)` | 记录调试日志(灰色) |
|
|
104
|
+
|
|
105
|
+
每个方法接受字符串或字符串数组作为参数。
|
|
106
|
+
|
|
107
|
+
**页面 Console 拦截**
|
|
108
|
+
|
|
109
|
+
页面中的 `console.log`、`console.error`、`console.info`、`console.warn`、`console.debug` 调用会被自动拦截并转发到主进程 Logger,输出中包含来源窗口的 ID。
|
|
110
|
+
|
|
111
|
+
### 托盘图标
|
|
112
|
+
|
|
113
|
+
通过 `app.createTray()` 创建和管理系统托盘图标及右键菜单。
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { readFileSync } from 'fs';
|
|
117
|
+
|
|
118
|
+
const tray = app.createTray({
|
|
119
|
+
id: 'my-tray',
|
|
120
|
+
tooltip: '我的应用',
|
|
121
|
+
icon: {
|
|
122
|
+
data: readFileSync('./icon.png'),
|
|
123
|
+
width: 32,
|
|
124
|
+
height: 32,
|
|
125
|
+
},
|
|
126
|
+
menuItems: [
|
|
127
|
+
{ id: 'show', label: '显示窗口' },
|
|
128
|
+
{ id: 'quit', label: '退出' },
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`icon` 字段接受 `TrayIconImage` 对象:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
interface TrayIconImage {
|
|
137
|
+
data: Buffer; // 图片二进制数据
|
|
138
|
+
width?: number; // 可选宽度
|
|
139
|
+
height?: number; // 可选高度
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**方法**
|
|
144
|
+
|
|
145
|
+
| 方法 | 描述 |
|
|
146
|
+
|---------------------------------------------|--------------------|
|
|
147
|
+
| `addMenuItem(item)` | 添加菜单项 |
|
|
148
|
+
| `removeMenuItem(id)` | 按 ID 删除菜单项 |
|
|
149
|
+
| `setMenuItems(items)` | 替换所有菜单项 |
|
|
150
|
+
| `setIcon(icon, width?, height?)` | 更改托盘图标(原始像素数据 `Uint8Array` / `number[]`) |
|
|
151
|
+
| `setTooltip(tooltip)` | 更改悬停提示文本 |
|
|
152
|
+
| `on(event, callback)` | 监听托盘事件(如 `'click'`) |
|
|
153
|
+
| `show()` | 显示托盘图标 |
|
|
154
|
+
| `hide()` | 隐藏托盘图标 |
|
|
155
|
+
| `destroy()` | 销毁托盘图标 |
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
tray.on('click', () => {
|
|
159
|
+
console.log('托盘被点击');
|
|
160
|
+
});
|
|
161
|
+
tray.addMenuItem({ id: 'about', label: '关于' });
|
|
162
|
+
tray.setTooltip('Nexfep 应用');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 桌面通知
|
|
166
|
+
|
|
167
|
+
通过 `app.utils.notify()` 发送桌面通知。
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const notification = app.utils.notify('标题', '通知内容');
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**参数**
|
|
174
|
+
|
|
175
|
+
- `title` — 通知标题
|
|
176
|
+
- `body`(可选)— 通知正文
|
|
177
|
+
|
|
178
|
+
### 窗口池
|
|
179
|
+
|
|
180
|
+
`WindowPool` 是框架的核心管理类,负责窗口的创建和回收。
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const pool = app.windows;
|
|
184
|
+
```
|
|
60
185
|
|
|
61
186
|
### 窗口创建
|
|
62
187
|
|
|
@@ -78,6 +203,7 @@ window.maximize();
|
|
|
78
203
|
window.minimize();
|
|
79
204
|
window.close();
|
|
80
205
|
window.setTitle('新标题');
|
|
206
|
+
window.setSize(800, 600);
|
|
81
207
|
window.openDevTools();
|
|
82
208
|
```
|
|
83
209
|
|
|
@@ -183,9 +309,9 @@ const value = await window.getGlobal('hello');
|
|
|
183
309
|
|
|
184
310
|
- `name` — 全局变量名称
|
|
185
311
|
|
|
186
|
-
#### 全局变量Map
|
|
312
|
+
#### 全局变量 Map
|
|
187
313
|
|
|
188
|
-
在主进程中通过 `
|
|
314
|
+
在主进程中通过 `pool.global` 获取一个包含所有全局变量的 `Map<string, any>` 对象,可对其进行设置、获取等操作:
|
|
189
315
|
|
|
190
316
|
```typescript
|
|
191
317
|
const globals = pool.global;
|
|
@@ -336,41 +462,94 @@ if (window.isNexfepLoadDone) {
|
|
|
336
462
|
}
|
|
337
463
|
```
|
|
338
464
|
|
|
465
|
+
## CLI
|
|
466
|
+
|
|
467
|
+
Nexfep 提供命令行工具,用于将应用打包为独立可执行文件。
|
|
468
|
+
|
|
469
|
+
### 用法
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
npx nexfep build [options]
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### 选项
|
|
476
|
+
|
|
477
|
+
| 选项 | 说明 |
|
|
478
|
+
|------|------|
|
|
479
|
+
| `-n, --name <name>` | 应用名称(默认:来自 package.json) |
|
|
480
|
+
| `-e, --entry <file>` | 入口文件路径(默认:来自 package.json main) |
|
|
481
|
+
| `-o, --output <dir>` | 输出目录(默认:dist) |
|
|
482
|
+
| `-i, --ignore <pattern>` | 要忽略的文件或目录(可多次使用) |
|
|
483
|
+
| `-c, --console` | 在 Windows 上显示控制台窗口(默认:false) |
|
|
484
|
+
| `-r, --reinstall` | 构建前仅重新安装生产依赖 |
|
|
485
|
+
| `-s, --skip-clean` | 跳过清理旧的构建文件 |
|
|
486
|
+
| `-u, --upx <level>` | 使用 UPX 压缩可执行文件,级别 0-9(默认:0) |
|
|
487
|
+
|
|
488
|
+
### 示例
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
# 使用 package.json 默认配置构建
|
|
492
|
+
nexfep build
|
|
493
|
+
|
|
494
|
+
# 设置自定义应用名称和入口文件
|
|
495
|
+
nexfep build -n my-app -e ./src/index.js
|
|
496
|
+
|
|
497
|
+
# 设置自定义输出目录
|
|
498
|
+
nexfep build -o ./build
|
|
499
|
+
|
|
500
|
+
# 忽略多个模式
|
|
501
|
+
nexfep build -i node_modules -i test -i temp
|
|
502
|
+
|
|
503
|
+
# 使用 UPX 压缩构建
|
|
504
|
+
nexfep build -u 7
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
该命令使用 [nexfpack](https://github.com/nexfteam/Nexfpack) 将应用打包为独立可执行文件。
|
|
508
|
+
|
|
339
509
|
## API
|
|
340
510
|
|
|
511
|
+
### Application
|
|
512
|
+
|
|
513
|
+
| 方法/属性 | 参数 | 返回值 | 说明 |
|
|
514
|
+
|-----------|------|--------|------|
|
|
515
|
+
| `constructor(options?)` | `{ WindowsWebview2UserDataFolder?, LogFilePath? }` | Application | 创建应用实例 |
|
|
516
|
+
| `windows` | / | WindowPool | 窗口池实例 |
|
|
517
|
+
| `utils` | / | \_\_Utils | 工具方法(通知) |
|
|
518
|
+
| `logger` | / | Logger | 日志实例 |
|
|
519
|
+
| `createTray(options)` | 见 Tray 章节 | Tray | 创建系统托盘图标 |
|
|
520
|
+
| `exit()` | 无 | void | 退出应用 |
|
|
521
|
+
|
|
341
522
|
### WindowPool
|
|
342
523
|
|
|
343
|
-
| 方法/属性
|
|
344
|
-
|
|
345
|
-
| `
|
|
346
|
-
| `
|
|
347
|
-
| `
|
|
348
|
-
| `
|
|
349
|
-
| `
|
|
350
|
-
| `
|
|
351
|
-
| `closePool()` | 无 | Promise\<void> | 关闭窗口池,退出应用 |
|
|
352
|
-
| `mainloop()` | 无 | void | 启动应用主循环,阻塞直到应用退出 |
|
|
353
|
-
| `onCustomMessage` | `(window: Window, data: string) => void` | 无 | 自定义消息回调函数,当收到页面发来的自定义消息时触发 |
|
|
524
|
+
| 方法/属性 | 参数 | 返回值 | 说明 |
|
|
525
|
+
|-----------|------|--------|------|
|
|
526
|
+
| `createWindow(isShow?, isDecorated?)` | `isShow`: boolean(默认 true), `isDecorated`: boolean(默认 true) | Promise\<Window> | 创建并获取一个窗口 |
|
|
527
|
+
| `handle(event, callback)` | `event`: string, `callback`: (data: any) => any | 无 | 监听指定事件 |
|
|
528
|
+
| `unhandle(event, callback)` | `event`: string, `callback`: (data: any) => any | 无 | 取消监听指定事件 |
|
|
529
|
+
| `global` | / | Map\<string, any> | 全局变量 Map |
|
|
530
|
+
| `closeWindow(window)` | `window`: Window | Promise\<void> | 关闭指定窗口并回收至池中 |
|
|
531
|
+
| `onCustomMessage` | `(window: Window, data: string) => void` | 无 | 自定义消息回调函数 |
|
|
354
532
|
|
|
355
533
|
### Window
|
|
356
534
|
|
|
357
|
-
| 方法/属性
|
|
358
|
-
|
|
359
|
-
| `loadURL(url)`
|
|
360
|
-
| `loadHTML(html)`
|
|
361
|
-
| `show()`
|
|
362
|
-
| `hide()`
|
|
363
|
-
| `maximize()`
|
|
364
|
-
| `unMaximize()`
|
|
365
|
-
| `minimize()`
|
|
366
|
-
| `unMinimize()`
|
|
367
|
-
| `close()`
|
|
368
|
-
| `setTitle(title)`
|
|
369
|
-
| `setDecorated(isDecorated)` | `isDecorated`: boolean
|
|
370
|
-
| `resizable(resizable)`
|
|
371
|
-
| `
|
|
372
|
-
| `
|
|
373
|
-
| `
|
|
535
|
+
| 方法/属性 | 参数 | 返回值 | 说明 |
|
|
536
|
+
|-----------|------|--------|------|
|
|
537
|
+
| `loadURL(url)` | `url`: string — 要加载的网页地址 | Promise\<void> | 加载指定 URL |
|
|
538
|
+
| `loadHTML(html)` | `html`: string — HTML 字符串 | Promise\<void> | 加载指定 HTML 内容 |
|
|
539
|
+
| `show()` | 无 | void | 显示窗口 |
|
|
540
|
+
| `hide()` | 无 | void | 隐藏窗口 |
|
|
541
|
+
| `maximize()` | 无 | void | 最大化窗口 |
|
|
542
|
+
| `unMaximize()` | 无 | void | 还原窗口(取消最大化) |
|
|
543
|
+
| `minimize()` | 无 | void | 最小化窗口 |
|
|
544
|
+
| `unMinimize()` | 无 | void | 还原窗口(取消最小化) |
|
|
545
|
+
| `close()` | 无 | void | 关闭窗口并回收至池中 |
|
|
546
|
+
| `setTitle(title)` | `title`: string | void | 设置窗口标题 |
|
|
547
|
+
| `setDecorated(isDecorated)` | `isDecorated`: boolean | void | 设置窗口是否带边框和标题栏 |
|
|
548
|
+
| `resizable(resizable)` | `resizable`: boolean | void | 设置窗口是否可调整大小 |
|
|
549
|
+
| `setSize(width, height)` | `width`: number, `height`: number | void | 设置窗口尺寸(像素) |
|
|
550
|
+
| `openDevTools()` | 无 | void | 打开开发者工具 |
|
|
551
|
+
| `closeDevTools()` | 无 | void | 关闭开发者工具 |
|
|
552
|
+
| `id` | 无 | number | 窗口唯一标识,自增编号 |
|
|
374
553
|
|
|
375
554
|
## 开发
|
|
376
555
|
|
package/README.md
CHANGED
|
@@ -22,6 +22,10 @@ The framework uses a window pool management mechanism, supporting multi-window a
|
|
|
22
22
|
- **IPC Communication** — Supports bidirectional message communication between the main process and WebView, via injected functions
|
|
23
23
|
- **Window Control** — Provides complete window operation APIs including maximize, minimize, close, title setting, and developer tools
|
|
24
24
|
- **Drag Regions** — Built-in HTML attribute support for defining window drag regions (`nexfep-area-drag`, etc.)
|
|
25
|
+
- **System Tray** — Create and manage system tray icons with context menus
|
|
26
|
+
- **Desktop Notifications** — Send desktop notifications via `app.utils.notify()`
|
|
27
|
+
- **Logging System** — Built-in logger with file output and colored console support, with automatic interception of page console messages
|
|
28
|
+
- **CLI Build Tool** — Package your application into a standalone executable via `nexfep build`
|
|
25
29
|
- **TypeScript Support** — Complete type definitions for excellent development experience
|
|
26
30
|
|
|
27
31
|
## Installation
|
|
@@ -33,30 +37,151 @@ pnpm add nexfep
|
|
|
33
37
|
## Quick Start
|
|
34
38
|
|
|
35
39
|
```typescript
|
|
36
|
-
import {
|
|
40
|
+
import { Application } from 'nexfep';
|
|
37
41
|
|
|
38
|
-
const
|
|
42
|
+
const app = new Application();
|
|
39
43
|
|
|
40
|
-
const window = await
|
|
44
|
+
const window = await app.windows.createWindow(true, false);
|
|
41
45
|
|
|
42
46
|
await window.loadHTML('<h1 nexfep-area-drag>Hello Nexfep!</h1>');
|
|
43
|
-
|
|
44
|
-
pool.mainloop();
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
## Usage Guide
|
|
48
50
|
|
|
49
|
-
###
|
|
51
|
+
### Application
|
|
50
52
|
|
|
51
|
-
`
|
|
53
|
+
`Application` is the main entry point of the framework, responsible for managing the application lifecycle and providing access to windows, system tray, and logger.
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { Application } from 'nexfep';
|
|
57
|
+
|
|
58
|
+
const app = new Application();
|
|
59
|
+
// or with custom WebView2 user data directory (Windows only)
|
|
60
|
+
const app = new Application({ WindowsWebview2UserDataFolder: 'C:\\custom\\webview2-data' });
|
|
61
|
+
// or with log file path
|
|
62
|
+
const app = new Application({ LogFilePath: './app.log' });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Constructor Options**
|
|
66
|
+
|
|
67
|
+
| Option | Type | Default | Description |
|
|
68
|
+
|--------|------|---------|-------------|
|
|
69
|
+
| `WindowsWebview2UserDataFolder` | `string` (optional) | `%LOCALAPPDATA%\NexfepDevelopment.webview2-data` | WebView2 user data directory (Windows only) |
|
|
70
|
+
| `LogFilePath` | `string` (optional) | none | File path for log output |
|
|
71
|
+
|
|
72
|
+
**Properties**
|
|
73
|
+
|
|
74
|
+
- `windows` — The `WindowPool` instance for managing browser windows
|
|
75
|
+
- `utils` — Utility methods (e.g., desktop notifications)
|
|
76
|
+
- `logger` — The `Logger` instance for logging
|
|
77
|
+
|
|
78
|
+
**Methods**
|
|
79
|
+
|
|
80
|
+
- `createTray(options)` — Create a system tray icon with context menu
|
|
81
|
+
- `exit()` — Exit the application
|
|
82
|
+
|
|
83
|
+
### Logger
|
|
84
|
+
|
|
85
|
+
The logger supports both file output and colored console output. It can be accessed via `app.logger`.
|
|
52
86
|
|
|
53
87
|
```typescript
|
|
54
|
-
|
|
88
|
+
app.logger.log('Hello World');
|
|
89
|
+
app.logger.error('An error occurred');
|
|
90
|
+
app.logger.warn('Warning message');
|
|
91
|
+
app.logger.info('Info message');
|
|
92
|
+
app.logger.debug('Debug message');
|
|
55
93
|
```
|
|
56
94
|
|
|
57
|
-
**
|
|
95
|
+
**Methods**
|
|
96
|
+
|
|
97
|
+
| Method | Description |
|
|
98
|
+
|--------|-------------|
|
|
99
|
+
| `log(message)` | Log a message |
|
|
100
|
+
| `error(message)` | Log an error message (red) |
|
|
101
|
+
| `warn(message)` | Log a warning message (yellow) |
|
|
102
|
+
| `info(message)` | Log an info message (blue) |
|
|
103
|
+
| `debug(message)` | Log a debug message (gray) |
|
|
104
|
+
|
|
105
|
+
Each method accepts either a string or an array of strings.
|
|
106
|
+
|
|
107
|
+
**Page Console Interception**
|
|
58
108
|
|
|
59
|
-
|
|
109
|
+
Console calls (`console.log`, `console.error`, `console.info`, `console.warn`, `console.debug`) in the page are automatically intercepted and forwarded to the main process logger, with the source window ID included in the output.
|
|
110
|
+
|
|
111
|
+
### Tray
|
|
112
|
+
|
|
113
|
+
Create and manage system tray icons with context menus via `app.createTray()`.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { readFileSync } from 'fs';
|
|
117
|
+
|
|
118
|
+
const tray = app.createTray({
|
|
119
|
+
id: 'my-tray',
|
|
120
|
+
tooltip: 'My App',
|
|
121
|
+
icon: {
|
|
122
|
+
data: readFileSync('./icon.png'),
|
|
123
|
+
width: 32,
|
|
124
|
+
height: 32,
|
|
125
|
+
},
|
|
126
|
+
menuItems: [
|
|
127
|
+
{ id: 'show', label: 'Show Window' },
|
|
128
|
+
{ id: 'quit', label: 'Quit' },
|
|
129
|
+
],
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The `icon` field accepts a `TrayIconImage` object:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
interface TrayIconImage {
|
|
137
|
+
data: Buffer; // Image binary data
|
|
138
|
+
width?: number; // Optional width
|
|
139
|
+
height?: number; // Optional height
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Methods**
|
|
144
|
+
|
|
145
|
+
| Method | Description |
|
|
146
|
+
|---------------------------------------------|-------------------------------------|
|
|
147
|
+
| `addMenuItem(item)` | Add a menu item |
|
|
148
|
+
| `removeMenuItem(id)` | Remove a menu item by ID |
|
|
149
|
+
| `setMenuItems(items)` | Replace all menu items |
|
|
150
|
+
| `setIcon(icon, width?, height?)` | Change the tray icon (raw pixel data as `Uint8Array` / `number[]`) |
|
|
151
|
+
| `setTooltip(tooltip)` | Change the tooltip text |
|
|
152
|
+
| `on(event, callback)` | Listen for tray events (e.g. `'click'`) |
|
|
153
|
+
| `show()` | Show the tray icon |
|
|
154
|
+
| `hide()` | Hide the tray icon |
|
|
155
|
+
| `destroy()` | Destroy the tray icon |
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
tray.on('click', () => {
|
|
159
|
+
console.log('Tray clicked');
|
|
160
|
+
});
|
|
161
|
+
tray.addMenuItem({ id: 'about', label: 'About' });
|
|
162
|
+
tray.setTooltip('Nexfep App');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Notifications
|
|
166
|
+
|
|
167
|
+
Send desktop notifications via `app.utils.notify()`.
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
const notification = app.utils.notify('Title', 'Notification body');
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Parameters**
|
|
174
|
+
|
|
175
|
+
- `title` — Notification title
|
|
176
|
+
- `body` (optional) — Notification body text
|
|
177
|
+
|
|
178
|
+
### Window Pool
|
|
179
|
+
|
|
180
|
+
`WindowPool` is the core management class of the framework, responsible for window creation and recycling.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
const pool = app.windows;
|
|
184
|
+
```
|
|
60
185
|
|
|
61
186
|
### Window Creation
|
|
62
187
|
|
|
@@ -78,6 +203,7 @@ window.maximize();
|
|
|
78
203
|
window.minimize();
|
|
79
204
|
window.close();
|
|
80
205
|
window.setTitle('New Title');
|
|
206
|
+
window.setSize(800, 600);
|
|
81
207
|
window.openDevTools();
|
|
82
208
|
```
|
|
83
209
|
|
|
@@ -185,7 +311,7 @@ const value = await window.getGlobal('hello');
|
|
|
185
311
|
|
|
186
312
|
#### Global Variable Map
|
|
187
313
|
|
|
188
|
-
Get a `Map<string, any>` containing all global variables via `
|
|
314
|
+
Get a `Map<string, any>` containing all global variables via `pool.global` in the main process, which supports operations like set and get:
|
|
189
315
|
|
|
190
316
|
```typescript
|
|
191
317
|
const globals = pool.global;
|
|
@@ -336,41 +462,94 @@ if (window.isNexfepLoadDone) {
|
|
|
336
462
|
}
|
|
337
463
|
```
|
|
338
464
|
|
|
465
|
+
## CLI
|
|
466
|
+
|
|
467
|
+
Nexfep provides a command-line tool for building applications into standalone executables.
|
|
468
|
+
|
|
469
|
+
### Usage
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
npx nexfep build [options]
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Options
|
|
476
|
+
|
|
477
|
+
| Option | Description |
|
|
478
|
+
|--------|-------------|
|
|
479
|
+
| `-n, --name <name>` | Application name (default: from package.json) |
|
|
480
|
+
| `-e, --entry <file>` | Entry file path (default: from package.json main) |
|
|
481
|
+
| `-o, --output <dir>` | Output directory (default: dist) |
|
|
482
|
+
| `-i, --ignore <pattern>` | Files or directories to ignore (can be used multiple times) |
|
|
483
|
+
| `-c, --console` | Show console window on Windows (default: false) |
|
|
484
|
+
| `-r, --reinstall` | Reinstall production dependencies only before building |
|
|
485
|
+
| `-s, --skip-clean` | Skip cleaning old build files before building |
|
|
486
|
+
| `-u, --upx <level>` | Use UPX to compress the executable, level 0-9 (default: 0) |
|
|
487
|
+
|
|
488
|
+
### Examples
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
# Build using defaults from package.json
|
|
492
|
+
nexfep build
|
|
493
|
+
|
|
494
|
+
# Set custom application name and entry file
|
|
495
|
+
nexfep build -n my-app -e ./src/index.js
|
|
496
|
+
|
|
497
|
+
# Set custom output directory
|
|
498
|
+
nexfep build -o ./build
|
|
499
|
+
|
|
500
|
+
# Ignore multiple patterns
|
|
501
|
+
nexfep build -i node_modules -i test -i temp
|
|
502
|
+
|
|
503
|
+
# Build with UPX compression
|
|
504
|
+
nexfep build -u 7
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
This command uses [nexfpack](https://github.com/nexfteam/Nexfpack) to package your application into a standalone executable.
|
|
508
|
+
|
|
339
509
|
## API
|
|
340
510
|
|
|
511
|
+
### Application
|
|
512
|
+
|
|
513
|
+
| Method/Property | Parameters | Return Value | Description |
|
|
514
|
+
|----------------|-----------|--------------|-------------|
|
|
515
|
+
| `constructor(options?)` | `{ WindowsWebview2UserDataFolder?, LogFilePath? }` | Application | Creates the application instance |
|
|
516
|
+
| `windows` | / | WindowPool | The window pool instance |
|
|
517
|
+
| `utils` | / | \_\_Utils | Utility methods (notifications) |
|
|
518
|
+
| `logger` | / | Logger | The logger instance |
|
|
519
|
+
| `createTray(options)` | see Tray section | Tray | Creates a system tray icon |
|
|
520
|
+
| `exit()` | None | void | Exits the application |
|
|
521
|
+
|
|
341
522
|
### WindowPool
|
|
342
523
|
|
|
343
|
-
| Method/Property
|
|
344
|
-
|
|
345
|
-
| `
|
|
346
|
-
| `
|
|
347
|
-
| `
|
|
348
|
-
| `
|
|
349
|
-
| `
|
|
350
|
-
| `
|
|
351
|
-
| `closePool()` | None | Promise\<void> | Closes the window pool and exits the application |
|
|
352
|
-
| `mainloop()` | None | void | Starts the application main loop, blocking until the application exits |
|
|
353
|
-
| `onCustomMessage` | `(window: Window, data: string) => void` | None | Custom message callback, triggered when receiving custom messages from pages |
|
|
524
|
+
| Method/Property | Parameters | Return Value | Description |
|
|
525
|
+
|----------------|-----------|--------------|-------------|
|
|
526
|
+
| `createWindow(isShow?, isDecorated?)` | `isShow`: boolean (default true), `isDecorated`: boolean (default true) | Promise\<Window> | Creates and returns a window |
|
|
527
|
+
| `handle(event, callback)` | `event`: string, `callback`: (data: any) => any | None | Listens for the specified event |
|
|
528
|
+
| `unhandle(event, callback)` | `event`: string, `callback`: (data: any) => any | None | Removes the specified event listener |
|
|
529
|
+
| `global` | / | Map\<string, any> | A global variable map |
|
|
530
|
+
| `closeWindow(window)` | `window`: Window | Promise\<void> | Closes the specified window and returns it to the pool |
|
|
531
|
+
| `onCustomMessage` | `(window: Window, data: string) => void` | None | Custom message callback |
|
|
354
532
|
|
|
355
533
|
### Window
|
|
356
534
|
|
|
357
|
-
| Method/Property
|
|
358
|
-
|
|
359
|
-
| `loadURL(url)`
|
|
360
|
-
| `loadHTML(html)`
|
|
361
|
-
| `show()`
|
|
362
|
-
| `hide()`
|
|
363
|
-
| `maximize()`
|
|
364
|
-
| `unMaximize()`
|
|
365
|
-
| `minimize()`
|
|
366
|
-
| `unMinimize()`
|
|
367
|
-
| `close()`
|
|
368
|
-
| `setTitle(title)`
|
|
369
|
-
| `setDecorated(isDecorated)`
|
|
370
|
-
| `resizable(resizable)`
|
|
371
|
-
| `
|
|
372
|
-
| `
|
|
373
|
-
| `
|
|
535
|
+
| Method/Property | Parameters | Return Value | Description |
|
|
536
|
+
|----------------|-----------|--------------|-------------|
|
|
537
|
+
| `loadURL(url)` | `url`: string — URL to load | Promise\<void> | Loads the specified URL |
|
|
538
|
+
| `loadHTML(html)` | `html`: string — HTML string | Promise\<void> | Loads the specified HTML content |
|
|
539
|
+
| `show()` | None | void | Shows the window |
|
|
540
|
+
| `hide()` | None | void | Hides the window |
|
|
541
|
+
| `maximize()` | None | void | Maximizes the window |
|
|
542
|
+
| `unMaximize()` | None | void | Restores the window (cancels maximize) |
|
|
543
|
+
| `minimize()` | None | void | Minimizes the window |
|
|
544
|
+
| `unMinimize()` | None | void | Restores the window (cancels minimize) |
|
|
545
|
+
| `close()` | None | void | Closes the window and returns to pool |
|
|
546
|
+
| `setTitle(title)` | `title`: string | void | Sets the window title |
|
|
547
|
+
| `setDecorated(isDecorated)` | `isDecorated`: boolean | void | Sets whether the window has borders and title bar |
|
|
548
|
+
| `resizable(resizable)` | `resizable`: boolean | void | Sets whether the window is resizable |
|
|
549
|
+
| `setSize(width, height)` | `width`: number, `height`: number | void | Sets the window size in pixels |
|
|
550
|
+
| `openDevTools()` | None | void | Opens developer tools |
|
|
551
|
+
| `closeDevTools()` | None | void | Closes developer tools |
|
|
552
|
+
| `id` | None | number | Unique window identifier, auto-incrementing |
|
|
374
553
|
|
|
375
554
|
## Development
|
|
376
555
|
|
|
@@ -381,4 +560,4 @@ pnpm run compile
|
|
|
381
560
|
|
|
382
561
|
## License
|
|
383
562
|
|
|
384
|
-
MIT License
|
|
563
|
+
MIT License
|
package/cli/index.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import build from './build.mjs';
|
|
5
|
+
import help from './help.mjs';
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
if(args.length === 0) {
|
|
8
|
+
help();
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
if(args[0] === 'help') {
|
|
12
|
+
if(args.length > 1) {
|
|
13
|
+
help(args[1]);
|
|
14
|
+
} else {
|
|
15
|
+
help();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
if(args[0] === 'build') {
|
|
19
|
+
build(args.slice(1));
|
|
20
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './src/
|
|
1
|
+
export * from './src/Application.js';
|
package/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './src/
|
|
1
|
+
export * from './src/Application.js';
|
package/package.json
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/nexfteam/Nexfep/issues"
|
|
8
8
|
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"nexfep": "cli/index.mjs"
|
|
11
|
+
},
|
|
9
12
|
"keywords": [
|
|
10
13
|
"desktop",
|
|
11
14
|
"application",
|
|
@@ -14,7 +17,7 @@
|
|
|
14
17
|
"webview",
|
|
15
18
|
"typescript"
|
|
16
19
|
],
|
|
17
|
-
"version": "0.
|
|
20
|
+
"version": "0.3.0",
|
|
18
21
|
"type": "module",
|
|
19
22
|
"main": "./index.js",
|
|
20
23
|
"scripts": {
|
|
@@ -33,10 +36,14 @@
|
|
|
33
36
|
"LICENSE",
|
|
34
37
|
"package.json",
|
|
35
38
|
"src/WindowManager.js",
|
|
36
|
-
"src/WindowManager.d.ts"
|
|
39
|
+
"src/WindowManager.d.ts",
|
|
40
|
+
"src/Application.js",
|
|
41
|
+
"src/Application.d.ts",
|
|
42
|
+
"src/Tray.js",
|
|
43
|
+
"src/Tray.d.ts"
|
|
37
44
|
],
|
|
38
45
|
"dependencies": {
|
|
39
|
-
"@webviewjs/webview": "0.
|
|
46
|
+
"@webviewjs/webview": "0.4.0"
|
|
40
47
|
},
|
|
41
48
|
"devDependencies": {
|
|
42
49
|
"@types/node": "^22.0.0",
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Application as WebviewApplication, TrayIconImage, Notification } from '@webviewjs/webview';
|
|
2
|
+
import { WindowPool } from './WindowManager.js';
|
|
3
|
+
import { Tray } from './Tray.js';
|
|
4
|
+
import { Logger } from './Logger.js';
|
|
5
|
+
declare class __Utils {
|
|
6
|
+
app: WebviewApplication;
|
|
7
|
+
constructor(app: WebviewApplication);
|
|
8
|
+
notify(title: string, body?: string): Notification;
|
|
9
|
+
}
|
|
10
|
+
declare class Application {
|
|
11
|
+
app: WebviewApplication;
|
|
12
|
+
windows: WindowPool;
|
|
13
|
+
logger: Logger;
|
|
14
|
+
utils: __Utils;
|
|
15
|
+
constructor(options: {
|
|
16
|
+
WindowsWebview2UserDataFolder?: string;
|
|
17
|
+
LogFilePath?: string;
|
|
18
|
+
});
|
|
19
|
+
createTray(options: {
|
|
20
|
+
id: string;
|
|
21
|
+
tooltip: string;
|
|
22
|
+
icon: TrayIconImage | undefined;
|
|
23
|
+
menuItems: Array<{
|
|
24
|
+
id: string;
|
|
25
|
+
label: string;
|
|
26
|
+
}>;
|
|
27
|
+
}): Tray;
|
|
28
|
+
exit(): void;
|
|
29
|
+
}
|
|
30
|
+
export { Application };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Application as WebviewApplication, Notification } from '@webviewjs/webview';
|
|
2
|
+
import { WindowPool } from './WindowManager.js';
|
|
3
|
+
import { Tray } from './Tray.js';
|
|
4
|
+
import { Logger } from './Logger.js';
|
|
5
|
+
class __Utils {
|
|
6
|
+
app;
|
|
7
|
+
constructor(app) {
|
|
8
|
+
this.app = app;
|
|
9
|
+
}
|
|
10
|
+
notify(title, body) {
|
|
11
|
+
const notification = new Notification(title, { body });
|
|
12
|
+
return notification;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class Application {
|
|
16
|
+
app;
|
|
17
|
+
windows;
|
|
18
|
+
logger;
|
|
19
|
+
utils;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
this.app = new WebviewApplication();
|
|
22
|
+
this.utils = new __Utils(this.app);
|
|
23
|
+
this.logger = new Logger(options.LogFilePath);
|
|
24
|
+
const poolOptions = { WindowsWebview2UserDataFolder: options.WindowsWebview2UserDataFolder, logger: this.logger };
|
|
25
|
+
this.windows = new WindowPool(this.app, poolOptions);
|
|
26
|
+
this.app.whenReady();
|
|
27
|
+
}
|
|
28
|
+
createTray(options) {
|
|
29
|
+
return new Tray(this.app, options);
|
|
30
|
+
}
|
|
31
|
+
exit() {
|
|
32
|
+
this.app.exit();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export { Application };
|
package/src/Tray.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Application, TrayIcon, TrayIconImage } from '@webviewjs/webview';
|
|
2
|
+
declare class Tray {
|
|
3
|
+
app: Application;
|
|
4
|
+
tray: TrayIcon;
|
|
5
|
+
menuItems: Array<{
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}>;
|
|
9
|
+
destroyed: boolean;
|
|
10
|
+
constructor(app: Application, options: {
|
|
11
|
+
id: string;
|
|
12
|
+
tooltip: string;
|
|
13
|
+
icon: TrayIconImage | undefined;
|
|
14
|
+
menuItems: Array<{
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
}>;
|
|
18
|
+
});
|
|
19
|
+
__ErrorWhenDestroyed(): void;
|
|
20
|
+
addMenuItem(item: {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
}): void;
|
|
24
|
+
removeMenuItem(id: string): void;
|
|
25
|
+
setMenuItems(items: Array<{
|
|
26
|
+
id: string;
|
|
27
|
+
label: string;
|
|
28
|
+
}>): void;
|
|
29
|
+
setIcon(icon: Uint8Array<ArrayBuffer | SharedArrayBuffer> | number[], width?: number | null | undefined, height?: number | null | undefined): void;
|
|
30
|
+
on(event: string, callback: (event: any) => void): void;
|
|
31
|
+
setTooltip(tooltip: string): void;
|
|
32
|
+
hide(): void;
|
|
33
|
+
show(): void;
|
|
34
|
+
destroy(): void;
|
|
35
|
+
}
|
|
36
|
+
export { Tray };
|
package/src/Tray.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
class Tray {
|
|
2
|
+
app;
|
|
3
|
+
tray;
|
|
4
|
+
menuItems;
|
|
5
|
+
destroyed;
|
|
6
|
+
constructor(app, options) {
|
|
7
|
+
this.menuItems = options.menuItems;
|
|
8
|
+
this.destroyed = false;
|
|
9
|
+
this.app = app;
|
|
10
|
+
this.tray = app.createTrayIcon({
|
|
11
|
+
id: options.id,
|
|
12
|
+
tooltip: options.tooltip,
|
|
13
|
+
icon: options.icon,
|
|
14
|
+
menu: {
|
|
15
|
+
items: this.menuItems
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
__ErrorWhenDestroyed() {
|
|
20
|
+
if (this.destroyed) {
|
|
21
|
+
throw new Error('Tray has been destroyed');
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
addMenuItem(item) {
|
|
25
|
+
this.__ErrorWhenDestroyed();
|
|
26
|
+
this.menuItems.push(item);
|
|
27
|
+
this.tray.setMenu({ items: this.menuItems });
|
|
28
|
+
}
|
|
29
|
+
removeMenuItem(id) {
|
|
30
|
+
this.__ErrorWhenDestroyed();
|
|
31
|
+
this.menuItems = this.menuItems.filter(item => item.id !== id);
|
|
32
|
+
this.tray.setMenu({ items: this.menuItems });
|
|
33
|
+
}
|
|
34
|
+
setMenuItems(items) {
|
|
35
|
+
this.__ErrorWhenDestroyed();
|
|
36
|
+
this.menuItems = items;
|
|
37
|
+
this.tray.setMenu({ items: this.menuItems });
|
|
38
|
+
}
|
|
39
|
+
setIcon(icon, width, height) {
|
|
40
|
+
this.__ErrorWhenDestroyed();
|
|
41
|
+
this.tray.setIcon(icon, width, height);
|
|
42
|
+
}
|
|
43
|
+
on(event, callback) {
|
|
44
|
+
this.__ErrorWhenDestroyed();
|
|
45
|
+
this.tray.on(event, callback);
|
|
46
|
+
}
|
|
47
|
+
setTooltip(tooltip) {
|
|
48
|
+
this.__ErrorWhenDestroyed();
|
|
49
|
+
this.tray.setTooltip(tooltip);
|
|
50
|
+
}
|
|
51
|
+
hide() {
|
|
52
|
+
this.__ErrorWhenDestroyed();
|
|
53
|
+
this.tray.setVisible(false);
|
|
54
|
+
}
|
|
55
|
+
show() {
|
|
56
|
+
this.__ErrorWhenDestroyed();
|
|
57
|
+
this.tray.setVisible(true);
|
|
58
|
+
}
|
|
59
|
+
destroy() {
|
|
60
|
+
this.__ErrorWhenDestroyed();
|
|
61
|
+
this.tray.dispose();
|
|
62
|
+
this.destroyed = true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export { Tray };
|
package/src/WindowManager.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { BrowserWindow, Webview } from "@webviewjs/webview";
|
|
1
|
+
import { Application, BrowserWindow, Webview } from "@webviewjs/webview";
|
|
2
|
+
import { Logger } from "./Logger.js";
|
|
2
3
|
declare class Window {
|
|
3
4
|
window: BrowserWindow;
|
|
4
5
|
webview: Webview;
|
|
@@ -17,6 +18,7 @@ declare class Window {
|
|
|
17
18
|
unMinimize(): void;
|
|
18
19
|
openDevTools(): void;
|
|
19
20
|
closeDevTools(): void;
|
|
21
|
+
setSize(width: number, height: number): void;
|
|
20
22
|
hide(): void;
|
|
21
23
|
show(): void;
|
|
22
24
|
close(): void;
|
|
@@ -25,22 +27,24 @@ declare class Window {
|
|
|
25
27
|
}
|
|
26
28
|
declare class WindowPool {
|
|
27
29
|
onCustomMessage: (window: Window, data: string) => void;
|
|
28
|
-
|
|
30
|
+
app: Application;
|
|
31
|
+
logger: Logger;
|
|
29
32
|
private windows;
|
|
30
33
|
private handlers;
|
|
31
34
|
private injectCount;
|
|
32
35
|
private windowCount;
|
|
33
36
|
private freeWindowCount;
|
|
34
37
|
global: Map<string, any>;
|
|
35
|
-
constructor(
|
|
38
|
+
constructor(app: Application, options: {
|
|
39
|
+
WindowsWebview2UserDataFolder?: string;
|
|
40
|
+
logger?: Logger;
|
|
41
|
+
});
|
|
36
42
|
__injectCode(window: Window, code: string): Promise<void>;
|
|
37
43
|
__injectControlFunctions(windowObj: Window): Promise<void>;
|
|
38
44
|
__createNewWindowObj(): Promise<Window>;
|
|
39
45
|
createWindow(isShow?: boolean, isDecorated?: boolean): Promise<Window>;
|
|
40
46
|
closeWindow(window: Window): Promise<void>;
|
|
41
|
-
closePool(): Promise<void>;
|
|
42
47
|
handle(event: string, callback: (data: any) => any): Promise<void>;
|
|
43
48
|
unhandle(event: string, callback: (data: any) => any): Promise<void>;
|
|
44
|
-
mainloop(): void;
|
|
45
49
|
}
|
|
46
50
|
export { WindowPool, Window };
|
package/src/WindowManager.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Logger } from "./Logger.js";
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs';
|
|
@@ -47,6 +47,9 @@ class Window {
|
|
|
47
47
|
closeDevTools() {
|
|
48
48
|
this.webview.closeDevtools();
|
|
49
49
|
}
|
|
50
|
+
setSize(width, height) {
|
|
51
|
+
this.window.setSize(width, height);
|
|
52
|
+
}
|
|
50
53
|
hide() {
|
|
51
54
|
this.window.hide();
|
|
52
55
|
this.isShow = false;
|
|
@@ -70,16 +73,17 @@ class Window {
|
|
|
70
73
|
class WindowPool {
|
|
71
74
|
onCustomMessage;
|
|
72
75
|
app;
|
|
76
|
+
logger;
|
|
73
77
|
windows;
|
|
74
78
|
handlers;
|
|
75
79
|
injectCount;
|
|
76
80
|
windowCount;
|
|
77
81
|
freeWindowCount;
|
|
78
82
|
global;
|
|
79
|
-
constructor(
|
|
83
|
+
constructor(app, options) {
|
|
80
84
|
if (os.platform() === 'win32') {
|
|
81
85
|
// 设置 WebView2 用户数据目录(仅 Windows 需要此配置)
|
|
82
|
-
const userDataDir = WindowsWebview2UserDataFolder;
|
|
86
|
+
const userDataDir = options.WindowsWebview2UserDataFolder || path.join(process.env.LOCALAPPDATA || os.homedir(), 'NexfepDevelopment.webview2-data');
|
|
83
87
|
if (!fs.existsSync(userDataDir)) {
|
|
84
88
|
fs.mkdirSync(userDataDir, { recursive: true });
|
|
85
89
|
}
|
|
@@ -89,12 +93,13 @@ class WindowPool {
|
|
|
89
93
|
console.log('Get Custom Message:', data, "from window", window.id);
|
|
90
94
|
};
|
|
91
95
|
this.handlers = new Map();
|
|
92
|
-
this.app =
|
|
96
|
+
this.app = app;
|
|
93
97
|
this.injectCount = 0;
|
|
94
98
|
this.windows = [];
|
|
95
99
|
this.windowCount = 0;
|
|
96
100
|
this.freeWindowCount = 0;
|
|
97
101
|
this.global = new Map();
|
|
102
|
+
this.logger = options.logger || new Logger();
|
|
98
103
|
}
|
|
99
104
|
async __injectCode(window, code) {
|
|
100
105
|
await window.webview.evaluateScript(code);
|
|
@@ -233,6 +238,37 @@ class WindowPool {
|
|
|
233
238
|
const MessageBody = { type: 'NexfepTell', to: to, message: message, data: data }
|
|
234
239
|
window.ipc.postMessage(JSON.stringify(MessageBody));
|
|
235
240
|
}
|
|
241
|
+
|
|
242
|
+
window.__originConsoleLog = console.log;
|
|
243
|
+
window.__originConsoleError = console.error;
|
|
244
|
+
window.__originConsoleInfo = console.info;
|
|
245
|
+
window.__originConsoleWarn = console.warn;
|
|
246
|
+
window.__originConsoleDebug = console.debug;
|
|
247
|
+
console.log = (...args) => {
|
|
248
|
+
const MessageBody = { type: 'NexfepConsoleLog', message: args }
|
|
249
|
+
window.ipc.postMessage(JSON.stringify(MessageBody));
|
|
250
|
+
window.__originConsoleLog(...args);
|
|
251
|
+
}
|
|
252
|
+
console.error = (...args) => {
|
|
253
|
+
const MessageBody = { type: 'NexfepConsoleError', message: args }
|
|
254
|
+
window.ipc.postMessage(JSON.stringify(MessageBody));
|
|
255
|
+
window.__originConsoleError(...args);
|
|
256
|
+
}
|
|
257
|
+
console.info = (...args) => {
|
|
258
|
+
const MessageBody = { type: 'NexfepConsoleInfo', message: args }
|
|
259
|
+
window.ipc.postMessage(JSON.stringify(MessageBody));
|
|
260
|
+
window.__originConsoleInfo(...args);
|
|
261
|
+
}
|
|
262
|
+
console.warn = (...args) => {
|
|
263
|
+
const MessageBody = { type: 'NexfepConsoleWarn', message: args }
|
|
264
|
+
window.ipc.postMessage(JSON.stringify(MessageBody));
|
|
265
|
+
window.__originConsoleWarn(...args);
|
|
266
|
+
}
|
|
267
|
+
console.debug = (...args) => {
|
|
268
|
+
const MessageBody = { type: 'NexfepConsoleDebug', message: args }
|
|
269
|
+
window.ipc.postMessage(JSON.stringify(MessageBody));
|
|
270
|
+
window.__originConsoleDebug(...args);
|
|
271
|
+
}
|
|
236
272
|
|
|
237
273
|
// 注入 CSS 样式
|
|
238
274
|
const style = document.createElement('style');
|
|
@@ -339,6 +375,21 @@ class WindowPool {
|
|
|
339
375
|
}
|
|
340
376
|
});
|
|
341
377
|
}
|
|
378
|
+
else if (dataObj.type == 'NexfepConsoleLog') {
|
|
379
|
+
this.logger.__printLog(windowObj.id, 'Log', dataObj.message);
|
|
380
|
+
}
|
|
381
|
+
else if (dataObj.type == 'NexfepConsoleError') {
|
|
382
|
+
this.logger.__printLog(windowObj.id, 'Error', dataObj.message, '\x1b[31m', '\x1b[0m');
|
|
383
|
+
}
|
|
384
|
+
else if (dataObj.type == 'NexfepConsoleInfo') {
|
|
385
|
+
this.logger.__printLog(windowObj.id, 'Info', dataObj.message, '\x1b[34m', '\x1b[0m');
|
|
386
|
+
}
|
|
387
|
+
else if (dataObj.type == 'NexfepConsoleWarn') {
|
|
388
|
+
this.logger.__printLog(windowObj.id, 'Warn', dataObj.message, '\x1b[33m', '\x1b[0m');
|
|
389
|
+
}
|
|
390
|
+
else if (dataObj.type == 'NexfepConsoleDebug') {
|
|
391
|
+
this.logger.__printLog(windowObj.id, 'Debug', dataObj.message, '\x1b[90m', '\x1b[0m');
|
|
392
|
+
}
|
|
342
393
|
});
|
|
343
394
|
return windowObj;
|
|
344
395
|
}
|
|
@@ -371,17 +422,11 @@ class WindowPool {
|
|
|
371
422
|
throw new Error("Window is not open");
|
|
372
423
|
}
|
|
373
424
|
}
|
|
374
|
-
async closePool() {
|
|
375
|
-
this.app.exit();
|
|
376
|
-
}
|
|
377
425
|
async handle(event, callback) {
|
|
378
426
|
this.handlers.set(event, [...this.handlers.get(event) || [], callback]);
|
|
379
427
|
}
|
|
380
428
|
async unhandle(event, callback) {
|
|
381
429
|
this.handlers.set(event, (this.handlers.get(event) || []).filter(c => c !== callback));
|
|
382
430
|
}
|
|
383
|
-
mainloop() {
|
|
384
|
-
this.app.run();
|
|
385
|
-
}
|
|
386
431
|
}
|
|
387
432
|
export { WindowPool, Window };
|