import { app, Menu } from 'electron'; import { revealLogFile } from '../logger'; import { checkForUpdatesAndNotify } from '../updater'; import { isMacOS } from '../utils'; import { applicationMenuSubjects } from './subject'; // Unique id for menuitems const MENUITEM_NEW_PAGE = 'affine:new-page'; export function createApplicationMenu() { const isMac = isMacOS(); // Electron menu cannot be modified // You have to copy the complete default menu template event if you want to add a single custom item // See https://www.electronjs.org/docs/latest/api/menu#examples const template = [ // { role: 'appMenu' } ...(isMac ? [ { label: app.name, submenu: [ { role: 'about' }, { type: 'separator' }, { role: 'services' }, { type: 'separator' }, { role: 'hide' }, { role: 'hideOthers' }, { role: 'unhide' }, { type: 'separator' }, { role: 'quit' }, ], }, ] : []), // { role: 'fileMenu' } { label: 'File', submenu: [ { id: MENUITEM_NEW_PAGE, label: 'New Page', accelerator: isMac ? 'Cmd+N' : 'Ctrl+N', click: () => { applicationMenuSubjects.newPageAction.next(); }, }, { type: 'separator' }, isMac ? { role: 'close' } : { role: 'quit' }, ], }, // { role: 'editMenu' } { label: 'Edit', submenu: [ { role: 'undo' }, { role: 'redo' }, { type: 'separator' }, { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, ...(isMac ? [ { role: 'pasteAndMatchStyle' }, { role: 'delete' }, { role: 'selectAll' }, { type: 'separator' }, { label: 'Speech', submenu: [{ role: 'startSpeaking' }, { role: 'stopSpeaking' }], }, ] : [{ role: 'delete' }, { type: 'separator' }, { role: 'selectAll' }]), ], }, // { role: 'viewMenu' } { label: 'View', submenu: [ { role: 'reload' }, { role: 'forceReload' }, { role: 'toggleDevTools' }, { type: 'separator' }, { role: 'resetZoom' }, { role: 'zoomIn' }, { role: 'zoomOut' }, { type: 'separator' }, { role: 'togglefullscreen' }, ], }, // { role: 'windowMenu' } { label: 'Window', submenu: [ { role: 'minimize' }, { role: 'zoom' }, ...(isMac ? [ { type: 'separator' }, { role: 'front' }, { type: 'separator' }, { role: 'window' }, ] : [{ role: 'close' }]), ], }, { role: 'help', submenu: [ { label: 'Learn More', click: async () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const { shell } = require('electron'); await shell.openExternal('https://affine.pro/'); }, }, { label: 'Open log file', click: async () => { await revealLogFile(); }, }, { label: 'Check for Updates', click: async () => { await checkForUpdatesAndNotify(true); }, }, ], }, ]; // @ts-expect-error: The snippet is copied from Electron official docs. // It's working as expected. No idea why it contains type errors. // Just ignore for now. const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); return menu; }