/* eslint-disable prettier/prettier */ /* eslint-disable @typescript-eslint/no-explicit-any */ import { electronApp, is, optimizer } from '@electron-toolkit/utils' import { app, BrowserWindow, globalShortcut, ipcMain, Menu, screen, shell, Tray } from 'electron' import fs from 'fs' import path, { join } from 'path' import icon from '../../resources/icon.png?asset' let mainWindow: null | BrowserWindow = null let isQuiting = false const startupFlagFile = path.join(app.getPath('userData'), 'startup-set.flag') function createWindow(): void { // Get Screen width, height const { width, height } = screen.getPrimaryDisplay().workAreaSize mainWindow = new BrowserWindow({ width: width / 2, height: height / 2, show: false, autoHideMenuBar: true, ...(process.platform === 'linux' ? { icon } : {}), webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false } }) // Set App Show Position mainWindow.setPosition(width / 2, height / 2) mainWindow.on('ready-to-show', () => { mainWindow?.show() }) mainWindow.webContents.setWindowOpenHandler((details) => { shell.openExternal(details.url) return { action: 'deny' } }) // Make the window always on top mainWindow.setAlwaysOnTop(true, 'normal') // mainWindow.webContents.openDevTools() // Inspect element with shortcut mainWindow.webContents.once('did-finish-load', () => { const shortcut = 'Control+Shift+C' mainWindow?.on('focus', () => { globalShortcut.register(shortcut, () => { const pos = screen.getCursorScreenPoint() mainWindow?.webContents.inspectElement(pos.x, pos.y) mainWindow?.webContents.focus() // optional: refocus back }) }) mainWindow?.on('blur', () => { globalShortcut.unregister(shortcut) }) mainWindow?.on('closed', () => { globalShortcut.unregister(shortcut) }) }) // HMR for renderer base on electron-vite cli. // Load the remote URL for development or the local html file for production. if (is.dev && process.env['ELECTRON_RENDERER_URL']) { mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) } else { mainWindow.loadFile(join(__dirname, '../renderer/index.html')) } // Khi bấm dấu X mainWindow.on('close', (event) => { if (!isQuiting) { event.preventDefault() mainWindow?.hide() } }) } function createTray() { const tray = new Tray(icon) const contextMenu = Menu.buildFromTemplate([ { label: 'Show', click: () => { mainWindow?.show() } }, { label: 'Quit', click: () => { isQuiting = true app.quit() } } ]) tray.setToolTip('Zulip notes') tray.setContextMenu(contextMenu) tray.on('double-click', () => { mainWindow?.show() }) } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.whenReady().then(() => { if (!fs.existsSync(startupFlagFile)) { app.setLoginItemSettings({ openAtLogin: true, path: process.execPath, args: [] }) fs.writeFileSync(startupFlagFile, 'ok') } // Set app user model id for windows electronApp.setAppUserModelId('com.electron') // Default open or close DevTools by F12 in development // and ignore CommandOrControl + R in production. // see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils app.on('browser-window-created', (_, window) => { optimizer.watchWindowShortcuts(window) }) // IPC test ipcMain.on('ping', () => console.log('pong')) // Create main window createWindow() // Create tray icon createTray() app.on('activate', function () { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits // explicitly with Cmd + Q. app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit() } }) // In this file you can include the rest of your app's specific main process // code. You can also put them in separate files and require them here. // Custom events ipcMain.handle('save-config-file', async (_event, jsonContent: any) => { // const filePath = path.join(__dirname, 'config-data.json') const userDataPath = app.getPath('userData') const filePath = path.join(userDataPath, 'config-data.json') try { let fileData: any = {} // 👀 Kiểm tra nếu file đã tồn tại if (fs.existsSync(filePath)) { const rawData = fs.readFileSync(filePath, 'utf-8') fileData = JSON.parse(rawData) } const now = new Date().getTime() // 📦 Ghi dữ liệu mới const newData = { ...jsonContent, // giữ dữ liệu mới (nếu bạn muốn merge thì sửa dòng này) created_at: fileData.created_at || now, // nếu có created_at thì giữ nguyên, không có thì lấy now updated_at: now // luôn cập nhật updated_at } fs.writeFileSync(filePath, JSON.stringify(newData, null, 2), 'utf-8') return { success: true, path: filePath } } catch (error: any) { console.error('Error saving file:', error) return { success: false, error: error.message } } }) ipcMain.handle('get-config-file', async () => { const userDataPath = app.getPath('userData') const filePath = path.join(userDataPath, 'config-data.json') try { // 📂 Kiểm tra file có tồn tại không if (!fs.existsSync(filePath)) { return { success: false, error: 'Config file does not exist' } } const rawData = fs.readFileSync(filePath, 'utf-8') const fileData = JSON.parse(rawData) return fileData } catch (error: any) { console.error('Error reading config file:', error) return null } }) ipcMain.handle('show-window', async () => { mainWindow?.show() }) ipcMain.handle('hide-window', async () => { mainWindow?.hide() })