electron | Build cross-platform desktop apps | Runtime Evironment library
kandi X-RAY | electron Summary
Support
Quality
Security
License
Reuse
Currently covering the most popular Java, JavaScript and Python libraries. See a Sample Here
electron Key Features
electron Examples and Code Snippets
mkdir apex-app-desktop cd apex-app-desktop npm init
{ "name": "apex-app-desktop", "version": "1.0.0", "description": "APEX Desktop Application using Github Electron", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "git+https://github.com/Dani3lSun/apex-app-desktop.git" }, "author": "Daniel Hochleitner", "license": "MIT", "bugs": { "url": "https://github.com/Dani3lSun/apex-app-desktop/issues" }, "homepage": "https://github.com/Dani3lSun/apex-app-desktop#readme", "devDependencies": { "electron-prebuilt": "^1.1.0" } }
{ "name": "apex-app-desktop", "version": "1.0.0", "description": "APEX Desktop Application using Github Electron", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "electron ." }, "repository": { "type": "git", "url": "git+https://github.com/Dani3lSun/apex-app-desktop.git" }, "author": "Daniel Hochleitner", "license": "MIT", "bugs": { "url": "https://github.com/Dani3lSun/apex-app-desktop/issues" }, "homepage": "https://github.com/Dani3lSun/apex-app-desktop#readme", "devDependencies": { "electron-prebuilt": "^1.1.0" } }
npm i electron-prebuilt --save-dev #for local app folder npm i -g electron-prebuilt #globally for using commandline
// Libraries used in the app var app = require('electron').app; var BrowserWindow = require('electron').BrowserWindow; var appMenu = require('electron').Menu; var appTray = require('electron').Tray; var path = require('path'); const { crashReporter } = require('electron'); app.commandLine.appendSwitch('ignore-certificate-errors', 'true'); // Crash Reporter (Optional) crashReporter.start({ productName: 'APEX Plugins', companyName: 'Daniel Hochleitner', submitURL: 'https://github.com/Dani3lSun/apex-app-desktop/issues', autoSubmit: false }); // Init mainWindow and Tray var mainWindow = null; var appIcon = null; // Kill the app when all windows are closed app.on('window-all-closed', function() { if (process.platform != 'darwin') { app.quit(); } }); // App is loaded app.on('ready', function() { // Tray icon // template var trayTemplate = [{ label: "About Application", selector: "orderFrontStandardAboutPanel:" }, { label: "Quit", click: function() { app.quit(); } }]; // set tray icon with context menu appIcon = new appTray(path.join(__dirname, 'img/tray.png')); appIcon.setContextMenu(appMenu.buildFromTemplate(trayTemplate)); // Create the main window for the app mainWindow = new BrowserWindow({ "width": 1280, // init width "height": 800, // init height "minWidth": 1024, "minHeight": 800, "resizable": true, "useContentSize": true, //"transparent": true, // better look in OSX "titleBarStyle": "hidden-inset", // better look in OSX "icon": path.join(__dirname, 'img/tray.png') // app icon (for linux build) }); // Load in content with webview to APEX app mainWindow.loadURL('file://' + __dirname + '/index.html'); // Ensure that garbage collection occurs when the window is closed mainWindow.on('closed', function(e) { mainWindow = null; }); // Create the Application main menu (required for Copy&Paste Support) // Application menu var menuTemplate = [{ label: "Application", submenu: [{ label: "About Application", selector: "orderFrontStandardAboutPanel:" }, { type: "separator" }, { label: "Quit", accelerator: "Command+Q", click: function() { app.quit(); } }] // Edit menu }, { label: "Edit", submenu: [{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" }, { label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" }, { type: "separator" }, { label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" }, { label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" }, { label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" }, { label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }] }, { // View menu label: 'View', submenu: [{ label: 'Reload', accelerator: 'CmdOrCtrl+R', click: function(item, focusedWindow) { if (focusedWindow) focusedWindow.reload(); } }, { label: 'Toggle Full Screen', accelerator: (function() { if (process.platform == 'darwin') return 'Ctrl+Command+F'; else return 'F11'; })(), click: function(item, focusedWindow) { if (focusedWindow) focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); } }, { label: 'Toggle Developer Tools', accelerator: (function() { if (process.platform == 'darwin') return 'Alt+Command+I'; else return 'Ctrl+Shift+I'; })(), click: function(item, focusedWindow) { if (focusedWindow) focusedWindow.toggleDevTools(); } }] }]; // set menu with options from above appMenu.setApplicationMenu(appMenu.buildFromTemplate(menuTemplate)); }); // Create the main window for the app when App is reopened from OSX Dock app.on('activate', function(e, hasVisibleWindows) { if (mainWindow === null) { mainWindow = new BrowserWindow({ "width": 1280, //init width "height": 800, // init height "minWidth": 1024, "minHeight": 800, "resizable": true, "useContentSize": true, "transparent": true, // better look in OSX "titleBarStyle": "hidden-inset", // better look in OSX "icon": path.join(__dirname, 'img/tray.png') // app icon (for linux build) }); mainWindow.loadURL('file://' + __dirname + '/index.html'); mainWindow.on('closed', function() { mainWindow = null; }); } });
APEX Plugins
// Functions for APEX triggered events in electron // opponent for electronapex.js functions inside APEX // // electron functions electron = window.require('electron'); var stringDevider = "::"; module.exports = { // opens a local file from consol.log text from APEX openLocalFile: function(consoleString) { var position = consoleString.indexOf(stringDevider) + 2; path = consoleString.substr(position); var shell = electron.shell; shell.openItem(path); } };
// Functions for electron used in APEX // functions often uses wrapper around console.log (webview.addEventListener('console-message' gets these events)) // upload to APEX App! var stringDevider = "::"; // global namespace var electronapex = { // open file openFile: function(path) { var type = 'open-file'; console.log(type + stringDevider + path); }, // check if notifications are enabled notifyCheck: function() { // Let's check if the browser supports notifications if (!("Notification" in window)) { alert("This browser does not support system notifications"); } // Let's check whether notification permissions have already been granted else if (Notification.permission === "granted") { // If it's okay let's create a notification var notification = new Notification("You already have granted Notification permissions, great!:)"); } // Otherwise, we need to ask the user for permission else if (Notification.permission !== 'denied') { Notification.requestPermission(function(permission) { // If the user accepts, let's create a notification if (permission === "granted") { var notification = new Notification("Now Notifications should work!"); } }); } }, // send a notification doNotify: function(text) { var notification = new Notification(text); } };
npm install #installs all dependencies from package.json npm start
npm start
npm install electron-packager -g
cd apex-app-desktop electron-packager . "APEX Plugins" --platform=darwin --arch=x64 --version=0.36.4 --app-version=1.0.0 --icon img/app.icns
cd apex-app-desktop electron-packager . "APEX Plugins" --platform=linux --arch=x64 --version=0.36.4 --app-version=1.0.0
cd apex-app-desktop electron-packager . "APEX Plugins" --platform=win32 --arch=x64 --version=0.36.4 --app-version=1.0.0 --icon img/app.ico
├── Contents (you are here!) │ ├── Architecture │ ├── * What can be used for? │ ├── In Electron Project │ └── In Nodejs/Electron Project │ ├── * Install │ ├── * Instruction1: ProcessManager │ ├── Require it in main.js(electron) │ └── Open process-manager window for your application │ ├── * Instruction2: Service │ ├── The arguments to create a service │ ├── Enable service auto reload after code changed │ └── The methods of a Service instance │ ├── * Instruction3: MessageChannel │ ├── The methods of MessageChannel │ └── A full usage │ ├── * Instruction4: ChildProcessPool │ ├── Create a childprocess pool │ ├── Send request to a process instance │ ├── Send request to all process instances │ ├── Destroy the child processes of the process pool │ └── Set the max instance limit of pool │ ├── * Instruction5: ProcessHost │ ├── Require it in a sub process │ ├── Registry a task with unique name │ ├── Working with ChildProcessPool │ └── Unregistry a task with unique name │ ├── Examples
$: npm install electron-re --save # or $: yarn add electron-re
const { MessageChannel, // must required in main.js even if you don't use it ProcessManager } = require('electron-re');
ProcessManager.openWindow();
const { BrowserService } = require('electron-re'); const myServcie = new BrowserService('app', path.join(__dirname, 'path/to/app.service.js'));
/* --- main.js --- */ const myService = new BrowserService('app', 'path/to/app.service.js', options);
/* --- main.js --- */ const myService = new BrowserService('app', 'path/to/app.service.js', { ...options, // set dev mode dev: true, // with webSecurity closed webPreferences: { webSecurity: false } });
/* --- main.js --- */ const { BrowserService, MessageChannel // must required in main.js even if you don't use it } = require('electron-re'); ... app.whenReady().then(async() => { // after app is ready in main process const myService = new BrowserService('app', 'path/to/app.service.js'); // async await myService.connected(); mhyService.openDevTools(); /* work with webContents method, also you can use MessageChannel instead */ mhyService.webContents.send('channel1', { value: 'test1' }); }); ...
/* --- app.service.js --- */ const { ipcRenderer } = require('electron'); /* working with ipc method, also you can use MessageChannel instead */ ipcRenderer.on('channel1', (event, result) => { // works ... });
/* send data to a service - like the build-in ipcMain.send */ MessageChannel.send('service-name', channel, params); /* send data to a service and return a Promise - extension method */ MessageChannel.invoke('service-name', channel, params); /* send data to a renderer/servcie which id is same as the given windowId/webContentsId, same as ipcRenderer.sendTo, recommend to use it when you want to send data from main/service to a renderer window */ MessageChannel.sendTo('windowId/webContentsId', channel, params); /* listen a channel, same as ipcMain.on/ipcRenderer.on */ MessageChannel.on(channel, func); /* listen a channel once, same as ipcMain.once/ipcRenderer.once */ MessageChannel.once(channel, func);
/* send data to main process - like the build-in ipcRender.send */ MessageChannel.send('main', channel, params); /* send data to main process and return a Promise - extension method */ MessageChannel.invoke('main', channel, params);
/* handle a channel signal, extension method, and you can return data directly or return a Promise instance */ MessageChannel.handle(channel, processorFunc);
const { BrowserService, MessageChannel // must required in main.js even if you don't use it } = require('electron-re'); const isInDev = process.env.NODE_ENV === 'dev'; ... /* use MessageChannel instead of build-in method */ app.whenReady().then(() => { const myService = new BrowserService('app', 'path/to/app.service.js'); myService.connected().then(() => { // open devtools in dev mode for debugging if (isInDev) myService.openDevTools(); MessageChannel.send('app', 'channel1', { value: 'test1' }); MessageChannel.invoke('app', 'channel2', { value: 'test2' }).then((response) => { console.log(response); }); MessageChannel.on('channel3', (event, response) => { console.log(response); }); MessageChannel.handle('channel4', (event, response) => { console.log(response); return { res: 'channel4-res' }; }); }) });
const { ipcRenderer } = require('electron'); const { MessageChannel } = require('electron-re'); MessageChannel.on('channel1', (event, result) => { console.log(result); }); MessageChannel.handle('channel2', (event, result) => { console.log(result); return { response: 'channel2-response' } }); MessageChannel.invoke('app2', 'channel3', { value: 'channel3' }).then((event, result) => { console.log(result); }); MessageChannel.send('app2', 'channel4', { value: 'channel4' });
MessageChannel.handle('channel3', (event, result) => { console.log(result); return { response: 'channel3-response' } }); MessageChannel.once('channel4', (event, result) => { console.log(result); }); MessageChannel.send('main', 'channel3', { value: 'channel3' }); MessageChannel.send('main', 'channel3', { value: 'channel3' }); MessageChannel.invoke('main', 'channel4', { value: 'channel4' });
const { ipcRenderer } = require('electron'); const { MessageChannel } = require('electron-re'); MessageChannel.send('app', 'channel1', { value: 'test1'}); MessageChannel.invoke('app2', 'channel3', { value: 'test2' }); MessageChannel.send('main', 'channel3', { value: 'test3' }); MessageChannel.invoke('main', 'channel4', { value: 'test4' });
const { ChildProcessPool } = require('electron-re'); global.ipcUploadProcess = new ChildProcessPool({ path: path.join(app.getAppPath(), 'app/services/child/upload.js'), max: 6, env: { lang: global.lang, NODE_ENV: nodeEnv } });
global.ipcUploadProcess.send( 'init-works', { name: 'fileName', type: 'fileType', size: 'fileSize', }, uploadId ) .then((rsp) => { console.log(rsp); });
global.ipcUploadProcess.sendToAll( 'record-get-all', { data: 'test' } ) .then((rsp) => { console.log(rsp); });
global.ipcUploadProcess.disconnect(id);
global.ipcUploadProcess.setMaxInstanceLimit(number);
const { ProcessHost } = require('electron-re');
ProcessHost .registry('init-works', (params) => { return initWorks(params); }) .registry('async-works', (params) => { return asyncWorks(params); }); function initWorks(params) { console.log(params); return params; } function asyncWorks(params) { console.log(params); return fetch(url); }
/* 1. send a request in main process */ global.ipcUploadProcess.send( 'init-works', { name: 'fileName', type: 'fileType', size: 'fileSize', }, uploadId ); ... /* 2. handle this request in sub process */ ...
ProcessHost .unregistry('init-works') .unregistry('async-works') ...
/* ** When image is dragged on editor, addImageBlobHook will be called ** Returned callback will be shown on editor */ editorOptions: { hideModeSwitch: false, usageStatistics: false, highlightFormatting: true, hooks: { 'addImageBlobHook': (blob, callback) => { this.LoadingNotification = true; this.uploadFileAsync(blob).then(res => { if (!res) { this.LoadingNotification = false; this.showNotification("Error: Upload Failed by Connection", "error"); } else callback(res); }) return false; } }, }, /* ** Image blob file to base64 ** con file contains setting information (json file) */ async uploadFileAsync(blob) { let base64 = await this.readFileAsync(blob); // for github api V3 authentication header setting (axios) // axios instance must be created let uploadAxios = this.$http.create({ timeout: 10000, headers: { 'Authorization': 'token ' + con.token, 'Content-Type': 'application/json' } }); // for github api V3 : upload content in repo let repo_parm = { "message": con.uploadMsg, "branch": con.branch, "content": base64 } // content name must be distinguished : upload time append const uid = new Date(); // upload content -> response is "uploaded content url" return uploadAxios.put(con.repoURL + 'screenshot-' + uid + ".png", repo_parm) .then(res => { this.LoadingNotification = false; this.showNotification("Successfully Uploaded Image!", "success"); return res.data.content.html_url + '?raw=true' }) .catch(err => { console.log(err) return false; }) },
tmpSave() { if (this.newFlag) { this.newNoteLoading = true this.$store.dispatch('getMenuId').then(menuId => { if (!menuId) alert("Saving Note Failed : Invalid Menu!") else { let postData = { title_text: this.title_text, subtitle: this.subtitle, todo: this.checkIcon, completed: false, bgImage: this.backgroundUrl, content_text: this.content_text, menuId: menuId, createdAt: this.$moment(new Date()).format("YYYY-MM-DD HH:mm:ss"), updatedAt: "", }; db.collection("notes/").add(postData) .then((docRef) => { this.newNoteLoading = false; this.id = docRef.id; this.newFlag = false; console.log("Saved with ID: ", docRef.id); this.showNotification("Successfully Saved New Note", ""); }) .catch((error) => { console.error("Error saving document: ", error); }); } }) } else { if (this.id == "") { console.log("Note must be saved before temporary save!"); return 0; } this.newNoteLoading = true; let postData = { title_text: this.title_text, subtitle: this.subtitle, todo: this.checkIcon, bgImage: this.backgroundUrl, content_text: this.content_text, updatedAt: this.$moment(new Date()).format("YYYY-MM-DD HH:mm:ss"), }; db.collection("notes").doc(this.id).update(postData).then(() => { console.log("updated " + this.id) this.newNoteLoading = false; this.showNotification("Successfully Saved Temporary Note", ""); }); } },
/* // // Get electron window.webContents // printToPDF, encode to 'base64' // convert base64 to blob // - modified 190721 - */ exportAsPdf() { remote.getCurrentWebContents().printToPDF({ marginsType: 2, printBackground: true, printSelectionOnly: false, landscape: false }, (error, data) => { if (error) throw error const blob = new Blob([data], { type: 'application/pdf' }) const e = document.createEvent('MouseEvents'), a = document.createElement('a'); a.download = this.title_text + ".pdf"; a.href = window.URL.createObjectURL(blob); a.dataset.downloadurl = ['application/pdf', a.download, a.href].join(':'); e.initEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); a.dispatchEvent(e); }) }, /* // // Get content text data // convert text data to blob // - modified 190721 - */ exportAsFile() { const data = this.content_text const blob = new Blob([data], { type: 'text/plain' }) const e = document.createEvent('MouseEvents'), a = document.createElement('a'); a.download = this.title_text + ".md"; a.href = window.URL.createObjectURL(blob); a.dataset.downloadurl = ['text/json', a.download, a.href].join(':'); e.initEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); a.dispatchEvent(e); },
this.$http.get('https://source.unsplash.com/random/800x600').then(r => { this.backgroundUrl = r.request.responseURL; console.log("random background image set") });
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const nodePath = require("path");
const os = require('os-utils');
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
function cpuStats() {
return new Promise((resolve) => {
os.cpuUsage((value) => {
let data = {
'cpu': (value * 100).toFixed(2),
'mem': (os.freememPercentage() * 100).toFixed(2),
'totalMem': (os.totalmem() / 1024).toFixed(2)
}
resolve(data);
})
})
}
electronApp.on('ready', () => {
window = createWindow();
setInterval(() => {
cpuStats()
.then((data) => {
window.webContents.send('cpuStats:update', data);
console.log(data) // Testing
})
}, 1000);
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
CPU Stats Monitor
CPU (%)
-
Free Mem (%)
-
Total Mem (GB)
-
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [
'cpuStats:update'
],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
...
}
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'brightness:increment',
'brightness:decrement'
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodeFs = require("fs");
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false, // For safety, let's disable Node integration
contextIsolation: true, // For safety, let's enable context isolation
preload: nodePath.join(__dirname, 'preload.js') // Let's use our preload script
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// Let's listen for messages to increment or decrement the brightness
electronIpcMain.on('brightness:increment', () => {
adjustBrightness(10);
})
electronIpcMain.on('brightness:decrement', () => {
adjustBrightness(-10);
})
// Function to adjust screen brightness
function adjustBrightness(adjustment) {
let data = nodeFs.readFileSync('/sys/class/backlight/amdgpu_bl1/brightness', 'utf-8');
let newData = parseInt(data) + adjustment;
if (newData > 255) {
newData = 255;
} else if (newData < 0) {
newData = 0;
}
newData = newData.toString();
nodeFs.writeFileSync('/sys/class/backlight/amdgpu_bl1/brightness', newData, 'utf-8');
}
Clock App
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld("electron", {
ipcRenderer: {
on(channel, listener) {
ipcRenderer.on(channel, (evt, message) => {
listener(evt, message);
});
},
removeListener(channel, listener) {
ipcRenderer.removeListener(channel, (evt, message) => {
listener(evt, message);
});
},
invoke(channel, data) {
return ipcRenderer.invoke(channel, data);
}
}
};
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
return new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
}
function showMainWindow() {
window.loadFile('index.html')
.then(() => { window.show(); })
}
function showLoginWindow() {
// window.loadURL('https://www.your-site.com/login')
window.loadFile('login.html') // For testing purposes only
.then(() => { window.show(); })
}
electronApp.on('ready', () => {
window = createWindow();
showMainWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// ----- IPC -----
electronIpcMain.on('message:loginShow', (event) => {
showLoginWindow();
})
electronIpcMain.on('message:loginSuccessful', (event, session) => {
showMainWindow();
})
Main Window
Main Window
Login
Login Window
Login Window
Username:
Password:
Login
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'message:loginShow',
'message:loginSuccessful'
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': [
'getPath'
]
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
const electronBrowserWindow = require('electron').BrowserWindow;
const electronDialog = require('electron').dialog;
const electronIpcMain = require('electron').ipcMain;
const nodePath = require("path");
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// -----
// Let's listen for a call on the 'getPath' channel
electronIpcMain.handle('getPath', async () => {
// Dialog options.
const options = {
properties: ["openFile"],
filters: [
{
name: "DSC Projects",
extensions: ["DSCProj"],
}
]
}
// When available, return the modified path back to the render thread via IPC
return await openDialog(window, options)
.then((result) => {
// User cancelled the dialog
if (result.canceled === true) { return; }
// Modify and return the path
let path = result.filePaths[0];
let modifiedPath = nodePath.parse(path).dir; // Here's the magic.
console.log(modifiedPath); // Testing
return modifiedPath;
})
})
// Create an open dialog
function openDialog(parentWindow, options) {
return electronDialog.showOpenDialog(parentWindow, options)
.then((result) => { if (result) { return result; } })
.catch((error) => { console.error('Show open dialog error: ' + error); });
}
Open Project Test
// Import the necessary Electron modules
const electronApp = require('electron').app;
const electronMenu = require('electron').Menu;
const isMac = process.platform === 'darwin'
const isWin = process.platform === 'win32'
function build() {
const template = [
// { role: 'appMenu' }
...(isMac ? [{
label: electronApp.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
// more menu code...
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://github.com/aronsommer/eddy-g')
}
}
]
}
];
electronMenu.setApplicationMenu(electronMenu.buildFromTemplate(template));
}
// Export the publicly available function
module.exports = {build};
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const nodePath = require("path");
const appMenu = require('menu');
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: nodePath.join(__dirname, 'preload.js')
}
});
window.loadFile('index.html')
.then(() => { appMenu.build(); } // Build the menu
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// Rebuild the menu on localisation change.
appEvents.on('locale:changed', () => {
build();
});
const popupLogin = (htmlFile, parentWindow, width, height) => {
// Get the customers id from whatever source necessary.
let id = 99;
// Create the window.
let modal = new BrowserWindow({
width: 600,
height: 500,
modal: true,
resizable: false,
icon: path.join(__dirname +
'/public/auxwall/logos/favicon.png'),
parent: MainWindow,
frame: false,
show: false, // Hide possible flash and DOM update(s).
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true, // Set to true
nodeIntegration: true, // Node integration (removed in Electron v12)
}
})
// Load the window.
// Note: The loadFile() method returns a promise.
modal.loadFile(`${__dirname}/pages/Company/login.html`)
.then(() => { modal.webContents.send('login:id', id); })
.then(() => { modal.show(); }) // Now show the modal
return modal;
}
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [],
// From main to render.
'receive': [
'login:id'
],
// From render to main and back again.
'sendReceive': []
}
};
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Title
....
(function() => {
window.ipcRender.receive('login:id', (event, id) => { customerId(id); });
})();
function customerId(id) {
console.log(id);
}
Trending Discussions on electron
Trending Discussions on electron
QUESTION
When import 'semantic-ui-css/semantic.min.css'
into a brand new Electron-Forge/Webpack5 project, I get the following:
UnhandledSchemeError: Reading from "data:application/x-font-ttf;charset=utf-8;;base64,AAEAAAAO...
Webpack supports "data:" and "file:" URIs by default.
You may need an additional plugin to handle "data:" URIs.
After stepping through the code, it appears that the data:uri
format here does not match the regex extracting its format in NormalModule: https://github.com/webpack/webpack/blob/e5570ab5230e98e1030d696e84465b5f533fdae9/lib/schemes/DataUriPlugin.js#L16. Note the double ;;
in the data URI, that breaks the RegEx linked.
However, this CSS file loads fine in my website. When stepping through the webpack build, they both load the CSS file (as verified by breakpoints in https://github.com/webpack/webpack/blob/e83587cfef25db91dc5b86be5b729288fd1bafdd/lib/NormalModule.js#L761), but then the website just... doesn't load this data URL??? I tried replacing the webpack config for Electron with the one from the website, but no joy.
I'm all out of ideas after a day or 4 digging into this. I don't even know where to poke next. Any suggestions on where I can dig/what I can check to get this CSS file loading in Electron?
A minimal demo repo can be found here: https://github.com/FrozenKiwi/data-url-loading, only difference is pulled the offending CSS declaration out into the local CSS file
ANSWER
Answered 2021-Jul-26 at 17:05Finally fixed it...
Electron-Forge installs a recent version of CSS-Loader, whereas the website still has quite an old version. Downgrading fixed the issue.
QUESTION
I need help debugging Webpack's Compression Plugin.
SUMMARY OF PROBLEM
- Goal is to enable asset compression and reduce my app's bundle size. Using the Brotli algorithm as the default, and gzip as a fallback for unsupported browsers.
- I expected a content-encoding field within an asset's Response Headers. Instead, they're loaded without the field. I used the Chrome dev tools' network tab to confirm this. For context, see the following snippet:
- No errors show in my browser or IDE when running locally.
WHAT I TRIED
- Using different implementations for the compression plugin. See below list of approaches:
- (With Webpack Chain API)
config
.plugin('brotliCompress')
.use(CompressionWebpackPlugin, [{
exclude: /.map$/,
cache: true,
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}])
- (With Webpack Chain API)
config
.plugin('gzip')
.use(CompressionWebpackPlugin, [{
algorithm: 'gzip',
test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
threshold: 8192, // Assets larger than 8192 bytes are not processed
minRatio: 0.8, // Assets compressing worse that this ratio are not processed
}])
- (With Webpack Chain API)
config
.plugin('CompressionPlugin')
.use(CompressionWebpackPlugin)
- (Using vue-cli-plugin: compression) This fails due to a Missing generator error when I use
vue invoke compression
in response to an IDE console message after I runvue add compression
as an alternative to using Webpack Chain API for compression configuration.
pluginOptions: {
compression: {
brotli: {
filename: '[file].br[query]',
algorithm: 'brotliCompress',
include: /\.(js|css|html|svg|json)(\?.*)?$/i,
minRatio: 0.8,
},
gzip: {
filename: '[file].gz[query]',
algorithm: 'gzip',
include: /\.(js|css|html|svg|json)(\?.*)?$/i,
minRatio: 0.8
}
}
},
- Lastly, I tried setting the threshold field to 0 as well as raising it larger than 10k bytes.
POINTS OF SIGNIFICANCE
- The above attempts didn't achieve the goal I stated in the first summary bullet and were used in place of the previous approaches tested.
- I prioritized my efforts with Webpack Chain API since it resulted in no errors when rebuilding and running the app.
REFERENCED LINKS/DOCS
- https://webpack.js.org/plugins/compression-webpack-plugin/
- https://github.com/neutrinojs/webpack-chain/tree/main
- https://neutrinojs.org/webpack-chain/#config-plugins-adding
- https://github.com/nklayman/vue-cli-plugin-electron-builder/issues/500 (similar generator issue with another plugin)
- https://webpack.js.org/plugins/compression-webpack-plugin/
- Use webpack-chain to do webpack configuration in vue.config.js, so how to use speed-measure-webpack-plugin plugin? (not a valid answer, but referenced syntax nonetheless)
- https://github.com/vuejs/vue-cli/issues/6091#issuecomment-738536334
- Webpack prerender-spa-plugin with compression-webpack-plugin. index.html not compressed
CODE
vue.config.js
const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, dir)
}
module.exports = {
/* ....shortened for brevity */
// Compress option VI (with vue cli plugin, generator bug when invoked)
// pluginOptions: {
// compression: {
// brotli: {
// filename: '[file].br[query]',
// algorithm: 'brotliCompress',
// include: /\.(js|css|html|svg|json)(\?.*)?$/i,
// minRatio: 0.8,
// },
// gzip: {
// filename: '[file].gz[query]',
// algorithm: 'gzip',
// include: /\.(js|css|html|svg|json)(\?.*)?$/i,
// minRatio: 0.8
// }
// }
// },
chainWebpack: config => {
config
.resolve.alias
.set('@', resolve('src'))
config
.plugins.delete('prefetch')
config
.optimization.splitChunks()
config
.output
.chunkFilename('[id].js')
// The below configurations are recommeneded only in prod.
// config.when(process.env.NODE_ENV === 'production', config => { config... })
// Compress option VII
// config
// .plugin('gzip')
// .use(CompressionWebpackPlugin, [{
// algorithm: 'gzip',
// test: new RegExp('\\.(' + ['js', 'css'].join('|') + ')$'),
// threshold: 8192, // Assets larger than 8192 bytes are not processed
// minRatio: 0.8, // Assets compressing worse that this ratio are not processed
// }])
// Compress option VIII
// config
// .plugin('CompressionPlugin')
// .use(CompressionWebpackPlugin)
config
.plugin('brotliCompress')
.use(CompressionWebpackPlugin, [{
exclude: /.map$/,
// deleteOriginalAssets: true,
cache: true,
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}])
},
}
package.json
"dependencies": {
"@auth0/auth0-spa-js": "^1.15.0",
"audio-recorder-polyfill": "^0.4.1",
"compression-webpack-plugin": "^6.0.0",
"core-js": "^3.6.5",
"dotenv": "^8.2.0",
"dotenv-expand": "^5.1.0",
"moment": "^2.29.1",
"register-service-worker": "^1.7.1",
"uuid": "^3.4.0",
"vue": "^2.6.11",
"vue-loader": "^15.9.8",
"vue-router": "^3.5.1",
"vuex": "^3.6.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.5.0",
"@vue/cli-plugin-eslint": "~4.5.0",
"@vue/cli-plugin-pwa": "~4.5.0",
"@vue/cli-service": "~4.5.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-cli-plugin-compression": "~1.1.5",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.46.0"
}
I appreciate all input. Thanks.
ANSWER
Answered 2021-Sep-30 at 14:59It's not clear which server is serving up these assets. If it's Express, looking at the screenshot with the header X-Powered-By
, https://github.com/expressjs/compression/issues/71 shows that Brotli support hasn't been added to Express yet.
There might be a way to just specify the header for content-encoding
manually though.
QUESTION
I'm using Mac M1 and I've just upgraded to Node 14.17.6LTS.
I tried to rebuild better_sqlite3 (7.4.3) using with electron builder (22.11.7) and I'm getting the following errors:
no member named 'GetContents' in 'v8::ArrayBuffer'
Any ideas how to solve this? Thanks in advance!
gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ]
SOLINK_MODULE(target) Release/.node
CXX(target) Release/obj.target/fse/fsevents.o
In file included from ../fsevents.cc:6:
In file included from ../../nan/nan.h:2884:
../../nan/nan_typedarray_contents.h:34:43: error: no member named 'GetContents' in 'v8::ArrayBuffer'
data = static_cast(buffer->GetContents().Data()) + byte_offset;
ANSWER
Answered 2021-Sep-23 at 01:15I'm using Mac M1 and I've just upgraded to Node 14.17.6LTS.
An interesting choice, given that Node 16 officially introduced M1 support.
no member named 'GetContents' in 'v8::ArrayBuffer'
See this doc. In short, GetContents
was replaced by GetBackingStore
in late 2019. Being a compatibility layer, nan adapted to this in early 2020.
So you'll probably have to ensure that the versions of all involved packages (Node, nan, electron, ...) match each other (in the sense of having been released around the same time and targeting each other).
QUESTION
I've upgraded to Electron 14, refactored my project to accomodate for the "Removed: remote module" breaking change, but I'm unable to compile it due to the following TypeScript error:
Type '{ plugins: true; nodeIntegration: true; contextIsolation: false; enableRemoteModule: true; backgroundThrottling: false; webSecurity: false; }' is not assignable to type 'WebPreferences'.
Object literal may only specify known properties, and 'enableRemoteModule' does not exist in type 'WebPreferences'.ts(2322)
electron.d.ts(12612, 5): The expected type comes from property 'webPreferences' which is declared here on type 'BrowserWindowConstructorOptions'
The affected code:
const window = new electron.BrowserWindow({
// ...
webPreferences: {
plugins: true,
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
backgroundThrottling: false,
webSecurity: false
},
// ...
});
Is this a bug or an intentional change in Electron v14? What's a workaround?
ANSWER
Answered 2021-Sep-27 at 11:31Now Electron 14.0.1 is out, and this is how I could enable remote
modules for both Main and Renderer processes (your webPreferences
settings may vary).
First, install @electron/remote
package (important: no --save-dev
, as it needs to be bundled):
npm install "@electron/remote"@latest
Then, for Main process:
// from Main process
import * as electron from 'electron';
import * as remoteMain from '@electron/remote/main';
remoteMain.initialize();
// ...
const window = new electron.BrowserWindow({
webPreferences: {
plugins: true,
nodeIntegration: true,
contextIsolation: false,
backgroundThrottling: false,
nativeWindowOpen: false,
webSecurity: false
}
// ...
});
remoteMain.enable(window.webContents);
For Renderer process:
// from Renderer process
import * as remote from '@electron/remote';
const window = new remote.BrowserWindow({
webPreferences: {
plugins: true,
nodeIntegration: true,
contextIsolation: false,
backgroundThrottling: false,
nativeWindowOpen: false,
webSecurity: false
}
// ...
});
// ...
// note we call `require` on `remote` here
const remoteMain = remote.require("@electron/remote/main");
remoteMain.enable(window.webContents);
Or, as one-liner:
require("@electron/remote").require("@electron/remote/main").enable(window.webContents);
It's important to note, if created from a Renderer process like that, BrowserWindow
is a remote object, i.e. a Renderer proxy to a BrowserWindow
object created inside the Main process.
QUESTION
I'm trying to make an electron app that gets the file names of a directory in a repository. I just don't know how to do this with the api. I know there is a way, I just don't know how.
For example:
I want to get the file names in the src/ directory of a github repository using the api.
I use axios to make api requests.
ANSWER
Answered 2022-Feb-01 at 17:57Use Get repository content endpoint, documented here
Check out an example using Octokit
List Repository contents View in Fusebit// If you don't send the path property, by default will send the contents from the root level
const repoContent = await github.rest.repos.getContent({
owner: 'repo-owner',
repo: 'repo-name'
});
console.log('Files found at root level', repoContent.data.map((file) => file.name));
QUESTION
I have an electron repo (https://github.com/MartinBarker/RenderTune) which used to work on windows 10 fine when ran with command prompt. After a couple months I come back on a new fresh windows 10 machine with an Nvidia GPU, and the electron app prints an error in the window when starting up:
Uncaught TypeError: Cannot read properties of undefined (reading 'getCurrentWindow')
Running ffmpeg shell commands results in an error as well, and in the command prompt terminal this message is outputted:
[14880:1207/145651.085:ERROR:gpu_init.cc(457)] Passthrough is not supported, GL is disabled, ANGLE is
I checked on my other Windows laptop machines running the same exact code from the master branch of my repo, and it works perfectly fine when running locally.
It seems like this might be a recent issue? I have found it discussed in various forums: https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1944468
I tried upgrading my global electron npm package to a more recent version: electron@16.0.4 , but the errors still appear.
ANSWER
Answered 2022-Jan-03 at 01:54You can try disabling hardware acceleration using app.disableHardwareAcceleration()
(See the docs). I don't think this is a fix though, it just makes the message go away for me.
main.js
import { app, BrowserWindow } from 'electron'
import isDev from 'electron-is-dev'
app.disableHardwareAcceleration()
let win = null
async function createWindow() {
win = new BrowserWindow({
title: 'My Window'
})
const winURL = isDev
? 'http://localhost:9080'
: `file://${__dirname}/index.html`
win.loadURL(url)
win.on('ready-to-show', async () => {
win.show()
win.maximize()
})
}
app.whenReady().then(createWindow)
app.on('window-all-closed', () => {
win = null
if (process.platform !== 'darwin') {
app.quit()
}
})
QUESTION
I would like to use the react-to-print
library to print an iframe
from my Electron app. How can I use the iframe
reference to get the correct window/element to print?
const handleElectronPrint = async (target: HTMLIFrameElement) {
// Instead of this (printing the whole page)
// let win = BrowserWindow.getFocusedWindow();
// How do I print just the referenced iframe?
// `target` iframe has id="printWindow", how to select it?
let win = BrowserWindow.getMyIframe();
// Is this the right way to do the print once we have the iframe?
const options = { printBackground: true };
win.webContents.print(options, (success, failureReason) => {
if (!success) console.log(failureReason);
console.log('Print Initiated');
});
};
ANSWER
Answered 2022-Jan-01 at 12:22You need to convert the iframe object to Data URL. And load the URL in a new hidden BrowserWindow object.
Build data URL in Renderer process and send the URL to Main process using preload. In main process do the BrowserWindow.loadURL and printing.
App.js
// Send print request to the Main process
this.handlePrint = function (target) {
return new Promise(() => {
console.log('forwarding print request to the main process...');
// convert the iframe into data url
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
let data = target.contentWindow.document.documentElement.outerHTML;
//console.log(data);
var blob = new Blob([data], { type: 'text/html' });
var url = URL.createObjectURL(blob);
window.electronAPI.printComponent(url, (response) => {
console.log('Main: ', response);
});
});
};
main.js
// List of all options at -
// https://www.electronjs.org/docs/latest/api/web-contents#contentsprintoptions-callback
const printOptions = {
silent: false,
printBackground: true,
color: true,
margin: {
marginType: 'printableArea',
},
landscape: false,
pagesPerSheet: 1,
collate: false,
copies: 1,
header: 'Page header',
footer: 'Page footer',
};
ipcMain.handle('printComponent', (event, url) => {
let win = new BrowserWindow({ show: false });
win.loadURL(url);
win.webContents.on('did-finish-load', () => {
win.webContents.print(printOptions, (success, failureReason) => {
console.log('Print Initiated in Main...');
if (!success) console.log(failureReason);
});
});
return 'done in main';
});
preload.js
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
printComponent: async (url, callback) => {
let response = await ipcRenderer.invoke('printComponent', url);
callback(response);
},
});
Here is the list of all print options. Some options like page size, margins, orientation can be set in CSS @page rule refer App.css in my demo app.
Here is demo app on GitHub electron-react-to-print-demo.
Print Preview: There is no, Chrome browser style, inbuilt print preview feature due to these reasons. We need to implement our own workaround. Like print to PDF and show pdf in new window:
//handle preview
ipcMain.handle('previewComponent', (event, url) => {
let win = new BrowserWindow({ title: 'Preview', show: false, autoHideMenuBar: true });
win.loadURL(url);
win.webContents.once('did-finish-load', () => {
win.webContents.printToPDF(printOptions).then((data) => {
let buf = Buffer.from(data);
var data = buf.toString('base64');
let url = 'data:application/pdf;base64,' + data;
win.webContents.on('ready-to-show', () => {
win.show();
win.setTitle('Preview');
});
win.webContents.on('closed', () => win = null;);
win.loadURL(url);
})
.catch((error) => {
console.log(error);
});
});
return 'shown preview window';
});
I've added above preview feature in electron-react-to-print-demo.
QUESTION
I use webpack 4 and electron-builder to bundle and build my Electron app. I noticed that native node modules inside the node_modules
directory of the app.asar
bundle still contain their C++ source files.
Is there a way to exclude certain file extensions from the build step?
ANSWER
Answered 2021-Dec-25 at 05:32electron-builder can exclude files in the files section of your package.json
.
ExampleDefault pattern / is not added to your custom if some of your patterns is not ignore (i.e. not starts with !). package.json and /node_modules// (only production dependencies will be copied) is added to your custom in any case. All default ignores are added in any case — you don’t need to repeat it if you configure own patterns.
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
QUESTION
I recently started developing an Electron application, and I am using daisyUI's Tailwind CSS components for the appearance of the user interface. I want to make the main window of the application rounded; however, daisyUI is making this task pretty challenging.
As you can see in the screenshot below, by default, daisyUI adds a background color to the body. I added the .bg-transparent
class to the body
tag, in order to make the background transparent, but daisyUI does not let the change apply (note the corners):
On the contrary, if I don't add daisyUI's CSS file to the head tag, the body becomes transparent:
Here's my HTML code:
Widget
HEY
How can I make the body transparent with daisyUI?
ANSWER
Answered 2021-Dec-17 at 01:02Here you can read that daisyUI adds a few base styles if base
is true
in the tailwind.config.js
file. Thus, I had to set base
to false
to solve my problem:
module.exports = {
...
daisyui: {
base: false
}
}
Note that, to do this, I had to install Tailwind CSS and daisyUI from npm
.
QUESTION
I'm trying to run cypress on a WSL with Ubuntu, this is what I'm getting:
$ cypress run
[29023:1018/155130.159647:ERROR:bus.cc(392)] Failed to connect to the bus: Failed to connect to socket /var/run/dbus/system_bus_socket: No such file or directory
[29023:1018/155130.162020:ERROR:bus.cc(392)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
[29023:1018/155130.162068:ERROR:bus.cc(392)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
[29211:1018/155130.193707:ERROR:gpu_init.cc(441)] Passthrough is not supported, GL is swiftshader
...
[29023:1018/155132.292604:ERROR:bus.cc(392)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
Timed out waiting for the browser to connect. Retrying...
[29023:1018/155232.249036:ERROR:bus.cc(392)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
Timed out waiting for the browser to connect. Retrying again...
[29023:1018/155332.249372:ERROR:bus.cc(392)] Failed to connect to the bus: Could not parse server address: Unknown address type (examples of valid types are "tcp" and on UNIX "unix")
(-2) loading 'http://localhost:3000/__/#/tests/integration/simple.spec.ts'
Error: (-2) loading 'http://localhost:3000/__/#/tests/integration/simple.spec.ts'
at rejectAndCleanup (electron/js2c/browser_init.js:161:7486)
at Object.failListener (electron/js2c/browser_init.js:161:7699)
at Object.emit (events.js:376:20)
I couldn't find any related topics, any help?
ANSWER
Answered 2021-Oct-19 at 14:32Cypress requires the ability to run its GUI. Depending on your Windows version, you likely need some additional configuration in order to run GUI applications in WSL:
For all Windows releases, make sure you install the required dependencies:
apt-get install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb
This may have been done for you depending on how you installed Cypress. I used the npm
directions in the Cypress doc.
Windows 11 includes the WSLg feature by default, which allows you to run GUI applications directly on Windows. If you upgraded from Windows 10 to Windows 11, run wsl --update
to make sure you have the latest WSL version with WSLg.
Also make sure, if you've attempted to run an X server on an older release (like in the next suggestion), that you remove any manual configuration of DISPLAY
in your startup files (e.g. ~/.bashrc
, etc.).
For Windows 10, you will need to do some additional configuration. There are really two ways to do this, but it's a better topic for Super User (since it isn't directly related to programming), so I'm going to point you to this Super User question for some details. Either answer there is fine. While I'm partial to my solution, most people opt for running a third-party X server as in harrymc's answer there.
Just to make sure there weren't any "hidden tricks" needed to get Cypress running, I can confirm that I was able to successfully ./node_modules/.bin/cypress open
using the Cypress instructions and my xrdp
technique from the Super User answer.
Community Discussions, Code Snippets contain sources that include Stack Exchange Network
Vulnerabilities
No vulnerabilities reported
Install electron
Use Electron Fiddle to build, run, and package small Electron experiments, to see code examples for all of Electron's APIs, and to try out different versions of Electron. It's designed to make the start of your journey with Electron easier.
Support
Find, review, and download reusable Libraries, Code Snippets, Cloud APIs from over 650 million Knowledge Items
Find more librariesExplore Kits - Develop, implement, customize Projects, Custom Functions and Applications with kandi kits
Save this library and start creating your kit
Share this Page