Electron Misc Resources

Send message from React <-> Electron - Sync vs Async

In the react app, in the root component:

ts
import { ipcRenderer} from 'electron' const App = () => { useEffect(() => { // on message ipcRenderer.on('export', () => alert("export ON")); // reply ipcRenderer.send("export", {message: "done"}) }, []); return ( ... ) }

To handle the messages/replies from the React app, add this listener in the main.ts electron file:

ts
ipcMain.on("export", (event, message) => { console.log("answer ", message) })

NOTE: export is the channel name and it can be anything

Invoke Electron function from React & Exec shell command

ts
// on Electron ipcMain.handle('exec', async (event, command) => { try { const stdout = execSync(command, {stdio: 'pipe', encoding: 'utf-8'}) return {stdout, stderr: ''} } catch (stderr) { return {stdout: '', stderr} } }) // on React const result = await electron.ipcRenderer.invoke('exec', 'ls -al /tmp')

Sync vs Async

Requests (in the web client):

ts
const {ipcRenderer} = require('electron') // Synch message emmiter and handler console.log(ipcRenderer.sendSync('sync-message', 'sync ping')) // Async message handler ipcRenderer.on('asynchronous-reply', (event, arg) => { console.log(arg) }) // Async message sender ipcRenderer.send('async-message', 'async ping')

Replies (in the electron app):

ts
ipcMain.on('async-message', (event, arg) => { event.sender.send('asynchronous-reply', 'async pong') }) ipcMain.on('sync-message', (event, arg) => { event.returnValue = 'sync pong' })

Open File Browser Dialog

ts
const electron = window.require('electron'); const remote = electron.remote const {BrowserWindow,dialog,Menu} = remote /// const browseFolder = () => { const windowOne = new BrowserWindow() windowOne.loadURL('https://www.electronjs.org/') } /// <button onClick={browseFolder}>Browse...</button>

Get Folder Path (browser):

ts
const browseFolder = async () => { const path = await dialog.showOpenDialog({ properties: ['openDirectory'] }) console.log(path.filePaths); // empty array if cancel }

Get the operating system name:

ts
const isMac = process.platform === 'darwin'

DevTools

Start dev tools console:

ts
mainWindow.webContents.openDevTools()

Prevent dev tools console to open:

ts
win.webContents.on("devtools-opened", () => { win.webContents.closeDevTools(); });

Config Path - Data Storage

get the path to the config folder:

js
const {app} = require('electron'); const configDir = app.getPath('userData'); console.log(configDir);

src https://www.electronjs.org/docs/api/app#appgetpathname

home - User's home directory.

appData - Per-user application data directory, which by default points to

userData - The directory for storing your app's configuration files, which by default it is the appData directory appended with your app's name.

temp - Temporary directory.

desktop - The current user's Desktop directory.

documents - Directory for a user's "My Documents".

exe - The current executable file.

Misc BrowserWindow Settings

js
mainWindow = new BrowserWindow({ width: 800, height: 600, modal: true, center: true, alwaysOnTop: true, closable: false, resizable: false, minimizable: false, maximizable: false, skipTaskbar: true, frame: false, fullscreen: false, fullscreenable: false, // kiosk: true, webPreferences: { nodeIntegration: true } })

Register Global Key Shortcut

src: https://www.electronjs.org/docs/api/global-shortcut

js
app.whenReady().then(() => { const ret = globalShortcut.register('CommandOrControl+Shift+X', () => { console.log('CommandOrControl+Shift+X is pressed') }) if (!ret) { console.log('registration failed') } // Check whether a shortcut is registered. console.log(globalShortcut.isRegistered('CommandOrControl+Shift+X')) } app.on('will-quit', () => { // Unregister a shortcut. globalShortcut.unregister('CommandOrControl+Shift+X') // Unregister all shortcuts. globalShortcut.unregisterAll() })

more ex:

js
globalShortcut.register('Escape', () => { mainWindow.hide() })

Messages

src https://www.electronjs.org/docs/api/ipc-main

in main.js:

js
// In the main process. const { ipcMain } = require('electron') ipcMain.on('asynchronous-message', (event, arg) => { console.log(arg) // prints "ping" event.reply('asynchronous-reply', 'pong') }) ipcMain.on('synchronous-message', (event, arg) => { console.log(arg) // prints "ping" event.returnValue = 'pong' })

in index.html:

js
const { ipcRenderer } = require('electron') console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong" ipcRenderer.on('asynchronous-reply', (event, arg) => { console.log(arg) // prints "pong" }) ipcRenderer.send('asynchronous-message', 'ping')

Listen to Clipboard Events

example: https://github.com/arjun-g/electron-clipboard-extended#readme

js
const {clipboard} = require('electron') const EventEmitter = require('./EventEmitter') const clipboardEmitter = new EventEmitter() let watcherId = null, previousText = clipboard.readText(), previousImage = clipboard.readImage() clipboard.on = (event, listener) => { clipboardEmitter.on(event, listener) return clipboard } clipboard.once = (event, listener) => { clipboardEmitter.once(event, listener) return clipboard } clipboard.off = (event, listener) => { if(listener) clipboardEmitter.removeListener(event, listener) else clipboardEmitter.removeAllListeners(event) return clipboard } clipboard.startWatching = () => { if(!watcherId) watcherId = setInterval(() => { if(isDiffText(previousText, previousText = clipboard.readText())) clipboardEmitter.emit('text-changed') if(isDiffImage(previousImage, previousImage = clipboard.readImage())) clipboardEmitter.emit('image-changed') }, 500) return clipboard } clipboard.stopWatching = () => { if(watcherId) clearInterval(watcherId) watcherId = null return clipboard } function isDiffText(str1, str2){ return str2 && str1 !== str2 } function isDiffImage(img1, img2){ return !img2.isEmpty() && img1.toDataURL() !== img2.toDataURL() } module.exports = clipboard

Build a Tray App

src https://www.electronjs.org/docs/api/tray

First, create a folder assets and add an icon icon.png

Then, in main.js:

js
const path = require('path'); const { app, Menu, Tray, } = require('electron'); let tray = null; const getIcon = () => { // if (process.platform === 'win32') return 'icon-light.ico'; // if (systemPreferences.isDarkMode()) return 'icon-light.png'; // return 'icon-dark.png'; }; app.on('ready', () => { // hide on Mac if (app.dock) app.dock.hide(); tray = new Tray(path.join(__dirname, './assets/icon.png')); if (process.platform === 'win32') { tray.on('click', tray.popUpContextMenu); } const menu = Menu.buildFromTemplate([{ label: 'Quit', click() { app.quit(); } }]); tray.setToolTip('Tool Tip'); tray.setContextMenu(menu); });