import { app , BrowserWindow , globalShortcut , ipcMain , Menu , MenuItemConstructorOptions , protocol , screen , } from "electron" ; import electronDebug , { openDevTools } from "electron-debug" ; import Store from "electron-store" ; import { enforceMacOSAppLocation } from "electron-util" ; import { init } from "@sentry/node" ; import { join } from "path" ; import { format as formatUrl } from "url" ; import { createProtocol } from "vue-cli-plugin-electron-builder/lib" ; import installExtension , { VUEJS_DEVTOOLS } from "electron-devtools-installer" ; import { destroyDiscord , registerDiscord } from "./lib/discord" ; import { FFMPEG_RETRY , OPEN_DEVTOOLS , REGISTER_MEDIA_KEYS , UNREGISTER_MEDIA_KEYS , } from "./messages" ; import { initUpdateChecker } from "./background/updater" ; import { version } from "../package.json" ; import { SupportedMediaKeys } from "@/types" ; import { clamp , debounce , enumKeysToArray } from "@/utils" ; import { downloadBinariesIfNecessary } from "@/lib/ffmpeg" ; const isDevelopment = process . env . NODE_ENV !== "production" ; if ( isDevelopment ) { require ( "module" ) . globalPaths . push ( process . env . NODE_MODULES_PATH ) ; } init ( { enabled : process . env . NODE_ENV === "production" , dsn : "https://cd3bdb81216e42018409783fedc64b7d@sentry.io/1336205" , environment : process . env . NODE_ENV , release : `v${ version }` , ignoreErrors : [ /operation not permitted/ , /temp-yuna-setup/ , /.*net::ERR.*/ , /child "activity" fails/ , /ENOENT/ , /EPERM/ , /Origin not allowed/ , /code signature/ , /connection closed/ , /RPC_CONNECTION_TIMEOUT/ , /Cannot read property 'write' of null/ , /Redirect was cancelled/ , /no such file/ , /Cannot find latest/ , /Cannot parse releases feed/ , /The play\(\) request was interrupted/ , ] , } ) ; let mainWindow = null ; protocol . registerSchemesAsPrivileged ( [ { scheme : "app" , privileges : { secure : true , } , } , { scheme : "yuna" , privileges : { supportFetchAPI : true , } , } , ] ) ; electronDebug ( { } ) ; const registerMediaKeys = ( window ) => { enumKeysToArray ( SupportedMediaKeys ) . forEach ( ( key ) => globalShortcut . register ( key , ( ) => window . webContents . send ( SupportedMediaKeys [ key ] ) ) ) ; } ; const unregisterMediaKeys = ( ) => { enumKeysToArray ( SupportedMediaKeys ) . forEach ( ( key ) => { globalShortcut . unregister ( key ) ; } ) ; } ; const defaultSize = { width : 1200 , height : 755 , } ; const min = ( num , num2 ) => ( num < num2 ? num : num2 ) ; const max = ( num , num2 ) => ( num >= num2 ? num : num2 ) ; const createMainWindow = ( ) => { const settingsStore = new Store < any > ( { name : "settings" } ) ; const position = settingsStore . get ( "window" , { } ) ; const bounds = screen . getAllDisplays ( ) . reduce ( ( obj , { bounds } ) => { obj . x = [ min ( bounds . x , obj . x [ 0 ] ) , max ( bounds . x + bounds . width , obj . x [ 1 ] ) ] ; obj . y = [ min ( bounds . y , obj . y [ 0 ] ) , max ( bounds . y + bounds . height , obj . y [ 1 ] ) ] ; return obj ; } , { x : [ Infinity , - Infinity ] , y : [ Infinity , - Infinity ] } ) ; const window = new BrowserWindow ( { width : defaultSize . width , height : defaultSize . height , show : false , x : clamp ( position . x , bounds . x [ 0 ] , bounds . x [ 1 ] - defaultSize . width ) , y : clamp ( position . y , bounds . y [ 0 ] , bounds . y [ 1 ] - defaultSize . height ) , maximizable : false , frame : false , darkTheme : true , backgroundColor : "#111" , webPreferences : { webSecurity : false , allowRunningInsecureContent : false , nodeIntegration : ( process . env . ELECTRON_NODE_INTEGRATION as unknown ) as boolean , contextIsolation : false , } , } ) ; const template = [ { label : "Edit" , submenu : [ { type : "normal" , label : "Undo" , accelerator : "CmdOrCtrl+Z" , role : "undo" , } , { type : "normal" , label : "Redo" , accelerator : "Shift+CmdOrCtrl+Z" , role : "redo" , } , { type : "separator" } , { type : "normal" , label : "Cut" , accelerator : "CmdOrCtrl+X" , role : "cut" , } , { type : "normal" , label : "Copy" , accelerator : "CmdOrCtrl+C" , role : "copy" , } , { type : "normal" , label : "Paste" , accelerator : "CmdOrCtrl+V" , role : "paste" , } , { type : "normal" , label : "Select All" , accelerator : "CmdOrCtrl+A" , role : "selectAll" , } , ] , } , ] ; if ( process . platform === "darwin" ) { template . unshift ( { label : "Yuna" , submenu : [ { role : "about" } , { type : "separator" } , { role : "services" } , { type : "separator" } , { role : "hide" } , { role : "hideOthers" } , { role : "unhide" } , { type : "separator" } , { role : "quit" } , ] , } ) ; } Menu . setApplicationMenu ( Menu . buildFromTemplate ( template ) ) ; initUpdateChecker ( ) ; registerDiscord ( ) ; if ( ! process . env . IS_TEST ) { if ( isDevelopment || process . argv . includes ( "--devtools" ) ) { window . webContents . openDevTools ( ) ; } } if ( isDevelopment ) { window . loadURL ( process . env . WEBPACK_DEV_SERVER_URL as string ) ; } else { createProtocol ( "app" ) ; window . loadURL ( formatUrl ( { pathname : join ( __dirname , "index.html" ) , protocol : "file" , slashes : true , } ) ) ; } ipcMain . on ( OPEN_DEVTOOLS , ( ) => openDevTools ( ) ) ; ipcMain . on ( REGISTER_MEDIA_KEYS , ( ) => registerMediaKeys ( window ) ) ; ipcMain . on ( UNREGISTER_MEDIA_KEYS , ( ) => unregisterMediaKeys ( ) ) ; ipcMain . on ( FFMPEG_RETRY , ( ) => downloadBinariesIfNecessary ( mainWindow ! , true ) ) ; const saveWindowLocation = debounce ( ( ) => { settingsStore . set ( "window" , { ... mainWindow ! . getBounds ( ) , ... defaultSize , } ) ; } , 250 ) ; window . on ( "move" , ( ) => saveWindowLocation ( ) ) ; window . on ( "closed" , ( ) => { mainWindow = null ; unregisterMediaKeys ( ) ; } ) ; window . once ( "ready-to-show" , ( ) => { mainWindow ! . show ( ) ; if ( ! settingsStore . get ( "ffmpegFailed" ) ) { downloadBinariesIfNecessary ( mainWindow ! ) ; } } ) ; window . webContents . on ( "devtools-opened" , ( ) => { window . focus ( ) ; setImmediate ( ( ) => { window . focus ( ) ; } ) ; } ) ; return window ; } ; app . allowRendererProcessReuse = false ; app . commandLine . appendSwitch ( "force-color-profile" , "srgb" ) ; app . setAppUserModelId ( process . execPath ) ; app . on ( "window-all-closed" , ( ) => { if ( process . platform !== "darwin" ) { destroyDiscord ( ) ; app . quit ( ) ; } } ) ; app . on ( "activate" , ( ) => { if ( mainWindow === null ) { mainWindow = createMainWindow ( ) ; } } ) ; app . on ( "ready" , async ( ) => { enforceMacOSAppLocation ( ) ; if ( isDevelopment && ! process . env . IS_TEST ) { await installExtension ( VUEJS_DEVTOOLS ) ; } mainWindow = createMainWindow ( ) ; } ) ;