import { app, shell, BrowserWindow, ipcMain } from 'electron' import { join } from 'path' import { readFile, stat, writeFile } from "fs/promises"; import { electronApp, optimizer, is } from '@electron-toolkit/utils' import icon from '../../resources/icon.png?asset' interface Config { range: string queryDeviceKey: string scaleFactor: string } const defaultConfig: Config = { range: '21|108', queryDeviceKey: '', scaleFactor: '1' } const configPath = './config.json' const naturalKeyWidth = 20 const pedalBoardWidth = 239 const height = 200 const getNaturalKeys = (range: string): number => { const [startKeyRaw, endKeyRaw] = range.split('|') const startKey = Number(startKeyRaw) const endKey = Number(endKeyRaw) let naturalKeys = 0 for (let i = startKey; i <= endKey; i += 1) { switch (i % 12) { case 0: case 2: case 4: case 5: case 7: case 9: case 11: naturalKeys += 1 break default: break } } return naturalKeys } function createWindow(config: Config): void { // Create the browser window. const naturalKeys = getNaturalKeys(config.range) const width = naturalKeys * naturalKeyWidth + pedalBoardWidth const mainWindow = new BrowserWindow({ width, height, show: false, autoHideMenuBar: true, ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false, devTools: is.dev }, maximizable: is.dev, minimizable: false, maxHeight: is.dev ? undefined : height, minHeight: is.dev ? undefined : height, minWidth: is.dev ? undefined : width, maxWidth: is.dev ? undefined : width, fullscreenable: false, resizable: is.dev }) mainWindow.on('ready-to-show', () => { mainWindow.show() }) mainWindow.webContents.setWindowOpenHandler((details) => { void shell.openExternal(details.url) return { action: 'deny' } }) let search: URLSearchParams if (is.dev && process.env['ELECTRON_RENDERER_URL']) { const url = new URL(process.env['ELECTRON_RENDERER_URL']) search = new URLSearchParams(url.searchParams) Object.entries(config).forEach(([key, value]) => { search.set(key, value) }) url.search = search.toString() void mainWindow.loadURL(url.toString()) return } search = new URLSearchParams() Object.entries(config).forEach(([key, value]) => { search.set(key, value) }) void mainWindow.loadFile(join(__dirname, '../renderer/index.html'), { search: search.toString() }) } app.whenReady().then(async () => { const effectiveConfig = { ...defaultConfig } try { const theStat = await stat(configPath) if (theStat.isDirectory()) { return } const jsonRaw = await readFile(configPath, 'utf-8') const json = JSON.parse(jsonRaw) Object.entries(json).forEach(([key, value]) => { effectiveConfig[key] = value }) } catch { await writeFile(configPath, JSON.stringify(defaultConfig)) } electronApp.setAppUserModelId('sh.modal.pianomidimonitor') app.on('browser-window-created', (_, window) => { optimizer.watchWindowShortcuts(window) }) ipcMain.on('querydevicekeychange', async (_event, value) => { effectiveConfig.queryDeviceKey = value await writeFile(configPath, JSON.stringify(effectiveConfig)) }) ipcMain.on('scalefactorchange', async (_event, value) => { effectiveConfig.scaleFactor = value await writeFile(configPath, JSON.stringify(effectiveConfig)) }) ipcMain.on('rangechange', async (event, value) => { effectiveConfig.range = value await writeFile(configPath, JSON.stringify(effectiveConfig)) const webContents = event.sender const win = BrowserWindow.fromWebContents(webContents) if (!win) { return } win.close() createWindow(effectiveConfig) }) createWindow(effectiveConfig) app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) { createWindow(effectiveConfig) } }) }) app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } })