@easy-electron/core 1.0.1-alpha.1

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.cjs ADDED
@@ -0,0 +1,799 @@
1
+ 'use strict';
2
+
3
+ const electron = require('electron');
4
+ const logger = require('@easy-electron/logger');
5
+ const path = require('node:path');
6
+ const utils = require('@easy-electron/utils');
7
+ const node_crypto = require('node:crypto');
8
+
9
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
10
+
11
+ const path__default = /*#__PURE__*/_interopDefaultCompat(path);
12
+
13
+ class EasyElectronError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "EasyElectronError";
17
+ }
18
+ }
19
+
20
+ class IpcError extends EasyElectronError {
21
+ /** 错误码 */
22
+ code;
23
+ constructor(code, message) {
24
+ super(message);
25
+ this.name = "IpcError";
26
+ this.code = code;
27
+ }
28
+ /**
29
+ * 将任意错误包装为 IpcError
30
+ * @param error 原始错误
31
+ * @returns IpcError 实例
32
+ */
33
+ static from(error) {
34
+ if (error instanceof IpcError) return error;
35
+ const message = error instanceof Error ? error.message : String(error);
36
+ return new IpcError("IPC_UNKNOWN" /* UNKNOWN */, message);
37
+ }
38
+ }
39
+
40
+ const WindowEventTypes = ["created", "closed", "focus", "blur", "ready"];
41
+ const DEFAULT_MAIN_WINDOW_ID = "main";
42
+ const DEFAULT_APP_CONFIG = {
43
+ app: {
44
+ name: "Electron App",
45
+ singleInstance: true
46
+ },
47
+ window: {
48
+ width: 800,
49
+ height: 600,
50
+ webPreferences: {
51
+ preload: utils.EEUtil.getDefaultPreloadPath()
52
+ }
53
+ },
54
+ dev: {
55
+ openDevTools: "right"
56
+ },
57
+ prod: {
58
+ htmlPath: "dist/index.html"
59
+ },
60
+ plugins: []
61
+ };
62
+
63
+ class WindowManager {
64
+ windows;
65
+ defaultConfig;
66
+ eventListeners;
67
+ logger = logger.Logger.scope("WindowManager");
68
+ /** 开发模式下的页面 URL(可选,替代默认的 VITE_DEV_SERVER_URL 检测) */
69
+ devUrl;
70
+ /** 生产模式下的 HTML 路径(可选) */
71
+ prodHtmlPath;
72
+ constructor(defaultConfig, devUrl, prodHtmlPath) {
73
+ this.windows = /* @__PURE__ */ new Map();
74
+ this.defaultConfig = defaultConfig || {};
75
+ this.eventListeners = /* @__PURE__ */ new Map();
76
+ this.devUrl = devUrl;
77
+ this.prodHtmlPath = prodHtmlPath;
78
+ WindowEventTypes.forEach((type) => {
79
+ this.eventListeners.set(type, /* @__PURE__ */ new Set());
80
+ });
81
+ }
82
+ /**
83
+ * 创建窗口
84
+ * @param config 窗口配置
85
+ * @returns 窗口实例
86
+ */
87
+ async create(config) {
88
+ const mergedConfig = utils.ConfigUtil.deepMergeConfig(this.defaultConfig, config || {});
89
+ const windowId = this.windows.size === 0 ? DEFAULT_MAIN_WINDOW_ID : mergedConfig.id || node_crypto.randomUUID();
90
+ if (this.windows.has(windowId)) {
91
+ const error = new EasyElectronError(`Window with id '${windowId}' already exists`);
92
+ this.logger.error(error.message);
93
+ throw error;
94
+ }
95
+ this.logger.info(`Creating window with id '${windowId}'`);
96
+ const { id: _id, url: _url, htmlPath: _htmlPath, raw, ...nativeOptions } = mergedConfig;
97
+ const windowOptions = utils.ConfigUtil.deepMergeConfig(
98
+ nativeOptions,
99
+ raw || {}
100
+ );
101
+ const browserWindow = new electron.BrowserWindow(windowOptions);
102
+ const instance = {
103
+ id: windowId,
104
+ window: browserWindow,
105
+ config: mergedConfig
106
+ };
107
+ this.windows.set(windowId, instance);
108
+ this.setupWindowListeners(instance);
109
+ try {
110
+ await this.loadContent(browserWindow, mergedConfig);
111
+ } catch (error) {
112
+ this.logger.error(`\u7A97\u53E3\u52A0\u8F7D\u5931\u8D25 [${windowId}]:`, error instanceof Error ? error.message : "");
113
+ const errMsg = error instanceof Error ? error.message : String(error);
114
+ browserWindow.loadURL(`data:text/html;charset=utf-8,${encodeURIComponent(this.buildErrorPage(windowId, errMsg))}`);
115
+ }
116
+ this.emit("created", instance);
117
+ return instance;
118
+ }
119
+ /**
120
+ * 加载窗口内容
121
+ */
122
+ async loadContent(window, config) {
123
+ if (config.url) {
124
+ await window.loadURL(config.url);
125
+ } else if (config.htmlPath) {
126
+ await window.loadFile(config.htmlPath);
127
+ } else if (this.devUrl) {
128
+ await window.loadURL(this.devUrl);
129
+ } else {
130
+ const htmlPath = this.prodHtmlPath || path__default.join(utils.EEUtil.getAppRoot(), "dist", "index.html");
131
+ await window.loadFile(htmlPath);
132
+ }
133
+ }
134
+ /**
135
+ * 设置窗口事件监听器
136
+ */
137
+ setupWindowListeners(instance) {
138
+ const { window } = instance;
139
+ window.once("ready-to-show", () => {
140
+ this.emit("ready", instance);
141
+ });
142
+ window.on("focus", () => {
143
+ this.emit("focus", instance);
144
+ });
145
+ window.on("blur", () => {
146
+ this.emit("blur", instance);
147
+ });
148
+ window.on("close", () => {
149
+ this.emit("closed", instance);
150
+ this.windows.delete(instance.id);
151
+ });
152
+ }
153
+ /**
154
+ * 获取窗口实例
155
+ * @param id 窗口 ID
156
+ * @returns 窗口实例或 undefined
157
+ */
158
+ get(id) {
159
+ return this.windows.get(id);
160
+ }
161
+ /**
162
+ * 获取所有窗口实例
163
+ * @returns 窗口实例数组
164
+ */
165
+ getAll() {
166
+ return Array.from(this.windows.values());
167
+ }
168
+ /**
169
+ * 检查窗口是否存在
170
+ * @param id 窗口 ID
171
+ * @returns 是否存在
172
+ */
173
+ has(id) {
174
+ return this.windows.has(id);
175
+ }
176
+ /**
177
+ * 获取窗口数量
178
+ * @returns 窗口数量
179
+ */
180
+ count() {
181
+ return this.windows.size;
182
+ }
183
+ /**
184
+ * 关闭窗口
185
+ * @param id 窗口 ID
186
+ */
187
+ close(id) {
188
+ const instance = this.windows.get(id);
189
+ if (!instance) {
190
+ const error = new EasyElectronError(`Window with id '${id}' not found`);
191
+ this.logger.error(error.message);
192
+ throw error;
193
+ }
194
+ this.logger.info(`Closing window with id '${id}'`);
195
+ if (!instance.window.isDestroyed()) {
196
+ instance.window.close();
197
+ }
198
+ }
199
+ /**
200
+ * 关闭所有窗口
201
+ */
202
+ closeAll() {
203
+ const windowIds = Array.from(this.windows.keys());
204
+ windowIds.forEach((id) => {
205
+ try {
206
+ this.close(id);
207
+ } catch (error) {
208
+ console.error(`Error closing window '${id}':`, error);
209
+ }
210
+ });
211
+ }
212
+ /**
213
+ * 注册窗口事件监听器
214
+ * @param event 事件类型
215
+ * @param listener 监听器函数
216
+ */
217
+ on(event, listener) {
218
+ const listeners = this.eventListeners.get(event);
219
+ if (listeners) {
220
+ listeners.add(listener);
221
+ }
222
+ }
223
+ /**
224
+ * 移除窗口事件监听器
225
+ * @param event 事件类型
226
+ * @param listener 监听器函数
227
+ */
228
+ off(event, listener) {
229
+ const listeners = this.eventListeners.get(event);
230
+ if (listeners) {
231
+ listeners.delete(listener);
232
+ }
233
+ }
234
+ /**
235
+ * 触发窗口事件
236
+ * @param event 事件类型
237
+ * @param instance 窗口实例
238
+ */
239
+ emit(event, instance) {
240
+ const listeners = this.eventListeners.get(event);
241
+ if (listeners) {
242
+ listeners.forEach((listener) => {
243
+ try {
244
+ listener(instance);
245
+ } catch (error) {
246
+ console.error(`Error in window event listener for '${event}':`, error);
247
+ }
248
+ });
249
+ }
250
+ }
251
+ /**
252
+ * 获取默认配置
253
+ */
254
+ getDefaultConfig() {
255
+ return utils.ConfigUtil.deepMergeConfig({}, this.defaultConfig);
256
+ }
257
+ /**
258
+ * 构建降级错误页面(当窗口内容加载失败时显示)
259
+ * @param windowId 窗口 ID
260
+ * @param errorMessage 错误信息
261
+ * @returns HTML 字符串
262
+ */
263
+ buildErrorPage(windowId, errorMessage) {
264
+ return `<!DOCTYPE html>
265
+ <html lang="zh-CN">
266
+ <head>
267
+ <meta charset="UTF-8">
268
+ <style>
269
+ * { margin: 0; padding: 0; box-sizing: border-box; }
270
+ body {
271
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
272
+ display: flex; justify-content: center; align-items: center;
273
+ min-height: 100vh; background: #1a1a2e; color: #e0e0e0;
274
+ }
275
+ .card {
276
+ background: #16213e; border-radius: 12px; padding: 40px;
277
+ max-width: 520px; text-align: center;
278
+ box-shadow: 0 8px 32px rgba(0,0,0,0.3);
279
+ }
280
+ h1 { font-size: 20px; color: #e94560; margin-bottom: 16px; }
281
+ .detail {
282
+ background: #0f3460; border-radius: 8px; padding: 16px;
283
+ font-size: 13px; color: #a0a0b0; text-align: left;
284
+ word-break: break-all; margin: 16px 0;
285
+ }
286
+ .hint { font-size: 12px; color: #666; }
287
+ .hint code { background: #0f3460; padding: 2px 6px; border-radius: 4px; }
288
+ </style>
289
+ </head>
290
+ <body>
291
+ <div class="card">
292
+ <h1>\u26A0 \u7A97\u53E3\u52A0\u8F7D\u5931\u8D25</h1>
293
+ <p>\u7A97\u53E3 <strong>${windowId}</strong> \u7684\u5185\u5BB9\u672A\u80FD\u6210\u529F\u52A0\u8F7D\u3002</p>
294
+ <div class="detail">${errorMessage}</div>
295
+ <p class="hint">
296
+ \u8BF7\u68C0\u67E5 <code>htmlPath</code> \u6216 <code>url</code> \u914D\u7F6E\uFF0C\u6216\u786E\u4FDD\u5F00\u53D1\u670D\u52A1\u5668\u5DF2\u542F\u52A8\u3002
297
+ </p>
298
+ </div>
299
+ </body>
300
+ </html>`;
301
+ }
302
+ clear() {
303
+ this.windows.clear();
304
+ this.eventListeners.clear();
305
+ }
306
+ }
307
+
308
+ const IPC_MODULE_KEY = Symbol.for("easy-electron:ipc-module");
309
+ class IpcManager {
310
+ handlers;
311
+ windowManager;
312
+ logger = logger.Logger.scope("IpcManager");
313
+ constructor(windowManager) {
314
+ this.handlers = /* @__PURE__ */ new Map();
315
+ this.windowManager = windowManager;
316
+ }
317
+ /**
318
+ * 注册 IPC 处理器
319
+ * @param channel IPC 通道名称
320
+ * @param handler 处理器函数
321
+ * @param options 处理器选项
322
+ */
323
+ handle(channel, handler, options) {
324
+ if (this.handlers.has(channel)) {
325
+ const error = new EasyElectronError(`IPC handler for channel '${channel}' already exists`);
326
+ this.logger.error(error.message);
327
+ throw error;
328
+ }
329
+ this.logger.info(`Registering IPC handler for channel '${channel}'`);
330
+ this.handlers.set(channel, handler);
331
+ if (options?.sync) {
332
+ electron.ipcMain.on(channel, (event, ...args) => {
333
+ try {
334
+ const result = handler(event, ...args);
335
+ event.returnValue = result;
336
+ } catch (error) {
337
+ console.error(`Error in sync IPC handler for '${channel}':`, error);
338
+ event.returnValue = { error: error instanceof Error ? error.message : "Unknown error" };
339
+ }
340
+ });
341
+ } else {
342
+ electron.ipcMain.handle(channel, async (event, ...args) => {
343
+ try {
344
+ return await handler(event, ...args);
345
+ } catch (error) {
346
+ console.error(`Error in async IPC handler for '${channel}':`, error);
347
+ throw error;
348
+ }
349
+ });
350
+ }
351
+ }
352
+ /**
353
+ * 批量注册 defineIpc 返回的处理器映射
354
+ *
355
+ * 自动识别函数形式(handle)和对象形式({type, handler}),
356
+ * 根据 moduleName 前缀拼接 IPC 通道名。
357
+ *
358
+ * @param handlers defineIpc 返回的处理器映射对象
359
+ * @param moduleName 可选的模块名称(用于拼接通道前缀,如 `module:method`)
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * import ipcHandlers from './electron/ipc/handlers'
364
+ * app.ipc.registerHandlers(ipcHandlers)
365
+ * ```
366
+ */
367
+ registerHandlers(handlers, moduleName) {
368
+ if (!moduleName) {
369
+ moduleName = handlers[IPC_MODULE_KEY];
370
+ }
371
+ for (const [name, handler] of Object.entries(handlers)) {
372
+ if (typeof name === "string" && name.startsWith("__")) continue;
373
+ const channel = moduleName ? `${moduleName}:${name}` : name;
374
+ if (typeof handler === "object" && handler !== null && "handler" in handler) {
375
+ const config = handler;
376
+ const type = config.type ?? "handle";
377
+ const handlerFn = config.handler;
378
+ if (type === "on" || type === "once") {
379
+ this.logger.info(`\u6CE8\u518C IPC \u5904\u7406\u5668(${type}): ${channel}`);
380
+ const method = type === "once" ? "once" : "on";
381
+ electron.ipcMain[method](channel, (event, ...args) => {
382
+ try {
383
+ handlerFn(event, ...args);
384
+ } catch (error) {
385
+ console.error(`Error in IPC ${type} handler for '${channel}':`, error);
386
+ }
387
+ });
388
+ } else {
389
+ this.logger.info(`\u6CE8\u518C IPC \u5904\u7406\u5668(${type}): ${channel}`);
390
+ const method = type === "handleOnce" ? "handleOnce" : "handle";
391
+ electron.ipcMain[method](channel, async (event, ...args) => {
392
+ try {
393
+ return await handlerFn(event, ...args);
394
+ } catch (error) {
395
+ console.error(`Error in IPC ${type} handler for '${channel}':`, error);
396
+ throw IpcError.from(error);
397
+ }
398
+ });
399
+ }
400
+ if (!this.handlers.has(channel)) {
401
+ this.handlers.set(channel, handlerFn);
402
+ }
403
+ } else if (typeof handler === "function") {
404
+ this.logger.info(`\u6CE8\u518C IPC \u5904\u7406\u5668(handle): ${channel}`);
405
+ electron.ipcMain.handle(channel, async (event, ...args) => {
406
+ try {
407
+ return await handler(event, ...args);
408
+ } catch (error) {
409
+ console.error(`Error in IPC handler for '${channel}':`, error);
410
+ throw IpcError.from(error);
411
+ }
412
+ });
413
+ if (!this.handlers.has(channel)) {
414
+ this.handlers.set(channel, handler);
415
+ }
416
+ } else {
417
+ this.logger.warn(`\u8DF3\u8FC7\u65E0\u6CD5\u8BC6\u522B\u7684\u5904\u7406\u5668: ${channel}`);
418
+ }
419
+ }
420
+ }
421
+ /**
422
+ * 移除 IPC 处理器
423
+ * @param channel IPC 通道名称
424
+ */
425
+ removeHandler(channel) {
426
+ if (!this.handlers.has(channel)) {
427
+ return;
428
+ }
429
+ this.handlers.delete(channel);
430
+ electron.ipcMain.removeHandler(channel);
431
+ electron.ipcMain.removeAllListeners(channel);
432
+ }
433
+ /**
434
+ * 移除所有 IPC 处理器
435
+ */
436
+ removeAllHandlers() {
437
+ const channels = Array.from(this.handlers.keys());
438
+ channels.forEach((channel) => {
439
+ this.removeHandler(channel);
440
+ });
441
+ }
442
+ /**
443
+ * 获取已注册的处理器数量
444
+ */
445
+ count() {
446
+ return this.handlers.size;
447
+ }
448
+ /**
449
+ * 检查处理器是否已注册
450
+ */
451
+ has(channel) {
452
+ return this.handlers.has(channel);
453
+ }
454
+ /**
455
+ * 向主窗口发送消息
456
+ * @param channel IPC 通道名称
457
+ * @param args 消息参数
458
+ */
459
+ send(channel, ...args) {
460
+ const instance = this.windowManager.get(DEFAULT_MAIN_WINDOW_ID);
461
+ if (!instance) {
462
+ throw new EasyElectronError("Main window not found");
463
+ }
464
+ if (!instance.window.isDestroyed()) {
465
+ instance.window.webContents.send(channel, ...args);
466
+ }
467
+ }
468
+ /**
469
+ * 向指定窗口发送消息
470
+ * @param windowId 窗口 ID
471
+ * @param channel IPC 通道名称
472
+ * @param args 消息参数
473
+ */
474
+ sendTo(windowId, channel, ...args) {
475
+ const instance = this.windowManager.get(windowId);
476
+ if (!instance) {
477
+ throw new EasyElectronError(`Window with id '${windowId}' not found`);
478
+ }
479
+ if (!instance.window.isDestroyed()) {
480
+ instance.window.webContents.send(channel, ...args);
481
+ }
482
+ }
483
+ /**
484
+ * 向所有窗口广播消息
485
+ * @param channel IPC 通道名称
486
+ * @param args 消息参数
487
+ */
488
+ broadcast(channel, ...args) {
489
+ const instances = this.windowManager.getAll();
490
+ instances.forEach((instance) => {
491
+ if (!instance.window.isDestroyed()) {
492
+ try {
493
+ instance.window.webContents.send(channel, ...args);
494
+ } catch (error) {
495
+ console.error(`Error broadcasting to window '${instance.id}':`, error);
496
+ }
497
+ }
498
+ });
499
+ }
500
+ }
501
+
502
+ class LifecycleManager {
503
+ app;
504
+ windowManager;
505
+ readyHooks;
506
+ activateHooks;
507
+ beforeQuitHooks;
508
+ constructor(app, windowManager) {
509
+ this.app = app;
510
+ this.windowManager = windowManager;
511
+ this.readyHooks = /* @__PURE__ */ new Set();
512
+ this.activateHooks = /* @__PURE__ */ new Set();
513
+ this.beforeQuitHooks = /* @__PURE__ */ new Set();
514
+ }
515
+ /**
516
+ * 注册 ready 钩子
517
+ * @param hook 钩子函数
518
+ */
519
+ onReady(hook) {
520
+ this.readyHooks.add(hook);
521
+ }
522
+ /**
523
+ * 注册 activate 钩子(macOS)
524
+ * @param hook 钩子函数
525
+ */
526
+ onActivate(hook) {
527
+ this.activateHooks.add(hook);
528
+ }
529
+ /**
530
+ * 注册退出前的清理钩子
531
+ * @param hook 钩子函数
532
+ */
533
+ onBeforeQuit(hook) {
534
+ this.beforeQuitHooks.add(hook);
535
+ }
536
+ /**
537
+ * 初始化生命周期监听
538
+ */
539
+ init() {
540
+ this.app.whenReady().then(async () => {
541
+ await this.executeHooks(this.readyHooks);
542
+ });
543
+ this.app.on("activate", async () => {
544
+ await this.executeHooks(this.activateHooks);
545
+ });
546
+ this.app.on("will-quit", async (event) => {
547
+ if (this.beforeQuitHooks.size > 0) {
548
+ event.preventDefault();
549
+ try {
550
+ await this.executeHooks(this.beforeQuitHooks);
551
+ } catch (error) {
552
+ console.error("\u9000\u51FA\u524D\u6E05\u7406\u5931\u8D25:", error);
553
+ }
554
+ this.app.exit(0);
555
+ }
556
+ });
557
+ this.app.on("window-all-closed", () => {
558
+ if (process.platform !== "darwin") {
559
+ this.app.quit();
560
+ this.windowManager.clear();
561
+ }
562
+ });
563
+ }
564
+ /**
565
+ * 执行钩子集合
566
+ * @param hooks 钩子函数集合
567
+ */
568
+ async executeHooks(hooks) {
569
+ for (const hook of hooks) {
570
+ try {
571
+ await hook();
572
+ } catch (error) {
573
+ console.error("\u751F\u547D\u5468\u671F\u94A9\u5B50\u6267\u884C\u5931\u8D25:", error);
574
+ }
575
+ }
576
+ }
577
+ }
578
+
579
+ class PluginManager {
580
+ plugins;
581
+ context;
582
+ constructor(context) {
583
+ this.plugins = /* @__PURE__ */ new Map();
584
+ this.context = context;
585
+ }
586
+ /**
587
+ * 注册插件
588
+ * @param plugin 插件对象
589
+ */
590
+ register(plugin) {
591
+ if (!plugin.name) {
592
+ throw new EasyElectronError("Plugin must have a name");
593
+ }
594
+ if (this.plugins.has(plugin.name)) {
595
+ throw new EasyElectronError(`Plugin '${plugin.name}' is already registered`);
596
+ }
597
+ this.plugins.set(plugin.name, plugin);
598
+ }
599
+ /**
600
+ * 获取插件
601
+ * @param name 插件名称
602
+ * @returns 插件对象或 undefined
603
+ */
604
+ get(name) {
605
+ return this.plugins.get(name);
606
+ }
607
+ /**
608
+ * 检查插件是否已注册
609
+ * @param name 插件名称
610
+ * @returns 是否已注册
611
+ */
612
+ has(name) {
613
+ return this.plugins.has(name);
614
+ }
615
+ /**
616
+ * 调用插件钩子
617
+ * @param hookName 钩子名称
618
+ */
619
+ async callHook(hookName) {
620
+ for (const plugin of this.plugins.values()) {
621
+ const hook = plugin[hookName];
622
+ if (typeof hook === "function") {
623
+ try {
624
+ await hook.call(plugin, this.context);
625
+ } catch (error) {
626
+ console.error(`Error in plugin '${plugin.name}' hook '${String(hookName)}':`, error);
627
+ }
628
+ }
629
+ }
630
+ }
631
+ /**
632
+ * 获取已注册的插件数量
633
+ */
634
+ count() {
635
+ return this.plugins.size;
636
+ }
637
+ /**
638
+ * 获取所有插件
639
+ */
640
+ getAll() {
641
+ return Array.from(this.plugins.values());
642
+ }
643
+ }
644
+
645
+ class EasyElectron {
646
+ windowManager;
647
+ ipcManager;
648
+ lifecycleManager;
649
+ pluginManager;
650
+ config;
651
+ constructor(config) {
652
+ this.config = utils.ConfigUtil.deepMergeConfig(DEFAULT_APP_CONFIG, config || {});
653
+ this.windowManager = new WindowManager(
654
+ this.config.window,
655
+ this.config.dev?.serverUrl,
656
+ this.config.prod?.htmlPath
657
+ );
658
+ this.ipcManager = new IpcManager(this.windowManager);
659
+ this.lifecycleManager = new LifecycleManager(electron.app, this.windowManager);
660
+ const pluginContext = {
661
+ windows: this.windowManager,
662
+ ipc: this.ipcManager,
663
+ app: electron.app,
664
+ config: this.config
665
+ };
666
+ this.pluginManager = new PluginManager(pluginContext);
667
+ if (this.config.plugins) {
668
+ this.config.plugins.forEach((plugin) => {
669
+ this.pluginManager.register(plugin);
670
+ });
671
+ }
672
+ }
673
+ /**
674
+ * 获取窗口管理器
675
+ */
676
+ get windows() {
677
+ return this.windowManager;
678
+ }
679
+ /**
680
+ * 获取 IPC 管理器
681
+ */
682
+ get ipc() {
683
+ return this.ipcManager;
684
+ }
685
+ /**
686
+ * 初始化应用程序
687
+ */
688
+ async init() {
689
+ if (this.config.app?.singleInstance) {
690
+ const gotLock = electron.app.requestSingleInstanceLock();
691
+ if (!gotLock) {
692
+ electron.app.quit();
693
+ return;
694
+ }
695
+ electron.app.on("second-instance", () => {
696
+ const mainWindow = this.windowManager.get("main");
697
+ if (mainWindow && !mainWindow.window.isDestroyed()) {
698
+ if (mainWindow.window.isMinimized()) mainWindow.window.restore();
699
+ mainWindow.window.focus();
700
+ }
701
+ });
702
+ }
703
+ await this.pluginManager.callHook("install");
704
+ const entries = utils.ipcRegistry.drain();
705
+ for (const entry of entries) {
706
+ this.ipcManager.registerHandlers(entry.handlers, entry.moduleName);
707
+ }
708
+ this.lifecycleManager.onActivate(async () => {
709
+ if (this.windowManager.count() === 0) {
710
+ await this.windowManager.create();
711
+ }
712
+ });
713
+ this.lifecycleManager.onReady(async () => {
714
+ await this.pluginManager.callHook("onReady");
715
+ const instance = await this.windowManager.create();
716
+ if (utils.EEUtil.isDev && this.config.dev?.openDevTools) {
717
+ instance.window.webContents.openDevTools({ mode: this.config.dev?.openDevTools || "right" });
718
+ }
719
+ });
720
+ this.lifecycleManager.onBeforeQuit(async () => {
721
+ await this.pluginManager.callHook("onBeforeQuit");
722
+ this.ipcManager.removeAllHandlers();
723
+ });
724
+ this.lifecycleManager.init();
725
+ }
726
+ /**
727
+ * 注册插件
728
+ */
729
+ use(plugin) {
730
+ this.pluginManager.register(plugin);
731
+ return this;
732
+ }
733
+ /**
734
+ * 注册 ready 钩子
735
+ */
736
+ onReady(callback) {
737
+ this.lifecycleManager.onReady(callback);
738
+ return this;
739
+ }
740
+ /**
741
+ * 注册 activate 钩子
742
+ */
743
+ onActivate(callback) {
744
+ this.lifecycleManager.onActivate(callback);
745
+ return this;
746
+ }
747
+ /**
748
+ * 注册 beforeQuit 钩子
749
+ */
750
+ onBeforeQuit(callback) {
751
+ this.lifecycleManager.onBeforeQuit(callback);
752
+ return this;
753
+ }
754
+ /**
755
+ * 设置 IPC 处理器
756
+ * @param setup IPC 处理器设置函数
757
+ */
758
+ setupIpc(setup) {
759
+ setup(this.ipcManager);
760
+ return this;
761
+ }
762
+ /**
763
+ * 批量注册 IPC 处理器
764
+ *
765
+ * 接收 defineIpc 返回的定义对象,自动识别模块名并注册所有处理器。
766
+ *
767
+ * @param handlers defineIpc 返回的处理器映射
768
+ *
769
+ * @example
770
+ * ```ts
771
+ * import ipcHandlers from './electron/ipc/handlers'
772
+ * app.registerIpc(ipcHandlers)
773
+ * ```
774
+ */
775
+ registerIpc(handlers) {
776
+ this.ipcManager.registerHandlers(handlers);
777
+ return this;
778
+ }
779
+ /**
780
+ * 修改日志实现
781
+ * @param logger 日志实现
782
+ */
783
+ setLogger(logger$1) {
784
+ logger.Logger.setImpl(logger$1);
785
+ return this;
786
+ }
787
+ }
788
+
789
+ function createElectronApp(config) {
790
+ return new EasyElectron(config);
791
+ }
792
+
793
+ exports.EasyElectron = EasyElectron;
794
+ exports.EasyElectronError = EasyElectronError;
795
+ exports.IpcManager = IpcManager;
796
+ exports.LifecycleManager = LifecycleManager;
797
+ exports.PluginManager = PluginManager;
798
+ exports.WindowManager = WindowManager;
799
+ exports.createElectronApp = createElectronApp;