1- import electron , { type BrowserWindow as ElectronBrowserWindow , type MenuItemConstructorOptions } from 'electron' ;
1+ import {
2+ app ,
3+ BrowserWindow ,
4+ type BrowserWindow as ElectronBrowserWindow ,
5+ clipboard ,
6+ dialog ,
7+ Menu ,
8+ type MenuItemConstructorOptions ,
9+ screen ,
10+ shell ,
11+ } from 'electron' ;
212import fs from 'fs' ;
313import * as os from 'os' ;
414import path from 'path' ;
@@ -16,17 +26,15 @@ import {
1626} from '../common/constants' ;
1727import { docsBase } from '../common/documentation' ;
1828import * as log from '../common/log' ;
29+ import { invariant } from '../utils/invariant' ;
1930import LocalStorage from './local-storage' ;
2031
21- const { app, Menu, shell, dialog, clipboard, BrowserWindow } = electron ;
22-
2332const DEFAULT_WIDTH = 1280 ;
2433const DEFAULT_HEIGHT = 720 ;
2534const MINIMUM_WIDTH = 500 ;
2635const MINIMUM_HEIGHT = 400 ;
2736
28- let newWindow : ElectronBrowserWindow | null = null ;
29- const windows = new Set < ElectronBrowserWindow > ( ) ;
37+ const browserWindows = new Map < 'Insomnia' | 'HiddenBrowserWindow' , ElectronBrowserWindow > ( ) ;
3038let localStorage : LocalStorage | null = null ;
3139
3240interface Bounds {
@@ -40,14 +48,67 @@ export function init() {
4048 initLocalStorage ( ) ;
4149}
4250
43- export function createWindow ( ) {
51+ export async function createHiddenBrowserWindow ( ) : Promise < ElectronBrowserWindow > {
52+ // if open, close it
53+ if ( browserWindows . get ( 'HiddenBrowserWindow' ) ) {
54+ await new Promise < void > ( resolve => {
55+ const hiddenBrowserWindow = browserWindows . get ( 'HiddenBrowserWindow' ) ;
56+ invariant ( hiddenBrowserWindow , 'hiddenBrowserWindow is not defined' ) ;
57+
58+ // overwrite the closed handler
59+ hiddenBrowserWindow . on ( 'closed' , ( ) => {
60+ if ( hiddenBrowserWindow ) {
61+ console . log ( '[main] restarting hidden browser window' ) ;
62+ browserWindows . delete ( 'HiddenBrowserWindow' ) ;
63+ }
64+ resolve ( ) ;
65+ } ) ;
66+
67+ stopHiddenBrowserWindow ( ) ;
68+ } ) ;
69+ }
70+ const hiddenBrowserWindow = new BrowserWindow ( {
71+ show : false ,
72+ title : 'HiddenBrowserWindow' ,
73+ webPreferences : {
74+ sandbox : true ,
75+ contextIsolation : true ,
76+ nodeIntegration : false ,
77+ webSecurity : true ,
78+ preload : path . join ( __dirname , 'renderers/hidden-browser-window/preload.js' ) ,
79+ spellcheck : false ,
80+ } ,
81+ } ) ;
82+ browserWindows . set ( 'HiddenBrowserWindow' , hiddenBrowserWindow ) ;
83+
84+ const hiddenBrowserWindowPath = path . resolve ( __dirname , './renderers/hidden-browser-window/index.html' ) ;
85+ const hiddenBrowserWindowUrl = process . env . HIDDEN_BROWSER_WINDOW_URL || pathToFileURL ( hiddenBrowserWindowPath ) . href ;
86+ hiddenBrowserWindow . loadURL ( hiddenBrowserWindowUrl ) ;
87+
88+ console . log ( '[main][init hidden win step 1/6]: starting hidden browser window' ) ;
89+
90+ hiddenBrowserWindow . on ( 'closed' , ( ) => {
91+ if ( browserWindows . get ( 'HiddenBrowserWindow' ) ) {
92+ console . log ( '[main] closing hidden browser window' ) ;
93+ browserWindows . delete ( 'HiddenBrowserWindow' ) ;
94+ }
95+ } ) ;
96+
97+ return hiddenBrowserWindow ;
98+ }
99+
100+ export function stopHiddenBrowserWindow ( ) {
101+ browserWindows . get ( 'HiddenBrowserWindow' ) ?. close ( ) ;
102+ }
103+
104+ export function createWindow ( ) : ElectronBrowserWindow {
44105 const { bounds, fullscreen, maximize } = getBounds ( ) ;
45106 const { x, y, width, height } = bounds ;
46107
47108 const appLogo = 'static/insomnia-core-logo_16x.png' ;
48109 let isVisibleOnAnyDisplay = true ;
49110
50- for ( const d of electron . screen . getAllDisplays ( ) ) {
111+ for ( const d of screen . getAllDisplays ( ) ) {
51112 const isVisibleOnDisplay =
52113 // @ts -expect-error -- TSCONVERSION genuine error
53114 x >= d . bounds . x &&
@@ -63,7 +124,7 @@ export function createWindow() {
63124 }
64125 }
65126
66- newWindow = new BrowserWindow ( {
127+ const mainBrowserWindow = new BrowserWindow ( {
67128 // Make sure we don't initialize the window outside the bounds
68129 x : isVisibleOnAnyDisplay ? x : undefined ,
69130 y : isVisibleOnAnyDisplay ? y : undefined ,
@@ -88,22 +149,23 @@ export function createWindow() {
88149 disableBlinkFeatures : 'Auxclick' ,
89150 } ,
90151 } ) ;
152+ browserWindows . set ( 'Insomnia' , mainBrowserWindow ) ;
91153
92154 // BrowserWindow doesn't have an option for this, so we have to do it manually :(
93155 if ( maximize ) {
94- newWindow ? .maximize ( ) ;
156+ mainBrowserWindow . maximize ( ) ;
95157 }
96158
97- newWindow ? .on ( 'resize' , ( ) => saveBounds ( ) ) ;
98- newWindow ? .on ( 'maximize' , ( ) => saveBounds ( ) ) ;
99- newWindow ? .on ( 'unmaximize' , ( ) => saveBounds ( ) ) ;
100- newWindow ? .on ( 'move' , ( ) => saveBounds ( ) ) ;
101- newWindow ? .on ( 'unresponsive' , ( ) => {
159+ mainBrowserWindow . on ( 'resize' , ( ) => saveBounds ( ) ) ;
160+ mainBrowserWindow . on ( 'maximize' , ( ) => saveBounds ( ) ) ;
161+ mainBrowserWindow . on ( 'unmaximize' , ( ) => saveBounds ( ) ) ;
162+ mainBrowserWindow . on ( 'move' , ( ) => saveBounds ( ) ) ;
163+ mainBrowserWindow . on ( 'unresponsive' , ( ) => {
102164 showUnresponsiveModal ( ) ;
103165 } ) ;
104166
105167 // Open generic links (<a .../>) in default browser
106- newWindow ? .webContents . on ( 'will-navigate' , ( event , url ) => {
168+ mainBrowserWindow . webContents . on ( 'will-navigate' , ( event , url ) => {
107169 // Prevents local dev full-reload events from opening browser window, see https://github.com/Kong/insomnia/pull/4925
108170 if ( url . startsWith ( appUrl ) ) {
109171 return ;
@@ -118,7 +180,7 @@ export function createWindow() {
118180 }
119181 } ) ;
120182
121- newWindow ? .webContents . setWindowOpenHandler ( ( ) => {
183+ mainBrowserWindow . webContents . setWindowOpenHandler ( ( ) => {
122184 return { action : 'deny' } ;
123185 } ) ;
124186
@@ -127,12 +189,11 @@ export function createWindow() {
127189 const appUrl = process . env . APP_RENDER_URL || pathToFileURL ( appPath ) . href ;
128190
129191 console . log ( `[main] Loading ${ appUrl } ` ) ;
130- newWindow ? .loadURL ( appUrl ) ;
192+ mainBrowserWindow . loadURL ( appUrl ) ;
131193 // Emitted when the window is closed.
132- newWindow ?. on ( 'closed' , ( ) => {
133- if ( newWindow ) {
134- windows . delete ( newWindow ) ;
135- newWindow = windows . values ( ) . next ( ) . value || null ;
194+ mainBrowserWindow . on ( 'closed' , ( ) => {
195+ if ( browserWindows . get ( 'Insomnia' ) ) {
196+ browserWindows . delete ( 'Insomnia' ) ;
136197 }
137198 } ) ;
138199
@@ -259,23 +320,23 @@ export function createWindow() {
259320 {
260321 label : `Resize to ${ MNEMONIC_SYM } Small (qHD 540)` ,
261322 click : ( ) =>
262- newWindow ? .setBounds ( {
323+ mainBrowserWindow . setBounds ( {
263324 width : 960 ,
264325 height : 540 ,
265326 } ) ,
266327 } ,
267328 {
268329 label : `Resize to Defaul${ MNEMONIC_SYM } t (HD 720)` ,
269330 click : ( ) =>
270- newWindow ? .setBounds ( {
331+ mainBrowserWindow . setBounds ( {
271332 width : DEFAULT_WIDTH ,
272333 height : DEFAULT_HEIGHT ,
273334 } ) ,
274335 } ,
275336 {
276337 label : `Resize to ${ MNEMONIC_SYM } Large (FHD 1080)` ,
277338 click : ( ) =>
278- newWindow ? .setBounds ( {
339+ mainBrowserWindow . setBounds ( {
279340 width : 1920 ,
280341 height : 1080 ,
281342 } ) ,
@@ -367,7 +428,7 @@ export function createWindow() {
367428 {
368429 label : `Show App ${ MNEMONIC_SYM } Data Folder` ,
369430 click : ( ) => {
370- const directory = process . env [ 'INSOMNIA_DATA_PATH' ] || electron . app . getPath ( 'userData' ) ;
431+ const directory = process . env [ 'INSOMNIA_DATA_PATH' ] || app . getPath ( 'userData' ) ;
371432 shell . showItemInFolder ( directory ) ;
372433 } ,
373434 } ,
@@ -445,10 +506,10 @@ export function createWindow() {
445506 helpMenu . submenu ?. push ( {
446507 type : 'separator' ,
447508 } ,
448- {
449- label : `${ MNEMONIC_SYM } About` ,
450- click : aboutMenuClickHandler ,
451- } ) ;
509+ {
510+ label : `${ MNEMONIC_SYM } About` ,
511+ click : aboutMenuClickHandler ,
512+ } ) ;
452513 }
453514
454515 const developerMenu : MenuItemConstructorOptions = {
@@ -470,7 +531,7 @@ export function createWindow() {
470531 label : `Take ${ MNEMONIC_SYM } Screenshot` ,
471532 click : function ( ) {
472533 // @ts -expect-error -- TSCONVERSION not accounted for in the electron types to provide a function
473- newWindow ? .capturePage ( image => {
534+ mainBrowserWindow . capturePage ( image => {
474535 const buffer = image . toPNG ( ) ;
475536 const dir = app . getPath ( 'desktop' ) ;
476537 fs . writeFileSync ( path . join ( dir , `Screenshot-${ new Date ( ) } .png` ) , buffer ) ;
@@ -496,7 +557,7 @@ export function createWindow() {
496557 {
497558 label : `Set window for ${ MNEMONIC_SYM } FHD Screenshot` ,
498559 click : ( ) => {
499- newWindow ? .setBounds ( {
560+ mainBrowserWindow . setBounds ( {
500561 width : 1920 ,
501562 height : 1080 ,
502563 } ) ;
@@ -546,8 +607,7 @@ export function createWindow() {
546607 }
547608
548609 Menu . setApplicationMenu ( Menu . buildFromTemplate ( template ) ) ;
549- windows . add ( newWindow ) ;
550- return newWindow ;
610+ return mainBrowserWindow ;
551611}
552612
553613async function showUnresponsiveModal ( ) {
@@ -646,10 +706,10 @@ export const setZoom = (transformer: (current: number) => number) => () => {
646706} ;
647707
648708function initLocalStorage ( ) {
649- const localStoragePath = path . join ( process . env [ 'INSOMNIA_DATA_PATH' ] || electron . app . getPath ( 'userData' ) , 'localStorage' ) ;
709+ const localStoragePath = path . join ( process . env [ 'INSOMNIA_DATA_PATH' ] || app . getPath ( 'userData' ) , 'localStorage' ) ;
650710 localStorage = new LocalStorage ( localStoragePath ) ;
651711}
652712
653713export function getOrCreateWindow ( ) {
654- return newWindow ?? createWindow ( ) ;
714+ return browserWindows . get ( 'Insomnia' ) ?? createWindow ( ) ;
655715}
0 commit comments