StackEdit 4.0: Multiple Instances and Local Hosting

Tweet

There are many, many MarkDown editors out there. In fact, there are so many, I was made to rant about their current state not so long ago, and reviewed a strong competitor in the field in the hopes of finding the holy grail MD app, to no avail.

StackEdit 4.0

StackEdit has, through all that time, remained at the top of the browser based MD editor world, but it always lacked a certain something. The document management was less than ideal, with a chaotic side menu and the lack of a proper quicksearch option, and the spellchecker wasn’t native which caused unnecessary performance hits, especially on larger documents. The stats counters weren’t as approachable as they are now, and options to extend the editor were more limited. Plenty of publish options present in version 4 were missing, too. Most of these shortcomings were addressed in the beta version, but this version was neither very publicly available, nor declared stable – you used it at your own risk of data loss and crashes. I’ve personally been using it reliably for months, but I understand the hesitation of the masses. Last week, however, @benweet finally released the source code of version 4 on Github and geared up for a public release.

New features include a much improved documents panel with proper folder management, so you can stay on top of your files at all times. You can also search for any document in your stack (very important when you have hundreds).

Synchronization with cloud services like Dropbox, Google Drive, Github and others has been much improved, and I’ve personally been enjoying auto-synchronization with Google Drive for a while now. While I’m typing, my documents get automatically synced up with my cloud services, so there’s no fear of data loss.

You can now also add inline comments to your documents, allowing you to share them with others and collaborate in a very dynamic manner. The actual sharing of documents could still use some work, but one thing at a time.

Running it locally

Here at SitePoint, we use StackEdit regularly. However, aside from being able to run multiple instances at once, a common complaint has always been code blocks. We currently use a code highlighter in our backend which requires [ code ] tags. While this will eventually change, the fact still remains that a direct to HTML export from StackEdit produces properly classed <code><pre> blocks, which don’t go hand in hand with the highlighter we use. A solution for custom code block exporting should be rather easy to code, but up until now, the source code for StackEdit remained unavailable, and the extensions it did offer weren’t customizable enough.

To get around this, let’s start with installing StackEdit into a Homestead Improved instance.

To get StackEdit to run locally, first deploy a regular Homestead Improved instance. No need to make any specific changes, just run it as if you were starting a PHP project — identical to what link tells you to do. Then, SSH into the VM and follow these steps:

cd ~/Code
git clone https://github.com/benweet/stackedit
cd stackedit
npm install --no-bin-link
bower install
(export PORT=5000 && node server.js)

These are more or less the instructions as laid out here, only with a changed port and an added flag to npm install.

The --no-bin-link flag tells NPM to avoid symlinks. As we’ve learned before, Vagrant and symlinks don’t get along well on Windows hosts, so in an effort to be fully cross-platform, we’re using this flag here.

Note for Windows hosts: If npm install throws a throng of errors at you, that’s usually due to a Windows file-path length limitation. Windows can’t support filenames longer than 260 characters, and NPM being what it is, the nested node_modules can have several levels of sub folders before getting anywhere. Booting the VM up from a more “root” location usually solves this problem – in my particular case, moving the VM folder to D: and running vagrant up from there solved things.

Then, to access your locally running instance of StackEdit, just visit http://homestead.app:5000 in your host machine’s browser.

Running multiple instances of StackEdit

In my original post about the state of MarkDown editors, I noted StackEdit’s single-instance limitation as a big downside. And indeed, it is a big one – when you’re editing a lot of posts, some of which are parts of a single series, you tend to have to jump to and fro every so often.

When a web application uses local storage, that local storage container is domain-bound, per session. This means you can actually use several instances of StackEdit even in its original online form, if you open one in your main browser, one in an incognito window, another one in another browser, and so on. This is because each of those domain-session combinations is unique, and uses its own local storage. But in an age where a single browser tab can use over 4GB of RAM (I’m looking at you, Tweetdeck and Google+), opening new browsers and windows is less than an ideal approach.

To get around this when running our StackEdit instance locally, we can easily define several entries in our host machine’s hosts file.

127.0.0.1 test.app
127.0.0.1 test2.app
127.0.0.1 test3.app

Opening http://test.app:5000, http://test2.app:5000, andhttp://test3.app:5000 will open three different instances of the same running StackEdit application, each with its own local local storage cache.

Those more attentive among you may now be wondering: “But doesn’t that mean we can define domain aliases in the hosts file for the live version of StackEdit, too?” Indeed it does. If we ping stackedit.io to find out its IP address (54.236.87.105 at the time of writing) and put the following into the hosts file:

54.236.87.105 stack1.app
54.236.87.105 stack2.app
54.236.87.105 stack3.app

we can open the live hosted Stacks just like those we just opened locally, by visiting stack1.app/, stack2.app/ and stack3.app/ in the host machine’s browser. There’s a caveat though – since StackEdit is a Heroku app, the IP address of the app may change periodically and maintaining an up to date hosts file would be tedious. Hence, our Vagrant-hosted approach is obviously better – the IP is guaranteed to stay at 127.0.0.1.

Sharing Data

Of course, the downside to this approach is having three separate local storage databases – this decouples your StackEdit tabs, and prevents you from sharing the files you’re editing across these “different” instances.

This will lead to each StackEdit instance being truly separated from the rest, and unable to access the data from the other ones. Of course, you could copy and paste the content from one into the other and maintain different document databases per domain, but that’s too much work. There are two approaches you can take here to solve this.

1. Sharing LocalStorage Data

This approach can be applied to both the online and your locally hosted versions. Like we said before, local storage data is unique per origin (origin being a domain+session combination). Domain X cannot access the local storage of domain Y, and for good reason – if they’re unrelated, they shouldn’t share data.

This is where this post comes in handy. It’s almost four years old now, but produced a very interesting project. Put simply, you can use this library to share localStorage across domains, but you need to manually whitelist each domain. In other words, to use this, you would need to define several hosts entries for your StackEdit instances (whether online or locally) like we did above, whitelist them, and then implement some simple localStorage sharing code. The details of the implementation are outside the scope of this post, and, once you see the second approach, will seem somewhat unnecessary.

2. Deactivating Uniqueness Check

The only thing actually preventing StackEdit from running two or more instances at once is a uniqueness check built into the application. To be more precise, it’s a call to checkWindowUnique() on line 316 in public/res/core.js. Comment this line.

Then, launch a couple of tabs with test.app:5000/?debug to see if it works. The ?debug param is necessary to make StackEdit serve unminified files so we don’t have to run grunt for minification before testing our changes. If you’re happy with the changes, just run grunt in the stackedit folder. Unless you do some additional tweaks to make the code JSHint-valid, you’ll need to run grunt with the --force flag, like so;

grunt --force

This tells it to ignore the JSHint problems (in the particular case above, it’ll tell you that the function checkWindowUnique is defined, but never used). An easy no-force workaround is commenting out that function too, along with the line that calls it.

You’ll now be able to run multiple instances of StackEdit in your browser, and all the documents will be shared between them. I’ve been using this approach for a while now, and have yet to encounter the race condition that caused this block to be implemented in the first place.

If we have StackEdit Tab1 (ST1) and StackEdit Tab2 (ST2) and file X and file Y, keep in mind the following rules:

  1. If file X is open in both ST1 and ST2, the changes from the last used ST are applied. Editing file X in ST1 and then refreshing ST2 will show you the contents of file X as present in ST1, and vice versa.
  2. If file X is open in both ST1 and ST2, and you switch ST2 to file Y, make some changes, then refresh ST1, ST1 will also be switched to file Y. This is because StackEdit uses localStorage to keep track of your location and open files too, and syncs them up, so you end up where you left off when you re-open the app

I advise against using this multiple instances hack for anything other than only reading one document while writing to the other. Anything more than that gets chaotic rather fast.

Conclusion

StackEdit is a powerful MD editor, but even the best need upgrades sometimes. In this post, we looked at hosting our own local instance of StackEdit and at deploying multiple instances at the same time which share localStorage data. In a followup post, we’ll make modifications to the HTML output and make sure we can tweak the code blocks to our liking. Got any of your own hacks to share? Let us know!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • https://twitter.com/benweet Benoit Schweblin

    Thanks for the useful article. Allowing two different instances to modify the localStorage will also cause trouble if file syncing with Google Drive and Dropbox occurs in parallel. And a minor issue: StackEdit 4.2 now uses gulp instead of grunt.

    • http://www.bitfalls.com/ Bruno Skvorc

      Thanks for chiming in!
      Is that Google Drive AND Dropbox, as in, two services, or EITHER of the services, but both active in each instance?

      Will update post for Gulp soon, cheers!

      • https://twitter.com/benweet benweet

        Synchronization is triggered every 3 min on every instance and is performed on both Google Drive AND Dropbox, assuming you have files to sync on both accounts. If you don’t have any file to sync, you won’t have any problem.