It’s been a while since moving from Squarespace to using Pelican as a static site generator for this website. Squarespace is beautifully designed, very resilient and for the most part dependable (they experienced several DDOS attacks in the past). But it’s not flexible when it comes to deeply customizing your site or when writing content.

Site generators like Pelican are extremely malleable. Since they process markdown files and images stored in certain pre-defined folders, the user only needs a text editor and an internet connected device to write and post.

Up until now, I’ve been using a combination of Apps for writing posts depending on which platform I’m working on: On windows and Mac, I use Sublime Text and on iOS I use Editorial. Each of them is arguably the best text editor on their respective platform. Editorial may be lagging behind as it hasn’t been updated in a long time, but nevertheless continues to work fine on iOS 10.

After posts were written, I would use Dropbox as a glue between devices and Hazel to move files around between folders in order to generate the site. It’s a pretty straight forward process, with some caveats: visualizing posts with images on the iPad was a pain because of where the site’s content files are located in my computer (i.e. not in Dropbox) and the way markdown links work. There was a lot of massaging involved deleting temporary files and shuffling images around. Also, in order to work offline with Editorial syncing through Dropbox, I had to remember opening Editorial and syncing the files beforehand.

Enter Ulysses

I read a lot about Ulysses before writing this post. People are using it to writing blog posts, novels, do research and even writing technical books. I have yet to find a bad review. But what is so special about it?. Well, for starters everything is in one library, that’s the main feature that sets it apart from similar Apps. All documents (known as Sheets) and attachments are in one place, which allows to have a consistent look and previewing documents everywhere. There is an iOS version and a Mac OS version, and syncing is beautifully done through iCloud. It’s probably the best iCloud syncing implementation I have seen on any platform. Text, images and attachments were available in all platforms almost instantly in my tests. There is also support for Dropbox, but not all features are available when syncing that way (at least for now).

There is no Windows application, which is a big disadvantage for those of us who are forced to live in that environment during office hours. However, Ulysses allows to sync with External Folders and Dropbox folders can easily be added to the library on both the Mac App and the iOS App. The library, including synced external folders, can be used offline.

Ulysses for Mac - Preferences Pane.
Ulysses for Mac - Preferences Pane.

As seen above, Ulysses allows direct publishing to Medium and Wordpress, but there is no official support for other blogging platforms. That’s when the flexibility of Pelican and other static site generators comes into play. With some additional tools, posts can be sent to the Pelican in an almost seamless process.

General Setup

Here is the general layout of the process1:

From Ulysses to Pelican - General Process.
From Ulysses to Pelican - General Process.

As it can be seen, the heavy lifting will be done by a Python 3.5 Script. Posts can be written on either Ulysses for Mac or Ulysses for iOS, everything will be synced through iCloud. Images can be imported into the library from multiple sources, as always, and they will be automatically copied and placed in Pelican’s content folders at the end of the process, ready to be used when generating the website. Alternatively, images from outside Ulysses library can be manually put in Pelican’s content folders. As long as they are adequately referenced in the post/sheet, they will be used when generating the website.

Here is what is needed on each platform:

On the Mac

  • Ulysses for Mac.
  • Python 3.5.
  • Python script (more on this below).
  • Bash script for executing the Python script. The Python script can be run directly without this script. However, I’m using it to facilitate execution, do some testing and save some lines when running the Python script from the command line.

The Bash Script is just one line in the following format:

<Path to Python Interpreter> <path to Python script> "$1"

So, it would be something like this:

/anaconda/envs/Anaconda3/bin/python3.5 /Scripts/Python/pelican_injection.py "$1"

Remember to make the file executable like so:

chmod 700 pelican_injection.py

On iOS

  • Ulysses for iOS.
  • Workflow iOS App. This is one of my favorites apps on the platform. It allows to customize your device and automate repetitive tasks in an elegant way. it’s tremendously effective in filling the gaps that separate iOS as a mobile platform from a full feature desktop OS.
  • Workflow for Workflow App (I wish there was a better way to referencing the App and its actions).

Python Script

This part needs a bit more explanation as some configuration is required for the whole process to work as intended. Don’t worry, it’s very easy to setup.

The script is written in Python 3.5 (although it runs fine on Python 2.7). Here is a general overview of what the script does. It’s the glue that bonds all together: Ulysses sheet, custom HTML and Pelican contents folder.

From Ulysses to Pelican - Python Script
From Ulysses to Pelican - Python Script

It should be run passing an argument that must be either a Zip file or a Markdown file, which are two of the three file extensions Ulysses can export to when exporting sheets to Markdown format (the Zip file includes all the images referenced in the sheet in addition to the Markdown file). The third is a TextBundle format, but the script is not compatible with it (yet).

If a Zip file is passed, the script extracts the images and copies them into the Images content folder in Pelican. Then, it takes the Markdown file, renames it based on the Slug (which is contained in the header of every post), inserts Pelican’s image output folder on every Markdown image link and performs what I call HTML Code Injection.

Markdown files can include raw HTML code which is then passed to the rendered HTML page. HTML Code Injection is just a method I use to abbreviate the code that should be included in the final rendered page. For example, the Python script includes a function called before_after which inserts the necessary HTML code in the Markdown file to show the before-and-after image comparison I use on some review posts, like this one. In order to accomplish that, I just include the following line in the Ulysses sheet at the place where I want the image comparison to be inserted:

<beforeafter> before_filename|after_filename </beforeafter>

For this particular case, the before_filename and after_filename image files should be manually copied to Pelican’s image content folder in order to be rendered (i.e. They are not included in Ulysses library).

Keep in mind there is no need to use this feature, it’s just an additional capability I’ve included for my particular case. I use this methodology to include the code that is later rendered as Quarks Rating and 360 rotation.

One thing you do need to do is configuring the script Constants holding the paths to Pelican content folders (POSTS_FOLDER and IMAGES_FOLDER) as well as the path to the output image folder relative to the site (in the case below ”/images/“). For that, just edit the following section at the top of the script according to the way your Pelican folders are arranged on your system:

# Constant Definition
# -------------------

# Path to be prepended to image links in markdown file, like so: ![](IMAGE_LINK<image_filename>).
IMAGE_LNK = '/images/'

# Pelican Content Folders:
POSTS_FOLDER = 'Insert Path to Pelican markdown content folders.'
IMAGES_FOLDER = 'Insert Path to Pelican image content folders.'
FILES_FOLDER = 'Insert Path to Pelican files content folders. Not used in this version of the script.'
# -------------------

That’s it!. That’s all you need to do. The script is free to use and modify as needed.

Note - There is no need to rename the file when exporting from Ulysses. The script takes the Slug field in the header and renames it accordingly. - Be aware that the script will replace any file in the output folder that shares the same name/slug as the one being converted. - Sheet attachments are not transferred to Pelican since they are not exported by Ulysses in the zip file.

Workflow App

The Workflow App and associated Workflow are instrumental for the process to work seamlessly on the iOS platform. As seen in the diagram above, the workflow sends the exported sheet from Ulysses to the Python script in the remote machine where Pelican is run. This is done over an SSH connection (through Workflow’s Run Script Over SSH action), so it’s fast, reliable and secure.

From Ulysses to Pelican - Workflow.
From Ulysses to Pelican - Workflow.

Since Ulysses can send either a zip file or a text file when exporting from iOS, the Workflow automatically detects the file extension and reacts accordingly. When the workflow is imported, it’ll ask for the necessary info to set everything up: folder path for the script on the remote machine and login credentials. Everything will be stored locally.

Ulysses for Mac

The Mac OS version of Ulysses is an extremely polished product. You can tell The Soulmen are a detail-oriented team. Simplicity and elegance are probably the two terms that would describe the App more accurately. It’s very responsive for the most part (it was a bit slow with long Sheets but that was remedied in the last update).

Ulysses for Mac - Main Window.
Ulysses for Mac - Main Window.

Both the Mac OS and the iOS versions are continuously updated. Not because of bugs needing to be squashed, but because the team constantly adds features and refines the product. It’s truly a pleasure to use an App ecosystem in which so much thought has been put into.

When working on Ulysses for Mac, the process of exporting to Pelican is very straight forward: just export the Ulysses Sheet in Text and then Markdown format to your disk. If the Sheet has images, they will be exported alongside the Markdown file, in the same folder (note: they will not be compressed into a Zip file like on the iOS version). Since the script can only take either Markdown (.md) files or compressed files (.zip) you can either compressed the files and pass the resulting Zip file as an argument to the shell script or manually copy the image files to the appropriate Content folder on Pelican and run the Markdown file through the Shell script. So, for the first option, the following should be typed on the Command Line (obviously, change the path and the filenames below per your particular case):

/path-to-script/pelican_injection.sh ulyssesExport.zip

That’s the manual way of doing it. There is an even more streamlined way to go through this process: through an Alfred Workflow on the Mac.

Alfred Workflow for automating the Ulysses export process.
Alfred Workflow for automating the Ulysses export process.

Using this Alfred Workflow, I just export the files to disk from Ulysses, select them on the Finder, type Alt CMD \ and select Send to Pelican from the Menu. Done!, everything should now be on Pelican’s Content folders adequately formatted and ready for the site to be generated.

Ulysses for iOS

The iOS version of Ulysses has become one of my favorite apps on the platform. It simply invites you to continuing writing. It’s elegantly designed, very responsive, with great iCloud synchronization. As mentioned before, it’s possibly the best third party iCloud implementation as of today.

Ulysses for iOS on iPad Pro.
Ulysses for iOS on iPad Pro.

Everything on the App feels native and well thought out. All features on the Mac version have been brought to iOS without compromising the user interface: attachments, tags/keywords, external folders, etc. Images can be imported from the Photos App or from the other Apps or cloud services, such as Dropbox.

Exporting sheets works as expected and very similar to the way it has been implemented on the Mac. The procedure to export to Pelican is as follows:

  • Tap on the Share icon and select the format. In this case Text and then Markdown.
  • Tap on the three dots () and then Open in Another App.
  • On the new window, select Run Workflow and once the list of workflows is shown, just select Remote Pelican Injection.

That’s it!. It’s that easy.

Putting It All Together

I tried to make the process as easy and streamlined as possible on both platforms, MacOS and iOS. Looking at the paragraphs above, it may seem a bit complicated, but trust me, it’s not. As an example, here is a video for exporting this whole post from Ulysses for iOS into Pelican. When it’s done, all images and Markdown file are adequately formatted and organized within Pelican content folders. it only takes 17 seconds.

Note: Disregard the error at the end of the video, the Workflow was run with a dummy IP for demo purposes.

Exporting from Ulysses for iOS to Pelican Site Generator from Moving Electrons on Vimeo.


  1. Icons made by Freepik and Roundicons from www.flaticon.com.