const electron = require('electron'); const updater = require('electron-updater'); const fs = require('fs'); const os = require('os'); const path = require('path'); const process = require('process'); const url = require('url'); class Application { constructor() { this._views = new ViewCollection(); this._configuration = new ConfigurationService(); this._menu = new MenuService(); this._openQueue = []; electron.app.setAppUserModelId('com.lutzroeder.netron'); electron.app.allowRendererProcessReuse = true; if (!electron.app.requestSingleInstanceLock()) { electron.app.quit(); return; } electron.app.on('second-instance', (event, commandLine, workingDirectory) => { const currentDirectory = process.cwd(); process.chdir(workingDirectory); const open = this._parseCommandLine(commandLine); process.chdir(currentDirectory); if (!open) { if (this._views.count > 0) { const view = this._views.item(0); if (view) { view.restore(); } } } }); electron.ipcMain.on('open-file-dialog', () => { this._openFileDialog(); }); electron.ipcMain.on('get-environment', (event) => { event.returnValue = { version: electron.app.getVersion(), package: electron.app.isPackaged }; }); electron.ipcMain.on('get-configuration', (event, obj) => { event.returnValue = this._configuration.has(obj.name) ? this._configuration.get(obj.name) : undefined; }); electron.ipcMain.on('set-configuration', (event, obj) => { this._configuration.set(obj.name, obj.value); }); electron.ipcMain.on('drop-paths', (event, data) => { const paths = data.paths.filter((path) => { if (fs.existsSync(path)) { const stat = fs.statSync(path); return stat.isFile() || stat.isDirectory(); } return false; }); this._dropPaths(event.sender, paths); }); electron.ipcMain.on('show-message-box', (event, options) => { const owner = event.sender.getOwnerBrowserWindow(); event.returnValue = electron.dialog.showMessageBoxSync(owner, options); }); electron.ipcMain.on('show-save-dialog', (event, options) => { const owner = event.sender.getOwnerBrowserWindow(); event.returnValue = electron.dialog.showSaveDialogSync(owner, options); }); electron.app.on('will-finish-launching', () => { electron.app.on('open-file', (event, path) => { this._openPath(path); }); }); electron.app.on('ready', () => { this._ready(); }); electron.app.on('window-all-closed', () => { if (process.platform !== 'darwin') { electron.app.quit(); } }); electron.app.on('will-quit', () => { this._configuration.save(); }); this._parseCommandLine(process.argv); this._checkForUpdates(); } _parseCommandLine(argv) { let open = false; if (argv.length > 1) { for (const arg of argv.slice(1)) { if (!arg.startsWith('-') && arg !== path.dirname(__dirname)) { const extension = path.extname(arg).toLowerCase(); if (extension != '' && extension != 'js' && fs.existsSync(arg)) { const stat = fs.statSync(arg); if (stat.isFile() || stat.isDirectory()) { this._openPath(arg); open = true; } } } } } return open; } _ready() { this._configuration.load(); if (!this._configuration.has('userId')) { this._configuration.set('userId', this._uuid()); } if (this._openQueue) { const queue = this._openQueue; this._openQueue = null; while (queue.length > 0) { const file = queue.shift(); this._openPath(file); } } if (this._views.count == 0) { this._views.openView(); } this._resetMenu(); this._views.on('active-view-changed', () => { this._updateMenu(); }); this._views.on('active-view-updated', () => { this._updateMenu(); }); } _uuid() { const buffer = new Uint8Array(16); require("crypto").randomFillSync(buffer); buffer[6] = buffer[6] & 0x0f | 0x40; buffer[8] = buffer[8] & 0x3f | 0x80; const code = Array.from(buffer).map((value) => value < 0x10 ? '0' + value.toString(16) : value.toString(16)).join(''); return code.slice(0, 8) + '-' + code.slice(8, 12) + '-' + code.slice(12, 16) + '-' + code.slice(16, 20) + '-' + code.slice(20, 32); } _openFileDialog() { const showOpenDialogOptions = { properties: [ 'openFile' ], filters: [ { name: 'All Model Files', extensions: [ 'onnx', 'ort', 'pb', 'h5', 'hd5', 'hdf5', 'json', 'keras', 'mlmodel', 'mlpackage', 'caffemodel', 'model', 'dnn', 'cmf', 'mar', 'params', 'pdmodel', 'pdparams', 'nb', 'meta', 'tflite', 'lite', 'tfl', 'armnn', 'mnn', 'nn', 'uff', 'uff.txt', 'rknn', 'xmodel', 'kmodel', 'ncnn', 'param', 'tnnproto', 'tmfile', 'ms', 'om', 'pt', 'pth', 'ptl', 't7', 'pkl', 'joblib', 'pbtxt', 'prototxt', 'cfg', 'xml', 'zip', 'tar' ] } ] }; const selectedFiles = electron.dialog.showOpenDialogSync(showOpenDialogOptions); if (selectedFiles) { for (const file of selectedFiles) { this._openPath(file); } } } _openPath(path) { if (this._openQueue) { this._openQueue.push(path); return; } if (path && path.length > 0 && fs.existsSync(path)) { const stat = fs.statSync(path); if (stat.isFile() || stat.isDirectory()) { // find existing view for this file let view = this._views.find(path); // find empty welcome window if (view == null) { view = this._views.find(null); } // create new window if (view == null) { view = this._views.openView(); } this._loadPath(path, view); } } } _loadPath(path, view) { const recents = this._configuration.get('recents').filter((recent) => path != recent.path); view.open(path); recents.unshift({ path: path }); if (recents.length > 9) { recents.splice(9); } this._configuration.set('recents', recents); this._resetMenu(); } _dropPaths(sender, paths) { let view = this._views.from(sender); for (const path of paths) { if (view) { this._loadPath(path, view); view = null; } else { this._openPath(path); } } } _export() { const view = this._views.activeView; if (view && view.path) { let defaultPath = 'Untitled'; const file = view.path; const lastIndex = file.lastIndexOf('.'); if (lastIndex != -1) { defaultPath = file.substring(0, lastIndex); } const owner = electron.BrowserWindow.getFocusedWindow(); const showSaveDialogOptions = { title: 'Export', defaultPath: defaultPath, buttonLabel: 'Export', filters: [ { name: 'PNG', extensions: [ 'png' ] }, { name: 'SVG', extensions: [ 'svg' ] } ] }; const selectedFile = electron.dialog.showSaveDialogSync(owner, showSaveDialogOptions); if (selectedFile) { view.execute('export', { 'file': selectedFile }); } } } service(name) { if (name == 'configuration') { return this._configuration; } return undefined; } execute(command, data) { const view = this._views.activeView; if (view) { view.execute(command, data || {}); } this._updateMenu(); } _reload() { const view = this._views.activeView; if (view && view.path) { this._loadPath(view.path, view); } } _checkForUpdates() { if (!electron.app.isPackaged) { return; } const autoUpdater = updater.autoUpdater; if (autoUpdater.app && autoUpdater.app.appUpdateConfigPath && !fs.existsSync(autoUpdater.app.appUpdateConfigPath)) { return; } const promise = autoUpdater.checkForUpdates(); if (promise) { promise.catch((error) => { /* eslint-disable */ console.log(error.message); /* eslint-enable */ }); } } get package() { if (!this._package) { const file = path.join(path.dirname(__dirname), 'package.json'); const data = fs.readFileSync(file); this._package = JSON.parse(data); this._package.date = new Date(fs.statSync(file).mtime); } return this._package; } _about() { let dialog = null; const options = { show: false, backgroundColor: electron.nativeTheme.shouldUseDarkColors ? '#2d2d2d' : '#e6e6e6', width: 400, height: 250, center: true, minimizable: false, maximizable: false, useContentSize: true, resizable: true, fullscreenable: false, webPreferences: { nodeIntegration: true, } }; if (process.platform === 'darwin') { options.title = ''; dialog = Application._aboutDialog; } else { options.title = 'About ' + electron.app.name; options.parent = electron.BrowserWindow.getFocusedWindow(); options.modal = true; options.showInTaskbar = false; } if (process.platform === 'win32') { options.type = 'toolbar'; } if (!dialog) { dialog = new electron.BrowserWindow(options); if (process.platform === 'darwin') { Application._aboutDialog = dialog; } dialog.removeMenu(); dialog.excludedFromShownWindowsMenu = true; dialog.webContents.on('new-window', (event, url) => { if (url.startsWith('http://') || url.startsWith('https://')) { event.preventDefault(); electron.shell.openExternal(url); } }); let content = fs.readFileSync(path.join(__dirname, 'index.html'), 'utf-8'); content = content.replace('{version}', this.package.version); content = content.replace('