Electro is a single page ElectronJS application template primarily for Windows OS that attempts to mimic the functionality and behavior of a real window, with an appearance similar to that of Visual Studio Code. The reason it exists is to serve as a minimal single page ElectronJS based template for my personal use, and I have made it publicly available on the off chance that somebody else might find it of use, be that practical or educational.
Electro adheres to a standard model for a secure ElectronJS application, having a main process managing Electron tasks, a renderer process containing the main application logic, and a preload process which facilitates inter-process communication between them.
As well as providing a basic user interface with menus, modal dialogs, and popup notifications, Electro also contains several utility methods to facilitate local file access. All of these features combinbed mean you can get started on your actual application and let Electro take handle the routine tasks.
If you end up using Electro, let me know, I would love to see what you do with it. Maybe you would also consider buying me a coffee :)
v2.0.0 (12 Jan 2023)
dialog()
will now accept an HTML element in its optionsbody
property so extra content can be embedded inside.- Added
ignoreLinks
to dialog options so dialogs can be forced to not override default HTML anchor behavior. - Dialogs can now be positioned absolutley or centered in the window by passing options
left
,top
, andcenter
properties. - Dialog options
yesCallback
depricated, succeeded byonConfirm
. - Dialog options
noCallback
depricated, succeeded byonCancel
. - Tweaked various styles.
v1.0.2 (12 Dec 2022)
- Added
getWindowBounds()
so you can get the corrected bounds of theBrowserWindow
. - Added
getScreenBounds()
so you can get the corrected bounds of the screen. - Window resize callbacks will now receive an ElectronJS rectangle describing the bounds of the window, as returned by
getWindowBounds()
. - Added 'resized' window resize type for normal window resizing when dragging its borders.
v1.0.1 (11 Dec 2022)
- Added
getAppInfo()
which gets the applications "package.json" file as an object. - Cursor now changes to a pointer when the pointer is hovering over a dialog button.
- Added collapsable history to readme.
v1.0.0 (9 Dec 2022)
- Initial public release
As with all things in life, nothing is perfect, and Electro is no different in that regard.
Strengths
- Mostly complete ability to mimic a real windows behavior.
- Small and unconfusing code base compared to competing products.
- Is Relatively customizable.
- Better documentation that competing products.
- Easy to use compared to other products.
- Built in utility features for basic local file access.
- Loads CSS and generates HTML internally so index.html and style.css are relatively unpolluted.
- A self contained module so renderer.js is relatively unpolluted.
- Doesn't depend or include on any other modules or frameworks so you can add your own and go.
Weaknesses
- Not as complete as competing products.
- Is based on the "single page application" philosophy.
- Not tested with 3rd party frameworks.
- Not as customizable as competing products.
- Mac not really supported due to not owning one.
- Not as many "bells and whistles" as competing products.
alt
keypress doesn't initiate keyboard menu navigation.- Only one level of menus are implemented (no sub-menus).
If you are using Visual Studio Code then then getting started is fairly straightforward..
- Download this repository as a zip file.
- Unzip downloaded file into your projects folder.
- Rename the folder to whatever your application will be called.
- Open the folder in VSCode.
- Run
npm install
.
From there you can start creating your application by editing index.html
, renderer.js
, and style.css
.
NOTE: Your mileage may vary for other IDE's.
NOTE: Electro is pre configured with two launch configurations, "Run Electron Main", and "Debug Electron Main".
*NOTE: Electro uses Electron Forge to package and create a zip file for portable distributions.
The following example shows how to use the core features of Electro and hopefully demonstrates how easy it is to use...
import * as electro from './modules/electro/electro.js';
// Initialize Electro before calling any of its methods
electro.initialize();
// Define a simple menu and attach it to the title bar
const myMenus = [
{
label: 'Test Menu', // Menu label
items: [ // Menu items
{ // Menu item
label: 'Test Item',
key: 'T',
comboKeys: electro.TYPE_CTRL,
click: () => console.log('test item activated'),
},
]
},
];
electro.setMenus(myMenus);
// Define some colors and set them
const myColors = {
title_label: 'bbb', // Title bar title color
menu_label: 'bbb', // Menu label color
item_label: 'eee', // Menu item label color
item_highlight: '046', // Menu item highlight (hover) color
separator: '666', // Menu separator bar color
};
electro.setColors(myColors);
// Change the title bar label
electro.setTitle('My Electro App');
// Change the title bar icon
electro.setIcon('img/my-app-icon.svg');
// Change the title bar font
electro.setFont('Segoe UI');
//
// Optionally you can setup everything when calling `initialize` like so...
//
electro.initialize({
icon: 'img/my-app-icon.svg',
title: 'My Electro App',
menus: myMenus,
colors: myColors,
font: 'Segoe UI',
});
// Show a popup notification
electro.notify('Electro online');
// Show a modal dialog
electro.dialog({
title: 'Dialog Title',
body: 'Dialog Body',
singleButton: true
});
Let's examine each of the methods used in the example (and more) in more detail...
Electro contains the following core methods...
initialize(options)
Initialize the Electro module.
@param {object} options
Basically initialize
loads electro.css, attaches it to the head of index.html
, creates HTML elements and appends them to the body of index.html
, and installs various event handlers.
If you supply options, initialize
will setup menus, icons, and so forth as required. Valid options are as follows...
electro.initialize({
icon: 'my-icon.svg',
title: 'My Electro App',
menus: myMenus,
colors: myColors,
font: 'Segoe UI',
});
NOTE: You only need to include any of the above options you you want to set.
quitApplication()
Closes the Electro application.
enableConfig(state = true)
@param {boolean} state
Enable or disable Electro configuration.
When config is enabled, Electro will persist its window dimensions and position between sessions.
NOTE: Electro will NOT save window dimensions and position if the window is minimized or in fullscreen mode.
getAppInfo()
@returns {object} info or null
Get application information.
The applications 'package.json' file will be returned as an object. This is useful if you want to display information about the app such as the name, version, author, etc, which are all contained inside the 'package.json' file.
NOTE:
getAppInfo()
returns the entirepackage.json
object.
getWindowBounds()
@returns {object} bounds
Get window bounds.
bounds is an electron Rectangle
...
{
x: 0,
y: 0,
width: 1920,
height: 1080
}
NOTE: When resizing the Electron window, the window on the ipcRenderer process reports the wrong bounds.
getWindowBounds()
does give the correct bounds.
https://www.electronjs.org/docs/latest/api/structures/rectangle
getScreenBounds()
@returns {object} bounds
Get screen bounds.
bounds is an electron Rectangle
...
{
x: 0,
y: 0,
width: 1920,
height: 1080
}
https://www.electronjs.org/docs/latest/api/structures/rectangle
setMenus(menus)
Set title bar menus using the given array of menu objects
@param {[object]} menus
menus
is an array of menu objects, whose structure is as follows...
{ // Menu start
label: 'File', // Menu title
items: [ // Menu items start
{ // Item start
label: 'New Project', // Item label
key: 'N', // Hotkey that activates this menu item
comboKeys: TYPE_CTRL, // Extra keys required to activate hotkey
click: () => console.log('clicked'), // Code executed on click or hotkey activation
}, // Item end
] // Menu items end
} // Menu end
Combokeys is a combination (logical OR) of any of the following...
TYPE_NONE
TYPE_CTRL
TYPE_ALT
TYPE_SHIFT
TYPE_META // Mac "command" key
NOTE: Electro will replace
TYPE_CTRL
withTYPE_META
on Mac.
NOTE: Empty objects in a menus
items
array will be considered a horizontal menu separator.
NOTE: Calling
setMenus
will overwrite any menus currently attached to the title bar.
setColors(colors)
Set one or more colors.
@param {object} colors
colors
is an object with the following structure...
{
title_background: '444', // Title bar background color, inherited by all children
title_label: 'bbb', // Title bar title color
highlight: '555', // Highlight (hover) color for title bar elements except window close button
menu_label: 'bbb', // Menu label color
item_label: 'eee', // Menu item label color
item_highlight: '046', // Menu item highlight (hover) color
separator: '666', // Menu separator bar color
close_highlight: 'd12', // Highlight (hover) color for window close button
notify_background: '222', // Notification background color
notify_text: 'bbb', // Notification text color
dialog_background: '222', // Dialog background color
dialog_text: 'bbb', // Dialog text color
dialog_yes: '16a', // Dialog yes button color
dialog_no: '444', // Dialog no button color
}
The code above shows the valid color names, and their default values (as can be found in the :root
of electro.css).
NOTE: The color strings themselves are just HTML color strings without the "#" character.
NOTE: You only need to include any of the above colors you you want to set.
setTitle(title)
Set the windows title.
@param {string} title
setIcon(name)
Set the applications icons.
@param {string} name
Electro will change both the title bar and task bar icons using the name passed. Basically this means that if you pass in the name "img/icon.svg", then there must also exist (in the same path) a corresponding icon file "icon.icns" (Mac), "icon.ico" (Windows), or "icon.png" (Linux).
This is slightly confusing and mildly annoying but must be this way because the ElectronJS setIcon
method does not support anything but PNG, JPG, ICO, and ICNS files.
NOTE: If the corresponding icon file does not exist, Electro will overwrite both icons with it's own default ones.
setFont(name)
Set the windows font.
@param {string} name
setTitle(title)
Set the windows title.
@param {string} title
resizeCallback(callback)
Set the window resized callback.
@param {function} callback
Once set, your callback function will be executed on an Electro window resize event and passed a string specifying the type of event, which will be one of the following...
'entered-full-screen'
'exited-full-screen'
'maximized'
'unmaximized'
'minimized'
'restored'
'resized' // A normal resize by dragging edges of window
NOTE: The callback will also be passed an ElectronJS
Rectangle
object describing the bounds of the window.
NOTE: You only need to set a callback if you need to do something when the Electro window resizes.
Electro includes methods to enable the display of modal dialogs and popup toast-like notifications.
notify(text, type = TYPE_INFORMATION)
Displays the given text in a popup notification of the given type.
@param {string} text @param {number} type
There are three valid types
electro.TYPE_INFORMATION
electro.TYPE_WARNING
electro.TYPE_ERROR
dialog(options)
Displays a modal dialog using the given options.
@param {object} options
Valid options are as follows...
{
title: 'Math quiz', // Defaults to an empty string if not present
body: 'One multiplied by five equals three', // Defaults to an empty string if not present
type: electro.TYPE_INFORMATION, // defaults to `TYPE_INFORMATION` if not present
yesText: 'True', // Defaults to "YES" if not present
onConfirm: () => console.log('stupid'), // Defaults to `null` if not present
noText: 'False', // Defaults to "NO" if not present
onCancel: () => console.log('clever'), // Defaults to `null` if not present
singleButton: true, // Only yes button will be visible, defaults to `false` if not present
center: false, // If truem the dialog will be centered in the window
left: 64, // Absolute left position, defaults to 64 if not present
top: 64, // Absolute top position, defaults to 64 if not present
ignoreLinks: false, // do NOT override HTML anchor behavior, defaults to false if not present
}
NOTE: If
options.body
is an HTML element, the dialogs body will be the content of that element. Useoptions.ignoreLinks
if you use HTML anchors as faux elements, otherwise you will get unexpected behavior when they are clicked.
NOTE:
dialog
uses the same types asnotify
...
NOTE: Any hyperlinks in the body of the dialog will be opened using the systems default web browser.
NOTE: Passing no
options
will result in the worlds most useless dialog :D
Electro provides a number methods to enable selection of file and folders from the local file system by levaraging the ElectronJS Dialog API (https://www.electronjs.org/docs/latest/api/dialog). All native dialog methods are of the synchronous variety.
NOTE: The default filter for all dialogs will show all files (
{name: 'All Files', extensions: ['*']}
), and the default path will be the current process directory (as retrieved byelectro.currentDirectory
).
showSingleFileDialog(options)
Show an open file dialog where only one file can be selected.
@param {object} options
@returns {string} filename, or null
options
is an object containing any of the following optional parameters...
{
title: 'Dialog Title',
filter: {name: 'All Files', extensions: ['*']}, // An ElectronJS style filter
path: 'c:\temp', // Path that the dialog will start in
}
https://www.electronjs.org/docs/latest/api/structures/file-filter
https://www.electronjs.org/docs/latest/api/dialog#dialogshowopendialogsyncbrowserwindow-options
showMultipleFileDialog(options)
Show an open file dialog where multiple files can be selected.
@param {object} options
@returns {[string]} Array of filenames, or null
options
is an object containing any of the following optional parameters...
{
title: 'Dialog Title',
filter: {name: 'All Files', extensions: ['*']}, // An ElectronJS style filter
path: 'c:\temp', // Path that the dialog will start in
}
https://www.electronjs.org/docs/latest/api/structures/file-filter
https://www.electronjs.org/docs/latest/api/dialog#dialogshowopendialogsyncbrowserwindow-options
showSaveFileDialog(options)
Show a save file dialog where one file can be entered.
@param {object} options
@returns {string} filename, or null
options
is an object containing any of the following optional parameters...
{
title: 'Dialog Title',
filter: {name: 'All Files', extensions: ['*']}, // An ElectronJS style filter
path: 'c:\temp', // Path that the dialog will start in
}
https://www.electronjs.org/docs/latest/api/structures/file-filter
https://www.electronjs.org/docs/latest/api/dialog#dialogshowsavedialogsyncbrowserwindow-options
Electro provides a number methods to enable basic file system interaction on the local machine. These methods leverage the NodsJS filesystem (https://nodejs.org/api/fs.html) and path https://nodejs.org/api/path.html APIs. All native file system methods are of the synchronous variety.
exists(path)
Determines if the file with the given path exists.
@param {string} path @return {boolean} exists
https://nodejs.org/api/fs.html#fsexistssyncpath
deleteFile(path)
Deletes the file at the given path.
@param {string} path
https://nodejs.org/api/fs.html#fsunlinksyncpath
createFolder(path)
Create the folder with the given path.
@param {string} path
https://nodejs.org/api/fs.html#fsmkdirsyncpath-options
fileInfo(path)
Get information about the file with the given path.
@param {string} path @return {object}
NOTE: See https://nodejs.org/api/path.html#pathparsepath for the structure of the returned object
currentDirectory(path)
Sets or gets the current directory.
@param {string} path default is null @return {string} current directory, or null if the current directory was unable to be changed
Calling currentDirectory
with no parameter will result in the current directory being returned (get).
Calling currentdirectory
with a path will cause the current directory to be changed (set).
https://nodejs.org/api/process.html#processcwd
https://nodejs.org/api/process.html#process_process_chdir_directory
newFileName(...parts)
Create a new filename from the given parts.
@param {...string} ...parts An arbitrary number of strings @return {string} filename
The resulting filename will have the correct separators for various operating systems.
https://nodejs.org/api/path.html#pathjoinpaths
loadTextFile(path)
Load the text file with the given path.
@param {string} path @return {string} text
https://nodejs.org/api/fs.html#fsreadfilesyncpath-options
saveTextFile(path)
Save the given text to a file with the given path.
@param {string} path @param {string} text
https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options
saveBinaryFile(path)
Save the given binary data to a file with the given path.
@param {string} path @param {string} text
https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options
saveCanvas(path, dataURL)
Save the given dataURL to a PNG file with the given path.
@param {string} path @param {string} dataURL
dataURL
is a data URL obtained by calling HTMLCanvasElement.toDataURL()
.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL
NOTE:
saveCanvas()
leverages Electron'snativeImage
module.