@lynker-desktop/electron-window-manager 0.0.4-alpha.8 → 0.0.5-alpha.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/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,134 @@ 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
+ log('log', 'getAll', key, win, win?.webContents?.isDestroyed, win?.webContents?.isDestroyed?.());
291
+ if (!(win && win?.webContents?.isDestroyed && !win?.webContents?.isDestroyed?.())) {
292
+ this.windows.delete(key);
293
+ }
294
+ if (win?._type === 'BW') {
295
+ bwWindows.set(key, win);
296
+ }
297
+ if (win?._type === 'BV') {
298
+ bvWindows.set(key, win);
142
299
  }
143
300
  });
144
- return win;
145
- }
146
- getAll() {
301
+ if (type === 'BW') {
302
+ return bwWindows;
303
+ }
304
+ if (type === 'BV') {
305
+ return bvWindows;
306
+ }
147
307
  return this.windows;
148
308
  }
149
- close(name) {
309
+ close(idOrName) {
310
+ log('log', 'close', idOrName);
150
311
  let win = undefined;
151
312
  this.windows.forEach((i) => {
152
- if (i._name === name) {
153
- win = i;
313
+ if (typeof idOrName === 'number') {
314
+ if (i?.id === idOrName || i?._id === idOrName) {
315
+ win = i;
316
+ }
317
+ }
318
+ else if (typeof idOrName === 'string') {
319
+ if (i?._name === idOrName) {
320
+ win = i;
321
+ }
154
322
  }
155
323
  });
156
324
  // @ts-ignore
157
325
  win && this.windows.delete(win?.id);
158
326
  // @ts-ignore
159
- return win && win?.destroy();
327
+ if (win) {
328
+ // @ts-ignore
329
+ if (win._type === 'BV') {
330
+ this.windows.forEach(i => {
331
+ if (i?._type === 'BW') {
332
+ const _win = i;
333
+ _win.removeBrowserView(win);
334
+ }
335
+ });
336
+ }
337
+ // @ts-ignore
338
+ win?.webContent?.close?.();
339
+ // @ts-ignore
340
+ win?.close?.();
341
+ // @ts-ignore
342
+ win?.destroy?.();
343
+ }
344
+ return true;
160
345
  }
161
- closeById(id) {
162
- let win = undefined;
163
- this.windows.forEach((i) => {
164
- if (i.id === id) {
165
- win = i;
346
+ closeAll() {
347
+ log('log', 'closeAll');
348
+ this.windows.forEach((win) => {
349
+ try {
350
+ win && this.windows.delete(win?.id);
351
+ // @ts-ignore
352
+ if (win._type === 'BV') {
353
+ this.windows.forEach(i => {
354
+ if (i?._type === 'BW') {
355
+ const _win = i;
356
+ _win.removeBrowserView(win);
357
+ }
358
+ });
359
+ }
360
+ // @ts-ignore
361
+ win?.webContent?.close?.();
362
+ // @ts-ignore
363
+ win?.close?.();
364
+ // @ts-ignore
365
+ win?.destroy?.();
366
+ }
367
+ catch (error) {
166
368
  }
167
369
  });
168
- // @ts-ignore
169
- win && this.windows.delete(win?.id);
170
- // @ts-ignore
171
- return win && win?.destroy();
172
370
  }
173
371
  getPreload() {
372
+ log('log', 'getPreload');
174
373
  return this.preload;
175
374
  }
176
375
  }
177
376
  // @ts-ignore
178
377
  global['__ELECTRON_WINDOWS_MANAGER__'] = undefined;
179
378
  let isInitialized = false;
180
- const initialize = (preload) => {
379
+ const initialize = (preload, loadingViewUrl, errorViewUrl) => {
181
380
  // @ts-ignore
182
381
  if (isInitialized && global['__ELECTRON_WINDOWS_MANAGER__']) {
183
382
  // @ts-ignore
@@ -185,38 +384,45 @@ const initialize = (preload) => {
185
384
  }
186
385
  isInitialized = true;
187
386
  // @ts-ignore
188
- const wm = global['__ELECTRON_WINDOWS_MANAGER__'] = new WindowsManager(preload);
387
+ const wm = global['__ELECTRON_WINDOWS_MANAGER__'] = new WindowsManager(preload, loadingViewUrl, errorViewUrl);
189
388
  eIpc.mainIPC.handleRenderer('__ELECTRON_WINDOW_MANAGER_IPC_CHANNEL__', async (data) => {
190
389
  if (data?.type === 'create') {
191
390
  const opt = data;
192
391
  const findWin = wm.get(opt.data.name);
193
392
  if (findWin) {
194
- findWin.focus();
393
+ // @ts-ignore
394
+ findWin?.focus?.();
395
+ findWin?.webContents.focus?.();
396
+ if (opt.data.url && findWin.webContents.getURL() !== opt.data.url) {
397
+ findWin.webContents.loadURL(opt.data.url);
398
+ /** fix: url hash 无法正常加载 */
399
+ setTimeout(() => {
400
+ findWin.webContents.reload();
401
+ }, 100);
402
+ }
195
403
  return {
196
- winId: findWin?.id,
197
- winName: findWin?._name,
404
+ winId: Number(`${findWin?.id || findWin?._id || -1}`),
405
+ winName: `${findWin?._name || ''}`,
406
+ winType: `${findWin?._type || ''}`,
407
+ winExtraData: `${findWin?._extraData || ''}`,
198
408
  };
199
409
  }
200
410
  const res = wm.create(opt.data);
201
411
  return {
202
- winId: Number(res.id),
203
- winName: res._name,
412
+ winId: Number(`${res.id || res._id || -1}`),
413
+ winName: `${res?._name || ''}`,
414
+ winType: `${res?._type || ''}`,
415
+ winExtraData: `${res?._extraData || ''}`,
204
416
  };
205
417
  }
206
418
  if (data?.type === 'get') {
207
419
  const opt = data;
208
420
  const res = wm.get(opt?.data);
209
421
  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 : '',
422
+ winId: Number(`${res?.id || res?._id || -1}`),
423
+ winName: `${res?._name || ''}`,
424
+ winType: `${res?._type || ''}`,
425
+ winExtraData: `${res?._extraData || ''}`,
220
426
  };
221
427
  }
222
428
  if (data?.type === 'getAll') {
@@ -224,9 +430,11 @@ const initialize = (preload) => {
224
430
  const obj = {};
225
431
  res.forEach(i => {
226
432
  // @ts-ignore
227
- obj[i.id] = {
228
- winId: i?.id ? Number(i?.id) : -1,
229
- winName: i?._name ? i?._name : '',
433
+ obj[i.id || i._id] = {
434
+ winId: Number(`${i?.id || i?._id || -1}`),
435
+ winName: `${i?._name || ''}`,
436
+ winType: `${i?._type || ''}`,
437
+ winExtraData: `${i?._extraData || ''}`,
230
438
  };
231
439
  });
232
440
  return obj;
@@ -236,11 +444,37 @@ const initialize = (preload) => {
236
444
  const res = wm.close(opt?.data);
237
445
  return res;
238
446
  }
239
- if (data?.type === 'closeById') {
240
- const opt = data;
241
- const res = wm.closeById(opt?.data);
447
+ if (data?.type === 'closeAll') {
448
+ const res = wm.closeAll();
242
449
  return res;
243
450
  }
451
+ if (data?.type === 'getWindowForWebContentId') {
452
+ const opt = data;
453
+ const targetWebContents = webContents.fromId(opt.data);
454
+ if (targetWebContents) {
455
+ let win = BrowserWindow.fromWebContents(targetWebContents);
456
+ if (!win) {
457
+ // 获取所有的 BrowserWindows
458
+ let allWindows = BrowserWindow.getAllWindows();
459
+ // 遍历所有窗口,检查每个窗口的 BrowserView
460
+ for (let _win of allWindows) {
461
+ let views = _win.getBrowserViews();
462
+ // 遍历窗口的所有 BrowserView
463
+ for (let view of views) {
464
+ if (view.webContents === targetWebContents) {
465
+ win = _win;
466
+ break;
467
+ }
468
+ }
469
+ if (win)
470
+ break;
471
+ }
472
+ }
473
+ // @ts-ignore
474
+ return win?.id || win?._id;
475
+ }
476
+ return undefined;
477
+ }
244
478
  if (data?.type === 'getPreload') {
245
479
  const res = wm.getPreload();
246
480
  return res;
@@ -249,6 +483,15 @@ const initialize = (preload) => {
249
483
  });
250
484
  return wm;
251
485
  };
486
+ const log = (type, ...data) => {
487
+ const key = `[electron-window-manager]: `;
488
+ try {
489
+ console[type](key, ...data);
490
+ }
491
+ catch (error) {
492
+ console.error(key, error);
493
+ }
494
+ };
252
495
 
253
- export { WindowsManager, enable, initialize, isInitialized };
496
+ export { WindowsManager, enable, getCustomSession, initialize, isInitialized };
254
497
  //# sourceMappingURL=index.js.map