@@ -4,11 +4,14 @@ import initLoggingEventHandlers from "./ipc/LoggingEventHandlers";
44import { EventType } from "./enums" ;
55import { attachTitlebarToWindow , setupTitlebar } from "custom-electron-titlebar/main" ;
66import * as Environment from "./utils/EnvironmentUtils" ;
7- import { fileToTab , readEditorState } from "./utils/EditorUtils" ;
7+ import { fileToTab , openInWindow , readEditorState } from "./utils/EditorUtils" ;
88import initIpcMainListeners from "./ipc/IpcMainListener" ;
99import { initApplicationMenu , loadThemeToWindow , refreshTitlebar } from "./utils/ElectronUtils" ;
1010import { EditorState } from "./domain/EditorState" ;
1111import { SupportedLanguages } from './domain/SupportedLanguage' ;
12+ import { hash } from "./utils/TextUtils" ;
13+ import { isFileExists } from "./utils/FileUtils" ;
14+
1215
1316setupTitlebar ( ) ;
1417
@@ -32,8 +35,10 @@ process.on('unhandledRejection', (reason: any) => {
3235 logError ( reason ) ;
3336} ) ;
3437
38+ let mainWindow : BrowserWindow ;
39+
3540const createWindow = ( ) => {
36- const mainWindow = new BrowserWindow ( {
41+ mainWindow = new BrowserWindow ( {
3742 width : 1440 ,
3843 height : 860 ,
3944 titleBarStyle : "hidden" ,
@@ -71,33 +76,9 @@ const createWindow = () => {
7176 loadThemeToWindow ( mainWindow ) ;
7277 refreshTitlebar ( ) ;
7378
74- const fileArg = getFileArgument ( ) ;
75- const activeTabId = crypto . randomUUID ( ) ;
76- let initialState : EditorState = {
77- tabs : [ {
78- id : activeTabId ,
79- name : "New Document.txt" ,
80- displayName : "New Document.txt" ,
81- content : "" ,
82- language : SupportedLanguages . text ,
83- } ] ,
84- activeTabId : activeTabId
85- }
86-
87- const persistentState = readEditorState ( ) ;
79+ const state = await getEditorState ( ) ;
8880
89- if ( persistentState ) { // restore the state
90- initialState = persistentState ;
91- }
92-
93- if ( fileArg && ! initialState . tabs . map ( t => t . file ) . includes ( fileArg ) ) {
94- const tab = await fileToTab ( fileArg ) ;
95-
96- initialState . tabs . push ( tab ) ;
97- initialState . activeTabId = tab . id ;
98- }
99-
100- mainWindow . webContents . send ( EventType . INIT_WITH_STATE , initialState ) ;
81+ mainWindow . webContents . send ( EventType . INIT_WITH_STATE , state ) ;
10182 } ) ;
10283
10384 mainWindow . on ( EventType . CLOSE_WINDOW , ( event ) => {
@@ -107,22 +88,84 @@ const createWindow = () => {
10788 } )
10889} ;
10990
110- // This method will be called when Electron has finished
111- // initialization and is ready to create browser windows.
112- // Some APIs can only be used after this event occurs.
113- app . whenReady ( ) . then ( ( ) => {
114- Environment . loadPreferences ( ) ; // load user preferences
91+ async function getEditorState ( ) {
92+ const fileArg = getFileArgument ( ) ;
93+
94+ const tempTabId = crypto . randomUUID ( ) ;
95+ let initialState : EditorState = {
96+ tabs : [ {
97+ id : tempTabId ,
98+ name : "New Document.txt" ,
99+ displayName : "New Document.txt" ,
100+ content : "" ,
101+ hash : await hash ( "" ) ,
102+ language : SupportedLanguages . text ,
103+ } ] ,
104+ activeTabId : tempTabId
105+ }
106+
107+ const persistentState = readEditorState ( ) ;
108+
109+ if ( persistentState ) { // restore the state
110+ initialState = persistentState ;
111+ }
112+
113+ if ( fileArg && ! initialState . tabs . map ( t => t . file ) . includes ( fileArg ) ) {
114+ const tab = await fileToTab ( fileArg ) ;
115+
116+ initialState . tabs . push ( tab ) ;
117+ initialState . activeTabId = tab . id ;
118+ }
119+
120+ return initialState ;
121+ }
122+
123+ if ( ! app . requestSingleInstanceLock ( ) ) {
124+ app . quit ( ) ; // Quit if another instance is already running
125+ } else {
126+ app . on ( 'second-instance' , ( _ , argv ) => {
127+ // When a second instance is launched
128+ if ( mainWindow ) {
129+ // Focus the existing window
130+ if ( mainWindow . isMinimized ( ) ) mainWindow . restore ( ) ;
131+ mainWindow . focus ( ) ;
132+
133+ // Pass the file argument (e.g., argv[2] on Windows, argv[1] on macOS)
134+ const fileArg = getFileArgument ( argv ) ;
135+ if ( fileArg ) {
136+ openInWindow ( mainWindow , fileArg ) ;
137+ }
138+ }
139+ } ) ;
140+
141+ // This method will be called when Electron has finished
142+ // initialization and is ready to create browser windows.
143+ // Some APIs can only be used after this event occurs.
144+ app . whenReady ( ) . then ( ( ) => {
145+ Environment . loadPreferences ( ) ; // load user preferences
146+
147+ const arg = getFileArgument ( ) ; // open with...
115148
116- createWindow ( ) ;
149+ if ( arg && mainWindow ) {
150+ const targetWindow = mainWindow ;
117151
118- // On OS X it's common to re-create a window in the app when the
119- // dock icon is clicked and there are no other windows open.
120- app . on ( 'activate' , ( ) => {
121- if ( BrowserWindow . getAllWindows ( ) . length === 0 ) {
152+ openInWindow ( targetWindow , arg ) ;
153+
154+ targetWindow . moveTop ( ) ;
155+ targetWindow . focus ( ) ;
156+ } else {
122157 createWindow ( ) ;
123158 }
159+
160+ // On OS X it's common to re-create a window in the app when the
161+ // dock icon is clicked and there are no other windows open.
162+ app . on ( 'activate' , ( ) => {
163+ if ( BrowserWindow . getAllWindows ( ) . length === 0 ) {
164+ createWindow ( ) ;
165+ }
166+ } ) ;
124167 } ) ;
125- } ) ;
168+ }
126169
127170// Quit when all windows are closed, except on macOS. There, it's common
128171// for applications and their menu bar to stay active until the user quits
@@ -133,12 +176,22 @@ app.on('window-all-closed', () => {
133176 }
134177} ) ;
135178
136- function getFileArgument ( ) {
137- if ( app . isPackaged && process . argv . length > 1 ) { // 'open with ...' option
138- const file = process . argv [ 1 ] ;
179+ function getFileArgument ( args ?: string [ ] ) {
180+ let result ;
139181
140- if ( file && file !== "." && file !== "./" ) return file ;
182+ const processArguments = args || process . argv ;
183+
184+ if ( app . isPackaged && processArguments ?. length > 1 ) { // 'open with ...' option
185+ processArguments ?. forEach ( ( arg , idx ) => {
186+ if ( idx === 0 ) return ; // pass the first argument, it is the working directory
187+
188+ if ( arg && arg !== "." && arg !== "./" ) {
189+ if ( isFileExists ( arg ) ) result = arg ;
190+ }
191+ } ) ;
141192 }
193+
194+ return result ;
142195}
143196
144197initLoggingEventHandlers ( ) ;
0 commit comments