@lynker-desktop/electron-window-manager 0.0.4-alpha.9 → 0.0.5-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/esm/main/index.js CHANGED
@@ -1,8 +1,20 @@
1
- import _ from 'lodash';
2
- import { BrowserWindow, BrowserView } from 'electron';
1
+ import lodash from 'lodash';
2
+ import { app, session, BrowserWindow, BrowserView, webContents } from 'electron';
3
3
  import * as remote from '@electron/remote/main';
4
4
  import eIpc from '@lynker-desktop/electron-ipc/main';
5
5
 
6
+ const getCustomSession = (() => {
7
+ let customSession;
8
+ return () => {
9
+ if (!customSession) {
10
+ customSession = session.fromPartition('persist:__global_main_session__');
11
+ }
12
+ return customSession;
13
+ };
14
+ })();
15
+ app.on('ready', () => {
16
+ getCustomSession();
17
+ });
6
18
  if (!remote.isInitialized()) {
7
19
  remote.initialize();
8
20
  }
@@ -10,50 +22,162 @@ const enable = (win) => {
10
22
  remote.enable(win);
11
23
  };
12
24
  class WindowsManager {
13
- constructor(preload) {
25
+ constructor(preload, loadingViewUrl, errorViewUrl) {
14
26
  this.preload = preload;
15
27
  this.windows = new Map();
28
+ this.loadingViewUrl = `${loadingViewUrl ?? ''}`;
29
+ this.errorViewUrl = `${errorViewUrl ?? ''}`;
16
30
  }
17
31
  create(options) {
18
- const { name = 'anonymous', loadingView, browserWindow: browserWindowOptions = {}, openDevTools = false, preventOriginClose = false, } = options;
19
- const window = new BrowserWindow(_.merge({
20
- acceptFirstMouse: true,
21
- }, browserWindowOptions, {
22
- webPreferences: {
23
- plugins: true,
24
- nodeIntegration: true,
25
- contextIsolation: false,
26
- backgroundThrottling: false,
27
- nativeWindowOpen: false,
28
- webSecurity: false,
29
- preload: browserWindowOptions?.webPreferences?.preload || this.preload,
30
- }
31
- }));
32
- remote.enable(window.webContents);
33
- window._name = name;
34
- if (loadingView?.url) {
35
- this._setLoadingView(window, options);
36
- }
37
- window.on('close', (event) => {
38
- if (preventOriginClose) {
39
- event.preventDefault();
40
- return;
32
+ let window;
33
+ try {
34
+ const { type = 'BW', name = 'anonymous', url, loadingView = { url: undefined }, errorView = { url: undefined }, browserWindow: browserWindowOptions, openDevTools = false, preventOriginClose = false, } = options;
35
+ options.type = type;
36
+ try {
37
+ loadingView.url = `${loadingView?.url ?? this.loadingViewUrl}`;
38
+ lodash.merge(options, {
39
+ loadingView,
40
+ });
41
41
  }
42
- this.windows.delete(window.id);
43
- });
44
- window.webContents.on('dom-ready', () => {
45
- if (openDevTools) {
46
- window.webContents.openDevTools();
42
+ catch (error) {
43
+ log('error', 'loadingView error:', loadingView, this.loadingViewUrl);
47
44
  }
48
- });
49
- this.windows.set(window.id, window);
50
- if (/^file/gi.test(options.url)) {
51
- window.loadFile(options.url);
45
+ try {
46
+ errorView.url = `${errorView?.url ?? this.errorViewUrl}`;
47
+ lodash.merge(options, {
48
+ errorView,
49
+ });
50
+ }
51
+ catch (error) {
52
+ log('error', 'errorView error:', errorView, this.errorViewUrl);
53
+ }
54
+ let parentWin = undefined;
55
+ if (typeof browserWindowOptions?.parent === 'number') {
56
+ parentWin = BrowserWindow.fromId(browserWindowOptions?.parent) || undefined;
57
+ if (parentWin) {
58
+ browserWindowOptions.parent = parentWin;
59
+ }
60
+ else {
61
+ browserWindowOptions.parent = undefined;
62
+ }
63
+ }
64
+ log('log', 'create 1: ', options);
65
+ log('log', 'create 2: ', `parentWin: ${parentWin?.id}`);
66
+ window = type === 'BV' ?
67
+ new BrowserView(lodash.merge((browserWindowOptions || {}), {
68
+ parent: undefined,
69
+ webPreferences: {
70
+ // session: getCustomSession(),
71
+ plugins: true,
72
+ nodeIntegration: true,
73
+ contextIsolation: false,
74
+ backgroundThrottling: false,
75
+ nativeWindowOpen: false,
76
+ webSecurity: false,
77
+ preload: browserWindowOptions?.webPreferences?.preload || this.preload,
78
+ }
79
+ }))
80
+ : new BrowserWindow(lodash.merge({
81
+ acceptFirstMouse: true,
82
+ }, (browserWindowOptions || {}), {
83
+ parent: parentWin,
84
+ webPreferences: {
85
+ // session: getCustomSession(),
86
+ plugins: true,
87
+ nodeIntegration: true,
88
+ contextIsolation: false,
89
+ backgroundThrottling: false,
90
+ nativeWindowOpen: false,
91
+ webSecurity: false,
92
+ preload: browserWindowOptions?.webPreferences?.preload || this.preload,
93
+ }
94
+ }));
95
+ try {
96
+ remote.enable(window.webContents);
97
+ }
98
+ catch (error) {
99
+ log('error', 'enable: ', error);
100
+ }
101
+ // @ts-ignore
102
+ try {
103
+ window.id = Number(`${window.id || window.webContents.id}`);
104
+ }
105
+ catch (error) {
106
+ // log('error', 'set id: ', error)
107
+ }
108
+ // @ts-ignore
109
+ try {
110
+ window._id = Number(`${window.id || window.webContents.id}`);
111
+ }
112
+ catch (error) {
113
+ // log('error', 'set id: ', error)
114
+ }
115
+ window._type = type;
116
+ window._name = name;
117
+ window._extraData = `${options?.extraData || ''}`;
118
+ log('log', 'create 5: ', window.id, window._id, window._name);
119
+ if (loadingView?.url) {
120
+ if (type === 'BW') {
121
+ // @ts-ignore
122
+ this._setLoadingView(window, options);
123
+ }
124
+ }
125
+ if (errorView?.url) {
126
+ const showErrorView = lodash.debounce(() => {
127
+ const _url = window?.webContents?.getURL?.() || url;
128
+ /**
129
+ * 判断是否是错误视图
130
+ */
131
+ const isErrorView = `${_url}`.toUpperCase().startsWith(`${errorView?.url}`.toUpperCase());
132
+ if (!isErrorView && _url) {
133
+ // @ts-ignore
134
+ window.loadURL ? window.loadURL(`${errorView?.url}`) : window.webContents.loadURL(`${errorView?.url}`);
135
+ window.webContents.executeJavaScript(`
136
+ var key = '__ELECTRON_WINDOW_MANAGER_DID_FAIL_LOAD_URL__';
137
+ window[key] = "${_url}";
138
+ sessionStorage.setItem(key, "${_url}");
139
+ `);
140
+ }
141
+ }, 300);
142
+ window.webContents.on('did-fail-load', () => {
143
+ showErrorView();
144
+ });
145
+ window.webContents.on('did-start-loading', () => {
146
+ showErrorView.cancel();
147
+ });
148
+ }
149
+ window.webContents.on('close', () => {
150
+ this.windows.delete(window.id || window._id);
151
+ });
152
+ window.webContents.on('destroyed', () => {
153
+ this.windows.delete(window.id || window._id);
154
+ });
155
+ window.webContents.on('dom-ready', () => {
156
+ if (openDevTools) {
157
+ window.webContents.openDevTools();
158
+ }
159
+ });
160
+ if (type === 'BW') {
161
+ // @ts-ignore
162
+ window.on('closed', () => {
163
+ log('log', 'closed', window.id, window._name);
164
+ this.windows.delete(window.id || window._id);
165
+ });
166
+ }
167
+ if (type === 'BV') {
168
+ parentWin?.addBrowserView(window);
169
+ log('log', 'create - addBrowserView');
170
+ }
171
+ // @ts-ignore
172
+ window.loadURL ? window.loadURL(options.url) : window.webContents.loadURL(options.url);
173
+ // @ts-ignore
174
+ window.focus ? window.focus() : window.webContents.focus();
175
+ this.windows.set(window.id || window._id || window.webContents.id, window);
176
+ log('log', 'create', this.windows.keys());
52
177
  }
53
- else {
54
- window.loadURL(options.url);
178
+ catch (error) {
179
+ log('error', 'create', error);
55
180
  }
56
- window.focus();
57
181
  return window;
58
182
  }
59
183
  _setLoadingView(window, createOptions) {
@@ -61,6 +185,7 @@ class WindowsManager {
61
185
  const { loadingView, preventOriginNavigate = false, } = createOptions;
62
186
  let _loadingView = new BrowserView({
63
187
  webPreferences: {
188
+ // session: getCustomSession(),
64
189
  contextIsolation: false,
65
190
  nodeIntegration: true,
66
191
  // 允许loadURL与文件路径在开发环境
@@ -79,9 +204,10 @@ class WindowsManager {
79
204
  width: viewWidth || 10,
80
205
  height: viewHeight || 10,
81
206
  });
207
+ log('log', 'loadLoadingView', window._name);
82
208
  _loadingView.webContents.loadURL(loadingView?.url || '');
83
209
  };
84
- const onFailure = () => {
210
+ const onFailure = lodash.debounce(() => {
85
211
  if (_loadingView.webContents && !_loadingView.webContents.isDestroyed()) {
86
212
  _loadingView.webContents.close();
87
213
  }
@@ -91,9 +217,9 @@ class WindowsManager {
91
217
  if (window) {
92
218
  window.removeBrowserView(_loadingView);
93
219
  }
94
- };
220
+ }, 300);
95
221
  loadLoadingView();
96
- window.on('resize', _.debounce(() => {
222
+ window.on('resize', lodash.debounce(() => {
97
223
  if (_loadingView.webContents && !_loadingView.webContents.isDestroyed()) {
98
224
  if (window.isDestroyed()) {
99
225
  return;
@@ -123,61 +249,133 @@ class WindowsManager {
123
249
  window.webContents.on('crashed', onFailure);
124
250
  window.webContents.on('unresponsive', onFailure);
125
251
  window.webContents.on('did-fail-load', onFailure);
252
+ window.webContents.on('did-finish-load', onFailure);
253
+ window.webContents.on('did-stop-loading', onFailure);
126
254
  }
127
255
  }
128
- get(name) {
129
- let win = undefined;
130
- this.windows.forEach((i) => {
131
- if (i._name === name) {
132
- win = i;
256
+ get(idOrName) {
257
+ log('log', 'get', idOrName);
258
+ let win;
259
+ this.windows.forEach((i, key) => {
260
+ try {
261
+ if (!(i && i?.webContents?.isDestroyed && !i?.webContents?.isDestroyed?.())) {
262
+ this.windows.delete(key);
263
+ }
264
+ }
265
+ catch (error) {
266
+ log('error', 'get');
267
+ }
268
+ if (typeof idOrName === 'number') {
269
+ if (i?.id === idOrName || i?._id === idOrName) {
270
+ win = i;
271
+ }
272
+ }
273
+ else if (typeof idOrName === 'string') {
274
+ if (i?._name === idOrName) {
275
+ win = i;
276
+ }
133
277
  }
134
278
  });
135
- return win;
279
+ // @ts-ignore
280
+ if (win && win?.webContents?.isDestroyed && !win?.webContents?.isDestroyed?.()) {
281
+ return win;
282
+ }
283
+ return undefined;
136
284
  }
137
- getById(id) {
138
- let win = undefined;
139
- this.windows.forEach((i) => {
140
- if (i.id === id) {
141
- win = i;
285
+ getAll(type) {
286
+ log('log', 'getAll');
287
+ const bwWindows = new Map();
288
+ const bvWindows = new Map();
289
+ this.windows.forEach((win, key) => {
290
+ if (!(win && win?.webContents?.isDestroyed && !win?.webContents?.isDestroyed?.())) {
291
+ this.windows.delete(key);
292
+ }
293
+ if (win?._type === 'BW') {
294
+ bwWindows.set(key, win);
295
+ }
296
+ if (win?._type === 'BV') {
297
+ bvWindows.set(key, win);
142
298
  }
143
299
  });
144
- return win;
145
- }
146
- getAll() {
300
+ if (type === 'BW') {
301
+ return bwWindows;
302
+ }
303
+ if (type === 'BV') {
304
+ return bvWindows;
305
+ }
147
306
  return this.windows;
148
307
  }
149
- close(name) {
308
+ close(idOrName) {
309
+ log('log', 'close', idOrName);
150
310
  let win = undefined;
151
311
  this.windows.forEach((i) => {
152
- if (i._name === name) {
153
- win = i;
312
+ if (typeof idOrName === 'number') {
313
+ if (i?.id === idOrName || i?._id === idOrName) {
314
+ win = i;
315
+ }
316
+ }
317
+ else if (typeof idOrName === 'string') {
318
+ if (i?._name === idOrName) {
319
+ win = i;
320
+ }
154
321
  }
155
322
  });
156
323
  // @ts-ignore
157
324
  win && this.windows.delete(win?.id);
158
325
  // @ts-ignore
159
- return win && win?.destroy();
326
+ if (win) {
327
+ // @ts-ignore
328
+ if (win._type === 'BV') {
329
+ this.windows.forEach(i => {
330
+ if (i?._type === 'BW') {
331
+ const _win = i;
332
+ _win.removeBrowserView(win);
333
+ }
334
+ });
335
+ }
336
+ // @ts-ignore
337
+ win?.webContent?.close?.();
338
+ // @ts-ignore
339
+ win?.close?.();
340
+ // @ts-ignore
341
+ win?.destroy?.();
342
+ }
343
+ return true;
160
344
  }
161
- closeById(id) {
162
- let win = undefined;
163
- this.windows.forEach((i) => {
164
- if (i.id === id) {
165
- win = i;
345
+ closeAll() {
346
+ log('log', 'closeAll');
347
+ this.windows.forEach((win) => {
348
+ try {
349
+ win && this.windows.delete(win?.id);
350
+ // @ts-ignore
351
+ if (win._type === 'BV') {
352
+ this.windows.forEach(i => {
353
+ if (i?._type === 'BW') {
354
+ const _win = i;
355
+ _win.removeBrowserView(win);
356
+ }
357
+ });
358
+ }
359
+ // @ts-ignore
360
+ win?.webContent?.close?.();
361
+ // @ts-ignore
362
+ win?.close?.();
363
+ // @ts-ignore
364
+ win?.destroy?.();
365
+ }
366
+ catch (error) {
166
367
  }
167
368
  });
168
- // @ts-ignore
169
- win && this.windows.delete(win?.id);
170
- // @ts-ignore
171
- return win && win?.destroy();
172
369
  }
173
370
  getPreload() {
371
+ log('log', 'getPreload');
174
372
  return this.preload;
175
373
  }
176
374
  }
177
375
  // @ts-ignore
178
376
  global['__ELECTRON_WINDOWS_MANAGER__'] = undefined;
179
377
  let isInitialized = false;
180
- const initialize = (preload) => {
378
+ const initialize = (preload, loadingViewUrl, errorViewUrl) => {
181
379
  // @ts-ignore
182
380
  if (isInitialized && global['__ELECTRON_WINDOWS_MANAGER__']) {
183
381
  // @ts-ignore
@@ -185,38 +383,45 @@ const initialize = (preload) => {
185
383
  }
186
384
  isInitialized = true;
187
385
  // @ts-ignore
188
- const wm = global['__ELECTRON_WINDOWS_MANAGER__'] = new WindowsManager(preload);
386
+ const wm = global['__ELECTRON_WINDOWS_MANAGER__'] = new WindowsManager(preload, loadingViewUrl, errorViewUrl);
189
387
  eIpc.mainIPC.handleRenderer('__ELECTRON_WINDOW_MANAGER_IPC_CHANNEL__', async (data) => {
190
388
  if (data?.type === 'create') {
191
389
  const opt = data;
192
390
  const findWin = wm.get(opt.data.name);
193
391
  if (findWin) {
194
- findWin.focus();
392
+ // @ts-ignore
393
+ findWin?.focus?.();
394
+ findWin?.webContents.focus?.();
395
+ if (opt.data.url && findWin.webContents.getURL() !== opt.data.url) {
396
+ findWin.webContents.loadURL(opt.data.url);
397
+ /** fix: url hash 无法正常加载 */
398
+ setTimeout(() => {
399
+ findWin.webContents.reload();
400
+ }, 100);
401
+ }
195
402
  return {
196
- winId: findWin?.id,
197
- winName: findWin?._name,
403
+ winId: Number(`${findWin?.id || findWin?._id || -1}`),
404
+ winName: `${findWin?._name || ''}`,
405
+ winType: `${findWin?._type || ''}`,
406
+ winExtraData: `${findWin?._extraData || ''}`,
198
407
  };
199
408
  }
200
409
  const res = wm.create(opt.data);
201
410
  return {
202
- winId: Number(res.id),
203
- winName: res._name,
411
+ winId: Number(`${res.id || res._id || -1}`),
412
+ winName: `${res?._name || ''}`,
413
+ winType: `${res?._type || ''}`,
414
+ winExtraData: `${res?._extraData || ''}`,
204
415
  };
205
416
  }
206
417
  if (data?.type === 'get') {
207
418
  const opt = data;
208
419
  const res = wm.get(opt?.data);
209
420
  return {
210
- winId: res?.id ? Number(res?.id) : -1,
211
- winName: res?._name ? res?._name : '',
212
- };
213
- }
214
- if (data?.type === 'getById') {
215
- const opt = data;
216
- const res = wm.getById(opt?.data);
217
- return {
218
- winId: res?.id ? Number(res?.id) : -1,
219
- winName: res?._name ? res?._name : '',
421
+ winId: Number(`${res?.id || res?._id || -1}`),
422
+ winName: `${res?._name || ''}`,
423
+ winType: `${res?._type || ''}`,
424
+ winExtraData: `${res?._extraData || ''}`,
220
425
  };
221
426
  }
222
427
  if (data?.type === 'getAll') {
@@ -224,9 +429,11 @@ const initialize = (preload) => {
224
429
  const obj = {};
225
430
  res.forEach(i => {
226
431
  // @ts-ignore
227
- obj[i.id] = {
228
- winId: i?.id ? Number(i?.id) : -1,
229
- winName: i?._name ? i?._name : '',
432
+ obj[i.id || i._id] = {
433
+ winId: Number(`${i?.id || i?._id || -1}`),
434
+ winName: `${i?._name || ''}`,
435
+ winType: `${i?._type || ''}`,
436
+ winExtraData: `${i?._extraData || ''}`,
230
437
  };
231
438
  });
232
439
  return obj;
@@ -236,19 +443,86 @@ const initialize = (preload) => {
236
443
  const res = wm.close(opt?.data);
237
444
  return res;
238
445
  }
239
- if (data?.type === 'closeById') {
240
- const opt = data;
241
- const res = wm.closeById(opt?.data);
446
+ if (data?.type === 'closeAll') {
447
+ const res = wm.closeAll();
242
448
  return res;
243
449
  }
450
+ if (data?.type === 'getWindowForWebContentId') {
451
+ const opt = data;
452
+ const targetWebContents = webContents.fromId(opt.data);
453
+ if (targetWebContents) {
454
+ let win = BrowserWindow.fromWebContents(targetWebContents);
455
+ if (!win) {
456
+ // 获取所有的 BrowserWindows
457
+ let allWindows = BrowserWindow.getAllWindows();
458
+ // 遍历所有窗口,检查每个窗口的 BrowserView
459
+ for (let _win of allWindows) {
460
+ let views = _win.getBrowserViews();
461
+ // 遍历窗口的所有 BrowserView
462
+ for (let view of views) {
463
+ if (view.webContents === targetWebContents) {
464
+ win = _win;
465
+ break;
466
+ }
467
+ }
468
+ if (win)
469
+ break;
470
+ }
471
+ }
472
+ // @ts-ignore
473
+ return win?.id || win?._id;
474
+ }
475
+ return undefined;
476
+ }
244
477
  if (data?.type === 'getPreload') {
245
478
  const res = wm.getPreload();
246
479
  return res;
247
480
  }
481
+ if (data?.type === 'borrowView_getBounds') {
482
+ const opt = data;
483
+ const bv = wm.get(opt.data.webContentId);
484
+ if (bv) {
485
+ return bv.getBounds();
486
+ }
487
+ return undefined;
488
+ }
489
+ if (data?.type === 'borrowView_setBounds') {
490
+ const opt = data;
491
+ const bv = wm.get(opt.data.webContentId);
492
+ if (bv) {
493
+ bv.setBounds(opt.data.options);
494
+ }
495
+ return undefined;
496
+ }
497
+ if (data?.type === 'borrowView_setAutoResize') {
498
+ const opt = data;
499
+ const bv = wm.get(opt.data.webContentId);
500
+ if (bv) {
501
+ bv.setAutoResize(opt.data.options);
502
+ }
503
+ return undefined;
504
+ }
505
+ if (data?.type === 'borrowView_setBackgroundColor') {
506
+ const opt = data;
507
+ const bv = wm.get(opt.data.webContentId);
508
+ if (bv) {
509
+ bv.setBackgroundColor(opt.data.options);
510
+ }
511
+ return undefined;
512
+ }
248
513
  return undefined;
249
514
  });
250
515
  return wm;
251
516
  };
517
+ const log = (type, ...data) => {
518
+ const key = `[electron-window-manager]: `;
519
+ try {
520
+ console[type](key, ...data);
521
+ }
522
+ catch (error) {
523
+ console.error(key, error);
524
+ }
525
+ };
252
526
 
253
- export { WindowsManager, enable, initialize, isInitialized };
527
+ export { WindowsManager, enable, getCustomSession, initialize, isInitialized };
254
528
  //# sourceMappingURL=index.js.map