Showing posts with label toptal. Show all posts
Showing posts with label toptal. Show all posts

Tuesday, April 26, 2016

The 5 Most Common UI Design Mistakes - Kapil Sharma

Although the title UI Designer suggests a sort of departure from the traditional graphic designer, UI design is still a part of the historical trajectory of the visual design discipline.
With each movement or medium, the discipline has introduced new graphic languages, layouts, and design processes. Between generations, the designer has straddled the transition from press to xerox, or paper to pixel. Across these generations, graphic design has carried out the responsibility of representing the visual language of each era respectively.
Therefore, as UI Design makes the transition out of its infancy, what sort of graphic world can we expect to develop? Unfortunately, based on the current trajectory, the future may look bleak. Much of UI Design today has become standardized and repeatable. Design discussions online involve learning the rules to get designs to safely work, rather than push the envelope, or imagine new things. The tendency for UI Designers to resort to patterns and trends has not only created a bland visual environment, but also diminished the value of the designer as processes become more and more formulaic. The issue is precisely not one of technicalities, but of impending visual boredom.
Thus, the Top Five Common UI Design mistakes are:
  • Following Design Rules
  • Abusing the Grid
  • Misunderstanding Typefaces
  • Patterns and the Standardization of UI Design
  • Finding Safety in Contrast
UI Design Rule Book
Understand principles and be creative within their properties. Following the rules will only take your where others have been.

Common Mistake #1: UI Designers Follow the Rules

The world of graphic design has always followed sets of rules and standards. Quite often in any design discipline, the common mistakes that are made can closely coincide with a standard rule that has been broken. Thus, from this perspective the design rules seem to be pretty trustworthy to follow.
However, in just about any design discipline, new design movements and creative innovation has generally resulted from consciously breaking said rule book. This is possible because design is really conditional, and requires the discretion of the designer, rather than a process with any sort of finite answers. Therefore, the design rules should likely be considered as guidelines more so rather than hard and fast rules. The experienced designer knows and respects the rule book just enough to be able break the box.
Unfortunately, the way that design is often discussed online is within sets of do’s and don’ts. Top mistakes and practices for design in 10 easy steps! Design isn’t so straightforward, and requires a much more robust understanding of principles and tendencies, rather than checklists to systematically carry out.
The concern is that if designers were to cease ‘breaking the rules’, then nothing new creatively would ever be made. If UI designers only develop their ability to follow guidelines, rather than make their own decisions, then they may quickly become irrelevant. How else will we argue a value greater than off the shelf templates?

Be Wary of Top Ten Design Rules

The issue with design rules in today’s UI design community is they are so abundant. In the interest of solving any problem, the designer can look to the existing UI community and their set of solutions, rather than solve an issue on their own. However, the abundance of these guides and rules have made themselves less credible.
A google search for “Top UI Design Mistakes” yields a half million search results. So, what are the chances that most, if any of these authors of various articles agree with one another? Or, will each design tip that is discussed coincide accurately with the design problems of a reader?
Often the educational articles online discuss acute problems, rather than the guiding design principles behind the issue. The result is that new designers will never learn why design works the way that it does. Instead, they only become able to copy what has come before. Isn’t it concerning that in none of these sorts of articles is something like play encouraged?
The designer should have a tool kit of principles to guide them, rather than a book of rules to follow predetermined designs. Press x for parallax scrolling and y for carousels. Before choosing, refer to most recent blog post on which navigational tool is trending. Boring!
Trends are like junk food for designers. Following trends produces cheap designs that may offer some initial pay back, but little worth in the long run. This means that not only may trendy designers become dated, or ineffective quickly. But, for you the designer, don’t expect to experience any sense of reward when designing in this way. Although working to invent your own styles and systems is a lot of work, it’s so worth it day in and day out. There’s just something about copying that never seems to feed the soul.

Common Mistake #2: Allowing the Grid to Restrict UI Design

Despite my treatise against rules - here’s a rule: there is no way for a UI Designer to design without a grid. The web or mobile interface is fundamentally based on a pixel by pixel organization - there’s no way around it. However, this does not necessarily mean that the interface has to restrict designers to gridded appearances, or even gridded processes.

Using the Grid as a Trendy Tool

Generally, making any design moves as a response to trends can easily lead to poor design. Perhaps what results is a satisfactory, mostly functional product. But it will almost certainly be boring or uninteresting. To be trendy is to be commonplace. Therefore, when employing the grid in a design, understand what the grid has to offer as a tool, and what it might convey. Grids generally represent neutrality, as everything within the restraints of a grid appear equal. Grids also allow for a neutral navigational experience. Users can jump from item to item without any interference from the designer’s curatorial hand. Whereas, with other navigational structures, the designer may be able to group content, or establish desired sequences.
UI Design Rule Book
Although a useful tool, the grid can be very limiting to designers.

Defaulting to the Grid as a Work Flow

Dylan Fracareta, faculty of RISD and director of PIN-UP Magazine, points out that “most people start off with a 12 - column grid…because you can get 3 and 4 off of that”. The danger here is that immediately the designer predetermines anything that they might come up with. Alternatively, Fracareta resides to only using the move tool with set quantities, rather than physically placing things against a grid line. Although this establishes order, it opens up more potential for unexpected outcomes. Although designing for the browser used to mean that we would input some code, wait, and see what happens. Now, web design has returned to a more traditional form of layout designer that’s “more like adjusting two sheets of transparent paper”. How can we as designers benefit from this process? Working Without a Grid Although grids can be restricting, they are one of our most traditional forms of organization. The grid is intuitive. The grid is neutral and unassuming. Therefore, grids allow content to speak for itself, and for users to navigate at their will and with ease. Despite my warnings towards the restrictiveness of grids, different arrays allow for different levels of guidance or freedom.

Common Mistake #3:The Standardization of UI Design with Patterns

The concept of standardized design elements predates UI design. Architectural details have been frequently repeated in practice for typical conditions for centuries. Generally this practice makes sense for certain parts of a building that are rarely perceived by a user. However, once architects began to standardize common elements like furniture dimensions, or handrails heights, people eventually expressed disinterest in the boring, beige physical environment that resulted. Not only this, but standardized dimensions were proven to be ineffective, as although generated as an average, they didn’t really apply to the majority of the population. Thus, although repeatable detail have their place, they should be used critically.
If we as designers choose to automate, what value are we providing?
If we as designers choose to automate, what value are we providing?

Designers Using the Pattern as Product

Many UI designers don’t view the pattern as a time saving tool, but rather an off the shelf solution to design problems. Patterns are intended to take recurring tasks or artefacts and standardize them in order to make the designer’s job easier. Instead, certain patterns like F Pattern Layouts, Carousels or Pagination have become the entire structure of many of our interfaces.

Justification for the Pattern is Skewed

Designers tell themselves that the F shaped pattern exists as a result of the way that people read on the web. Espen Brunborg points out that perhaps people read this way as a result of us designing for that pattern. “What’s the point of having web designers if all they do is follow the recipe,” Brunborg asks.

Common Mistake #4: Misunderstanding Typefaces

Many designer’s quick tips suggest hard and fast rules about fonts as well. Each rule is shouted religiously, “One font family only! Monospaced fonts are dead! Avoid thin fonts at all costs!”. But really, the only legitimate rules on type, text and fonts should be to enforce legibility, and convey meaning. As long as type is legible, there may very well be an appropriate opportunity for all sorts of typefaces. The UI Designer must take on the responsibility of knowing the history, uses, and designed intentions for each font that they implement in a UI.

Consider a Typeface Only for Legibility

Typefaces convey meaning as well as affect legibility. With all of the discussion surrounding rules for proper legibility on devices etc, designers are forgetting that type is designed to augment a body of text with a sensibility, as much as it is meant to be legible. Legibility is critical, I do not dispute this - but my point is that legibility really should be an obvious goal. Otherwise, why wouldn’t we have just stopped at Helvetica, or maybe Highway Gothic. However, the important thing to remember is that fonts are not just designed for different contexts of legibility. Typefaces are also essential for conveying meaning or giving a body of text a mood.
Typefaces are each designed for their own uses. Don't allow narrow minded rules to restrict an exploration of the world of type.

Avoiding Thin Fonts At All Costs

Now that the trend has come (and almost gone?), a common design criticism is to avoid thin fonts entirely. In the same way thin fonts came as a trend, they may leave as one also. However, the hope should be to understand the principles of the typefaces rather than follow trends at all.
Some say that they’re impossible to read or untrustworthy between devices. All legitimate points. Yet, this represents a condition in the current discussion of UI design. The font choice is only understood by designers as technical choice in regards to legibility, rather than also understanding the meaning and value of typefaces. The concern is that if legibility is the only concern that a designer carried, would thin fonts be done away with entirely?
Understand why you are using a thin font, and within what contexts. Bold, thick text is actually much more difficult to read at length than thinner fonts. Yet, as bold fonts carry more visual weight they’re more appropriate for headings, or content with little text. As thin fonts are often serifs, its suitability for body text is entirely objective. As serif characters flow together when read in rapid succession, they make for much more comfortable long reading.
As well, thin fonts are often chosen because they convey elegance. So, if a designer was working on an interface for a client whose mandate was to convey elegance, they might find themselves hard pressed to find a heavy typeface to do the job.

Not Enough Variation

A common mistake is to not provide enough variation between fonts in an interface. Changing fonts is a good navigational tool to establish visual hierarchy, or potentially different functions within an interface. A crash course on hierarchy will teach you that generally the largest items, or boldest fonts, should be the most important, and carry the most visual weight. Visual importance can convey content headings, or perhaps frequently used functions.

Too Much Variation

A common UI Design mistake is to load in several different typefaces from different families that each denote a unique function. The issue with making every font choice special, when there is many fonts, is that no font stands out. Changing fonts is a good navigational tool to establish visual hierarchy, or potentially different functions within an interface. Therefore, if every font is different, there is too much confusion for a user to recognize any order.

Common Mistake #5: Under/Over Estimating the Potential of Contrast

A common mistake that appears on many Top UI Design Mistake lists is that designers should avoid low contrast interfaces. There are many instances in which low contrast designs are illegible and ineffective - true. However, as with the previous points, my worry is that this use of language alternatively produces a high contrast design culture in response.

Defaulting to High Contrast

The issue is that high contrast is aesthetically easy to achieve. High contrast visuals are undeniably stimulating or exciting. However, there are many more moods in the human imagination to convey or communicate with, other than high stimulation. To be visually stimulating may also be visually safe.
The same issue is actually occurring in sci-fi film. The entire industry has resorted to black and neon blue visuals as a way to trick viewers into accepting ‘exciting’ visuals, instead of new, creative, or beautiful visuals. This article points out what the sci-fi industry is missing out on by producing safe visuals.
Functionally, if every element in an interface is in high contrast to another, then nothing stands out. This defeats the potential value of contrast as a hierarchical tool. Considering different design moves as tools, rather than rules to follow is essential in avoiding stagnant, trendy design.

Illegibly Low Contrast

The use of low contrast fonts and backgrounds is a commonly made mistake. However, rather than being a design issue. This could potentially be discussed as a beta testing mistake, rather than a design mistake.
How the design element relates as a low contrast piece to the rest of the interface is a design concern. The issue could be that the most significant item hierarchically is low in contrast to the rest of the interface. For the interface to communicate its organizational structure, the elements should contrast one another in a certain way. This is a design discussion. Whether or not it is legible is arguably a testing mistake.
The point is that in only discussing contrast as a technical issue resolvable by adjusting a value, designers miss out on the critical understanding of what contrast is principally used for.

Conclusion

As with the previous 4 mistakes, the abuse of patterns will rarely result in a dysfunctional website, but rather just a boring one. The mistake is in being safe. This overly cautious method of design may not cause the individual project to fail. However, this series of safe mistakes performed by the greater web community can mean greater failures beyond the individual UI design project. The role of the designer should be to imagine, thoughtfully experiment and create - not to responsibly follow rules and guidelines.
The original article is found on the Toptal Design Blog.

Friday, January 29, 2016

Electron Cross Platform Desktop App - Kapil Sharma


Earlier this year, Github released Atom-Shell, the core of its famous open-source editor Atom, and renamed it to Electron for the special occasion.

Electron, unlike other competitors in the category of Node.js-based desktop applications, brings its own twist to this already well-established market by combining the power of Node.js (io.js until recent releases) with the Chromium Engine to bring us the best of both server and client-side JavaScript.

Imagine a world where we could build performant, data-driven, cross-platform desktop applications powered by not only the ever-growing repository of NPM modules, but also the entire Bower registry to fulfill all our client-side needs.

Enter Electron.


Building Cross-platform Desktop Apps with Electron:

In this tutorial, we will build a simple password keychain application using Electron, Angular.js and Loki.js, a lightweight and in-memory database with a familiar syntax for MongoDB developers.

The full source code for this application is available here.

This tutorial assumes that:

The reader has Node.js and Bower installed on their machine.
They are familiar with Node.js, Angular.js and MongoDB-like query syntax.

Getting the Goods
First things first, we will need to get the Electron binaries in order to test our app locally. We can install it globally and use it as a CLI, or install it locally in our application’s path. I recommend installing it globally, so that way we do not have to do it over and over again for every app we develop.
We will learn later how to package our application for distribution using Gulp. This process involves copying the Electron binaries, and therefore it makes little to no sense to manually install it in our application’s path.
To install the Electron CLI, we can type the following command in our terminal:
$ npm install -g electron-prebuilt
To test the installation, type electron -h and it should display the version of the Electron CLI.
At the time this article was written, the version of Electron was 0.31.2.

Setting up the Project
Let’s assume the following basic folder structure:
my-app
|- cache/
|- dist/
|- src/
|-- app.js
| gulpfile.js
… where: - cache/ will be used to download the Electron binaries when building the app. - dist/ will contain the generated distribution files. - src/ will contain our source code. - src/app.js will be the entry point of our application.
Next, we will navigate to the src/ folder in our terminal and create the package.json and bower.json files for our app:
$ npm init
$ bower init
We will install the necessary packages later on in this tutorial.

Understanding Electron Processes
Electron distinguishes between two types of processes:

The Main Process: The entry point of our application, the file that will be executed whenever we run the app. Typically, this file declares the various windows of the app, and can optionally be used to define global event listeners using Electron’s IPC module.

The Renderer Process
The controller for a given window in our application. Each window creates its own Renderer 
Process.

For code clarity, a separate file should be used for each Renderer Process. To define the Main Process for our app, we will open src/app.js and include the app module to start the app, and the browser-window module to create the various windows of our app (both part of the Electron core), as such:
var app = require('app'),
    BrowserWindow = require('browser-window');
When the app is actually started, it fires a ready event, which we can bind to. At this point, we can instantiate the main window of our app:
var mainWindow = null;

app.on('ready', function() {
    mainWindow = new BrowserWindow({
        width: 1024,
        height: 768
    });
   
    mainWindow.loadUrl('file://' + __dirname + '/windows/main/main.html');
    mainWindow.openDevTools();
});

Key points:
                 We create a new window by creating a new instance of the BrowserWindow object.
                 It takes an object as a single argument, allowing us to define various settings, amongst which the default width and height of the window.
                 The window instance has a loadUrl() method, allowing us to load the contents of an actual HTML file in the current window. The HTML file can either be local or remote.
                 The window instance has an optional openDevTools() method, allowing us to open an instance of the Chrome Dev Tools in the current window for debugging purposes.

Next, we should organize our code a little. I recommend creating a windows/ folder in our src/ folder, and where we can create a subfolder for each window, as such:
my-app
|- src/
|-- windows/
|--- main/
|---- main.controller.js
|---- main.html
|---- main.view.js
… where main.controller.js will contain the “server-side” logic of our application, and main.view.js will contain the “client-side” logic of our application.
The main.html file is simply an HTML5 webpage, so we can simply start it like this:
<html>
<head>
    <meta charset="utf-8">
    <title>Password Keychain</title>
</head>
<body>
    <h1>Password Keychain</h1>
</body>
</html>

At this point, our app should be ready to run. To test it, we can simply type the following in our terminal, at the root of the src folder:

$ electron .
We can automate this process by defining the start script of the package.son file.



















Building a Password Keychain Desktop App
To build a password keychain application, we need: - A way to add, generate and save passwords. - A convenient way to copy and remove passwords.

Generating and Saving Passwords
A simple form will suffice to insert new passwords. For the sake of demonstrating communication between multiple windows in Electron, start by adding a second window in our application, which will display the “insert” form. 

Since we will open and close this window multiple times, we should wrap up the logic in a method so that we can simply call it when needed:

function createInsertWindow() {
    insertWindow = new BrowserWindow({
        width: 640,
        height: 480,
        show: false
    });
   
    insertWindow.loadUrl('file://' + __dirname + '/windows/insert/insert.html');
   
    insertWindow.on('closed',function() {
        insertWindow = null;
    });
}

Key points:
                 We will need to set the show property to false in the options object of the BrowserWindow constructor, in order to prevent the window from being open by default when the applications starts.
                 We will need to destroy the BrowserWindow instance whenever the window is firing a closed event.

Opening and Closing the “Insert” Window
The idea is to be able to trigger the “insert” window when the end user clicks a button in the “main” window. In order to do this, we will need to send a message from the main window to the Main Process to instruct it to open the insert window. We can achieve this using 

Electron’s IPC module. There are actually two variants of the IPC module:
                 One for the Main Process, allowing the app to subscribe to messages sent from windows.
                 One for the Renderer Process, allowing the app to send messages to the main process.

Although Electron’s communication channel is mostly uni-directional, it is possible to access the Main Process’ IPC module in a Renderer Process by making use of the remote module. 

Also, the Main Process can send a message back to the Renderer Process from which the event originated by using the Event.sender.send() method.
To use the IPC module, we just require it like any other NPM module in our Main Process script:
var ipc = require('ipc');
… and then bind to events with the on() method:
ipc.on('toggle-insert-view', function() {
    if(!insertWindow) {
        createInsertWindow();
    }
    return (!insertWindow.isClosed() && insertWindow.isVisible()) ? insertWindow.hide() : insertWindow.show();
});

Key Points:
                 We can name the event however we want, the example is just arbitrary.
                 Do not forget to check if the BrowserWindow instance is already created, if not then instantiate it.
                 The BrowserWindow instance has some useful methods:
                                 isClosed() returns a boolean, whether or not the window is currently in a closed state.
                                 isVisible(): returns a boolean, whether or not the window is currently visible.
                                 show() / hide(): convenience methods to show and hide the window.

Now we actually need to fire that event from the Renderer Process. We will create a new script file called main.view.js, and add it to our HTML page like we would with any normal script:

<script src="./main.view.js"></script>

Loading the script file via the HTML script tag loads this file in a client-side context. This means that, for example, global variables are available via window.. To load a script in a server-side context, we can use the require() method directly in our HTML page: require('./main.controller.js');.

Even though the script is loaded in client-side context, we can still access the IPC module for the Renderer Process in the same way that we can for the Main Process, and then send our event as such:
var ipc = require('ipc');

angular
    .module('Utils', [])
    .directive('toggleInsertView', function() {
        return function(scope, el) {
            el.bind('click', function(e) {
                e.preventDefault();
                ipc.send('toggle-insert-view');
            });
        };
    });
There is also a sendSync() method available, in case we need to send our events synchronously.

Now, all we have left to do to open the “insert” window is to create an HTML button with the matching Angular directive on it:
<div ng-controller="MainCtrl as vm">
    <button toggle-insert-view class="mdl-button">
        <i class="material-icons">add</i>
    </button>
</div>
And add that directive as a dependency of the main window’s Angular controller:
angular
    .module('MainWindow', ['Utils'])
    .controller('MainCtrl', function() {
        var vm = this;
    });



Generating Passwords
To keep things simple, we can just use the NPM uuid module to generate unique ID’s that will act as passwords for the purpose of this tutorial. We can install it like any other NPM module, require it in our ‘Utils’ script and then create a simple factory that will return a unique ID:
var uuid = require('uuid');

angular
    .module('Utils', [])
   
    ...
   
    .factory('Generator', function() {
        return {
            create: function() {
                return uuid.v4();
            }
        };
    })

Now, all we have left to do is create a button in the insert view, and attach a directive to it that will listen to click events on the button and call the create() method:

<button generate-password class="mdl-button">generate</button>
// in Utils.js
angular
    .module('Utils', [])
   
    ...
   
    .directive('generatePassword', ['Generator', function(Generator) {
        return function(scope, el) {
            el.bind('click', function(e) {
                e.preventDefault();
                if(!scope.vm.formData) scope.vm.formData = {};
                scope.vm.formData.password = Generator.create();
                scope.$apply();
            });
        };
    }])

Saving Passwords
At this point, we want to store our passwords. The data structure for our password entries is fairly simple:
{
    "id": String
    "description": String,
    "username": String,
    "password": String
}

So all we really need is some kind of in-memory database that can optionally sync to file for backup. For this purpose, Loki.js seems like the ideal candidate. It does exactly what we need for the purpose of this application, and offers on top of it the Dynamic Views feature, allowing us to do things similar to MongoDB’s Aggregation module.

Dynamic Views do not offer all the functionality that MongodDB’s Aggregation module does. Please refer to the documentation for more information.

Let’s start by creating a simple HTML form:
<div class="insert" ng-controller="InsertCtrl as vm">
    <form name="insertForm" no-validate>
        <fieldset ng-disabled="!vm.loaded">
            <div class="mdl-textfield">
                <input class="mdl-textfield__input" type="text" id="description" ng-model="vm.formData.description" required />
                <label class="mdl-textfield__label" for="description">Description...</label>
            </div>
            <div class="mdl-textfield">
                <input class="mdl-textfield__input" type="text" id="username" ng-model="vm.formData.username" />
                <label class="mdl-textfield__label" for="username">Username...</label>
            </div>
            <div class="mdl-textfield">
                <input class="mdl-textfield__input" type="password" id="password" ng-model="vm.formData.password" required />
                <label class="mdl-textfield__label" for="password">Password...</label>
            </div>
            <div class="">
                <button generate-password class="mdl-button">generate</button>
                <button toggle-insert-view class="mdl-button">cancel</button>
                <button save-password class="mdl-button" ng-disabled="insertForm.$invalid">save</button>
            </div>
        </fieldset>
    </form>
</div>

And now, let’s add the JavaScript logic to handle posting and saving of the form’s contents:
var loki = require('lokijs'),
    path = require('path');

angular
    .module('Utils', [])
   
    ...
   
    .service('Storage', ['$q', function($q) {
        this.db = new loki(path.resolve(__dirname, '../..', 'app.db'));
        this.collection = null;
        this.loaded = false;
       
        this.init = function() {
            var d = $q.defer();
           
            this.reload()
                .then(function() {
                    this.collection = this.db.getCollection('keychain');
                    d.resolve(this);
                }.bind(this))
                .catch(function(e) {
                    // create collection
                    this.db.addCollection('keychain');
                    // save and create file
                    this.db.saveDatabase();
                   
                    this.collection = this.db.getCollection('keychain');
                    d.resolve(this);
                }.bind(this));
               
                return d.promise;
        };
       
        this.addDoc = function(data) {
            var d = $q.defer();
           
            if(this.isLoaded() && this.getCollection()) {
                this.getCollection().insert(data);
                this.db.saveDatabase();
               
                d.resolve(this.getCollection());
            } else {
                d.reject(new Error('DB NOT READY'));
            }
           
            return d.promise;
        };
    })
   
    .directive('savePassword', ['Storage', function(Storage) {
        return function(scope, el) {
            el.bind('click', function(e) {
                e.preventDefault();
               
                if(scope.vm.formData) {
                    Storage
                        .addDoc(scope.vm.formData)
                        .then(function() {
                           // reset form & close insert window
                           scope.vm.formData = {};
                           ipc.send('toggle-insert-view');
                        });
                }
            });
        };
    }])

Key Points:
                 We first need to initialize the database. This process involves creating a new instance of the Loki Object, providing the path to the database file as an argument, looking up if that backup file exists, creating it if needed (including the ‘Keychain’ collection), and then loading the contents of this file in memory.
                 We can retrieve a specific collection in the database with the getCollection() method.
                 A collection object exposes several methods, including an insert() method, allowing us to add a new document to the collection.
                 To persist the database contents to file, the Loki object exposes a saveDatabase() method.
                 We will need to reset the form’s data and send an IPC event to tell the Main Process to close the window once the document is saved.
We now have a simple form allowing us to generate and save new passwords. Let’s go back to the main view to list these entries.

Listing Passwords
A few things need to happen here:
                 We need to be able to get all the documents in our collection.
                 We need to inform the main view whenever a new password is saved so it can refresh the view.
We can retrieve the list of documents by calling the getCollection() method on the Loki object. This method returns an object with a property called data, which is simply an array of all the documents in that collection:
this.getCollection = function() {
    this.collection = this.db.getCollection('keychain');
    return this.collection;
};
       
this.getDocs = function() {
    return (this.getCollection()) ? this.getCollection().data : null;
};

We can then call the getDocs() in our Angular controller and retrieve all the passwords stored in the database, after we initialize it:
angular
    .module('MainView', ['Utils'])
    .controller('MainCtrl', ['Storage', function(Storage) {
        var vm = this;
        vm.keychain = null;
       
        Storage
            .init()
            .then(function(db) {
                vm.keychain = db.getDocs();
            });
    });   
 
A bit of Angular templating, and we have a password list:
<tr ng-repeat="item in vm.keychain track by $index" class="item--{{$index}}">
    <td class="mdl-data-table__cell--non-numeric">{{item.description}}</td>
    <td>{{item.username || 'n/a'}}</td>
    <td>
        <span ng-repeat="n in [1,2,3,4,5,6]">•</span>
    </td>
    <td>
        <a href="#" copy-password="{{$index}}">copy</a>
        <a href="#" remove-password="{{item}}">remove</a>
    </td>
</tr>



















A nice added feature would be to refresh the list of passwords after inserting a new one. 
For this, we can use Electron’s IPC module. As mentioned earlier, the Main Process’ IPC module can be called in a Renderer Process to turn it into a listener process, by using the remote module. 

Here is an example on how to implement it in main.view.js:
var remote = require('remote'),
    remoteIpc = remote.require('ipc');

angular
    .module('MainView', ['Utils'])
    .controller('MainCtrl', ['Storage', function(Storage) {
        var vm = this;
        vm.keychain = null;
       
        Storage
            .init()
            .then(function(db) {
                vm.keychain = db.getDocs();
               
                remoteIpc.on('update-main-view', function() {
                    Storage
                        .reload()
                        .then(function() {
                            vm.keychain = db.getDocs();
                        });
                });
            });
    }]);

Key Points:
                 We will need to use the remote module via its own require() method to require the remote IPC module from the Main Process.
                 We can then setup our Renderer Process as an event listener via the on() method, and bind callback functions to these events.

The insert view will then be in charge of dispatching this event whenever a new document is saved:

Storage
    .addDoc(scope.vm.formData)
    .then(function() {
        // refresh list in main view
        ipc.send('update-main-view');
        // reset form & close insert window
        scope.vm.formData = {};
        ipc.send('toggle-insert-view');
    });

Copying Passwords
It is usually not a good idea to display passwords in plain text. Instead, we are going to hide and provide a convenience button allowing the end user to copy the password directly for a specific entry.

Here again, Electron comes to our rescue by providing us with a clipboard module with easy methods to copy and paste not only text content, but also images and HTML code:

var clipboard = require('clipboard');

angular
    .module('Utils', [])
   
    ...
   
    .directive('copyPassword', [function() {
        return function(scope, el, attrs) {
            el.bind('click', function(e) {
                e.preventDefault();
                var text = (scope.vm.keychain[attrs.copyPassword]) ? scope.vm.keychain[attrs.copyPassword].password : '';
                // atom's clipboard module
                clipboard.clear();
                clipboard.writeText(text);
            });
        };
    }]);

Since the generated password will be a simple string, we can use the writeText() method to copy the password to the system’s clipboard. 

We can then update our main view HTML, and add the copy button with the copy-password directive on it, providing the index of the array of passwords:

<a href="#" copy-password="{{$index}}">copy</a>

Removing Passwords
Our end users might also like to be able to delete passwords, in case they become obsolete. To do this, all we need to do is call the remove() method on the keychain collection. We need to provide the entire doc to the ‘remove()’ method, as such:

this.removeDoc = function(doc) {
    return function() {
        var d = $q.defer();
       
        if(this.isLoaded() && this.getCollection()) {
            // remove the doc from the collection & persist changes
            this.getCollection().remove(doc);
            this.db.saveDatabase();
           
            // inform the insert view that the db content has changed
            ipc.send('reload-insert-view');
           
            d.resolve(true);
        } else {
            d.reject(new Error('DB NOT READY'));
        }
       
        return d.promise;
    }.bind(this);
};

Loki.js documentation states that we can also remove a doc by its id, but it does not seem to be working as expected.

Creating a Desktop Menu
Electron integrates seamlessly with our OS desktop environment to provide a “native” user experience look & feel to our apps. Therefore, Electron comes bundled with a Menu module, dedicated to creating complex desktop menu structures for our app.

The menu module is a vast topic and almost deserves a tutorial of its own. I strongly recommend you read through Electron’s Desktop Environment Integration tutorial to discover all the features of this module.

For the scope of this current tutorial, we will see how to create a custom menu, add a custom command to it, and implement the standard quit command.

Creating & Assigning a Custom Menu to Our App
Typically, the JavaScript logic for an Electron menu would belong in the main script file of our app, where our Main Process is defined. 

However, we can abstract it to a separate file, and access the Menu module via the remote module:
var remote = require('remote'),
    Menu = remote.require('menu');
To define a simple menu, we will need to use the buildFromTemplate() method:
var appMenu = Menu.buildFromTemplate([
    {
        label: 'Electron',
        submenu: [{
            label: 'Credits',
            click: function() {
                alert('Built with Electron & Loki.js.');
            }
        }]
    }
]);

The first item in the array is always used as the “default” menu item.
The value of the label property does not matter much for the default menu item. In dev mode it will always display Electron. We will see later how to assign a custom name to the default menu item during the build phase.

Finally, we need to assign this custom menu as the default menu for our app with the setApplicationMenu() method:
Menu.setApplicationMenu(appMenu);

Mapping Keyboard Shortcuts
Electron provides “accelerators”, a set of pre-defined strings that map to actual keyboard combinations, e.g.: Command+A or Ctrl+Shift+Z.

The Command accelerator does not work on Windows or Linux. For our password keychain application, we should add a File menu item, offering two commands:
                        Create Password: open the insert view with Cmd (or Ctrl) + N
                        Quit: quit the app altogether with Cmd (or Ctrl) + Q
...
{
    label: 'File',
    submenu: [
        {
            label: 'Create Password',
            accelerator: 'CmdOrCtrl+N',
            click: function() {
                ipc.send('toggle-insert-view');
            }
        },
        {
            type: 'separator' // to create a visual separator
        },
        {
            label: 'Quit',
            accelerator: 'CmdOrCtrl+Q',
            selector: 'terminate:' // OS X only!!!
        }
    ]
}
...

Key Points:
                        We can add a visual separator by adding an item to the array with the type property set to separator.
                        The CmdOrCtrl accelerator is compatible with both Mac and PC keyboards
                        The selector property is OSX-compatible only!

Styling Our App
You probably noticed throughout the various code examples references to class names starting with mdl-. For the purpose of this tutorial I opted to use the Material Design Lite UI framework, but feel free to use any UI framework of your choice.
Anything that we can do with HTML5 can be done in Electron; just keep in mind the growing size of the app’s binaries, and the resulting performance issues that may occur if you use too many third-party libraries.

Packaging Electron Apps for Distribution
You made an Electron app, it looks great, you wrote your e2e tests with Selenium and WebDriver, and you are ready to distribute it to the world!

But you still want to personalize it, give it a custom name other than the default “Electron”, and maybe also provide custom application icons for both Mac and PC platforms.

Building with Gulp
These days, there is a Gulp plugin for anything we can think of. All I had to do is type gulp electron in Google, and sure enough there is a gulp-electron plugin!

This plugin is fairly easy to use as long as the folder structure detailed at the beginning of this tutorial was maintained. If not, you might have to move things around a bit.

This plugin can be installed like any other Gulp plugin:
$ npm install gulp-electron --save-dev

And then we can define our Gulp task as such:
var gulp = require('gulp'),
    electron = require('gulp-electron'),
    info = require('./src/package.json');

gulp.task('electron', function() {
    gulp.src("")
    .pipe(electron({
        src: './src',
        packageJson: info,
        release: './dist',
        cache: './cache',
        version: 'v0.31.2',
        packaging: true,
        platforms: ['win32-ia32', 'darwin-x64'],
        platformResources: {
            darwin: {
                CFBundleDisplayName: info.name,
                CFBundleIdentifier: info.bundle,
                CFBundleName: info.name,
                CFBundleVersion: info.version
            },
            win: {
                "version-string": info.version,
                "file-version": info.version,
                "product-version": info.version
            }
        }
    }))
    .pipe(gulp.dest(""));
});

Key Points:
                        the src/ folder cannot be the same as the folder where the Gulpfile.js is, nor the same folder as the distribution folder.
                        We can define the platforms we wish to export to via the platforms array.
                        We should define a cache folder, where the Electron binaries will be download so they can be packaged with our app.
                        The contents of the app’s package.json file need to be passed to the gulp task via the packageJson property.
                        There is an optional packaging property, allowing us to also create zip archives of the generated apps.
                        For each platform, there is a different set of “platform resources” that can be defined.

Adding App Icons
One of the platformResources properties is the icon property, allowing us to define a custom icon for our app:

"icon": "keychain.ico"

OS X requires icons with the .icns file extension. There are multiple online tools allowing us to convert .png files into .ico and .icns for free.

Conclusion
In this article we have only scratched the surface of what Electron can actually do. Think of great apps like Atom or Slack as a source of inspiration where you can go with this tool.

I hope you found this tutorial useful, please feel free to leave your comments and share your experiences with Electron!
;;

Source: Originally published on the Toptal Engineering blog

Electron is a popular framework for building cross-platform desktop applications using web technologies such as HTML, CSS, and JavaScript. Electron uses Node.js and Chromium to provide a runtime environment for building desktop applications that can run on Windows, macOS, and Linux.

To build an Electron app, you need to have a good understanding of web technologies, particularly JavaScript and HTML. You will also need to learn how to use Node.js, which is the runtime environment that Electron is built on.

Here are the basic steps to build an Electron app:

Set up your development environment: You'll need to install Node.js and Electron on your machine, and set up a code editor such as Visual Studio Code.

Create your app: You can use HTML, CSS, and JavaScript to build the user interface of your app, just like you would for a web app.

Integrate with Node.js: You can use Node.js to perform file operations, access system resources, and interact with the operating system.

Package your app: Once your app is complete, you can use Electron's packaging tools to create an executable file for each platform you want to support.

Distribute your app: You can distribute your app through various channels, such as an app store or a website.

Electron provides a rich set of APIs for building desktop applications, including access to system resources such as the file system, the clipboard, and the network. With its ease of use and cross-platform capabilities, Electron is a great choice for building desktop applications that can run on multiple operating systems.

The USB-C Moment for AI: A Student’s Guide to the Model Context Protocol (MCP)

  The USB-C Moment for AI: A Student’s Guide to the Model Context Protocol (MCP) In the rapidly evolving world of Artificial Intelligence, ...