Drafts: Exploded Markdown Preview11 Jul 2020
With the introduction of the TADpoLe library, I’m able to share an action via the ThoughtAsylum Drafts action group that I think a lot of people who write for the web may find useful. It’s a special web preview action that incorporates a number of features that I think make it pretty special. Welcome to my explanation of the “Exploded Markdown Preview”.
Exploding in programming terms is where something is expanded into constituent parts. It is often used as a way to tokenise strings to arrays and to break down objects. Here, I’m using in the sense that I’ve taken apart various elements to be previewed and, well, blown them up in a projector sort of sense so that you can preview the result. It was the first thing that popped into my head when trying to name the action in the ThoughtAsylum action group, so I just went with it.
But enough with the name? What does this preview actually do? Only a handful of things really, but I think that they are interesting things, and I’m going to step through them to build up a picture of what the preview can currently do.
Structure of the Action
First of all, we’ll take a look at the structure of the TAD-Exploded Markdown Preview action. It consists of four action steps:
1. Include Action
This step includes the
TAD action which is responsible for loading the TADpoLe library. The library incorporates a set of functions that the action will make use of in a later step.
2. Define Template Tag
Next we define the content of a custom template tag called
css. As you might imagine, this is where the CSS to be used in the preview can be defined. By splitting this out as a template tag there’s more opportunity here for re-use. If you duplicate this action you could choose to define a different set of content, you could set it programmatically based on some user prompt choices, or any number of other variations.
This step calls a function that transforms the content of the current draft into HTML for previewing. It is within this function that most of the interesting aspects of the preview happen.
The function is documented in on tadpole.thoughtasylum.com, but we’ll be covering below what it is doing in practical terms. There are two things that are worth noting.
- The function sets a custom Drafts tag,
previewto have the HTML content it generates.
- The function has a single parameter related to Drafts checkboxes, and this is set to
4. HTML Preview
The final step is an HTML preview action that contains the HTML document framework which is then populated using Drafts template tags:
css- the CSS defined in step 2 is inserted into the HTML document framework within a
title- the first line of the draft is used to populate the preview title.
preview- the HTML generated in step 3 is inserted into the main body of the HTML document framework.
If you wanted to include additional external libraries or styling, like the syntax highlighting included in the “TAD-Preview with Syntax Highlighting” action, this is the section you would amend to include it. But just to reiterate on the use of the ThoughtAsylum action group, I’d recommend rather than editing this action, you duplicate it to your own action group, rename it, and tailor that one to your own particular needs.
Generating the HTML
From the details above, the script in step 3 is where all of the “exploding” occurs. Now I’ll walk through the things it does.
Inclusion of Templates
Firstly I’d like to talk about templates. Now templates in Drafts can cover a few things and I don’t like overlapping terminology within a product, but Drafts is universally awesome, so I try instead to be specific when talking about templates.
Many people consider templates in Drafts to be contents that you can use to prime a new Draft. For example if you have a standard layout for taking meeting notes, the chances are that you have a meeting note template you can apply. This could be done by cloning an existing Draft, using a TextExpander snippet (or equivalent) to populate a new Draft, or having an action that adds the template text from an insert action, another Draft, or even a file.
If you are interested in this sort of templating, do take a look at the “TAD-Template from Draft” and “TAD-Template from File” actions in the action group.
Now, I expect many Drafts users may also be familiar with Templates as per the documentation. These are template tags as we utilised in step 2 for specifying the CSS content and steps 3 and 4 to get the HTML content (and title) into the final HTML preview. But did you realise that there’s a template tag (not to be confused with a Drafts tag), called
template? It’s one of my favourites as in general it provides a quick and easy way to keep static content outside of Drafts, but still make easy use of it in working with your drafts.
The Drafts documentation describes the
template template tag, a kind of utility tag, as follows:
Inserts a template store in a file in
“path” should be the related path to a file which exists in that folder, for example:
File will be loaded an evaluated as if it’s text was inline in the current template, allowing re-use of templates across actions.
The TAD-Exploded Markdown Preview action includes template tag evaluation, and so if you include a template of the format described, the content of that file will be automatically included into the preview.
Inclusion of Other Drafts
Now, the use of double square brackets in Drafts may be familiar to some of you as being the same format used in many applications for wiki-linking. As well as this wiki-link approach for file templates, since version 20, Drafts now includes support for other wiki-links.
I had already been using a different syntax to indicate including Drafts in a preview based on a Draft’s UUID, but with this update I switched my system to use the new standard for intra-Drafts linking.
The TAD-Exploded Markdown Preview action will automatically explode Draft wiki-links for titles or UUIDs to the matching draft’s content.
This will explode a draft based on title. [[A Draft Called Foo]] This will also explode a draft based on title. [[d:A Draft Called Bar]] This will explode a draft based on uuid. [[u:0EB6CB02-A1E3-499F-B205-000000000000]]
Note that I have not made this functionality recursive. If an exploded draft contains a wiki-link, that will be left as is; there’s a chance of infinite looping or an arbitrary limit and generally I’ve found a single level of explosion sufficient.
The action will search all drafts (so that excludes the trash in Drafts’ definition of ‘all’), and should the draft not be found, it will leave the link in place.
Inclusion of Images
Including images in a preview is pretty straight forward. Well, pretty straight forward as long as your image is hosted and served. The preview in Drafts is an isolated browser. It can’t go arbitrarily digging through your file system, for good reason, and it has to conform to standard browser practices.
The first step solution therefore is to host your images on a web server. You could do this on the Internet. You could do this on a local web server running on your Mac, or via a split screen app that supports local web serving of files on an iPad. The iPhone however is not so lucky, and discussions on social media and the forums with a few other Drafts users has indicated that no one really likes doing it that way. There seems to be a consensus that people would like to have their images in iCloud Drive, perhaps in the drafts folder structure and be able to preview the images from there,
While I haven’t cracked it for all possible requirements, I think I’m close enough for practical purposes to say I have a working solution. It’s one that requires no hosting and utilises iCloud Drive, so you can use it with Drafts being the only running app, and it even works on the iPhone.
How? Well it comes down to something called Data URLs. This scheme allows browsers to deal with files in encoded data formats embedded within the document. In the case of images, this is base64 encoding.
The premise here then is that we take each image file we want to use and we generate a sister-file which is a base64 encoded version of the file. At this point we’re back to loading in text information from a text file and substituting it into the HTML in the appropriate format for a Data URL. That’s very much inline with the features already discussed.
Base64 Encoding Images: iPhone & iPad
Shortcuts is the tool of choice on i*OS and it is ridiculously easy to build something to transform the image files. The shortcut starts with a file picker action that allows you to select the image files that you wish to encode to base64. For each file selected, the shortcut reads in the image, converts it to base64 and then writes back the file with a
.b64.txt extension. This does not replace the original image file, it simple co-exists alongside it. In the draft, you’ll still refer to the original image file, the preview action will simply look for the corresponding
.b64.txt file to substitute into the preview in its place.
If you find you use the shortcut a lot, consider adding it to one of your own action groups using Drafts’ Run Shortcut action.
Base64 Encoding Images: Mac
Currently, Shortcuts is not available on the Mac, and chances are, when working on a Mac, you are probably working with the image files in the Finder (or another file management app). I therefore elected to build a Quick Action using Automator so that I could select a set of files, and generate a Base64 image via the services menu. The solution is perhaps even easier on the Mac; as long as you happen to know that openssl (pre-installed on every modern Mac) happens to support base64 encoding.
The automator flow above takes the selected Finder items and passes them into a shell script. The script loops over each file and has openssl encode the file to a new file with an additional
.b64.txt file extension. Just like the Shortcuts solution.
for img in "$@" do openssl base64 -in "$img" -out "$img.b64.txt" done
Quick Actions/Services are located in
$HOME\Library\Services\, but if you find the lack of code-signing on the workflow an issue, create a new Quick Action in Automator and add the details in yourself.
Conversion of Checkboxes
This final feature is a little different to the others in that it isn’t pulling any content from elsewhere. Rather it is transforming existing content.
Drafts’ checkboxes are not part of the Markdown standards, but it can sometimes be useful to convert them into HTML checkboxes. To that end the function, and thus the preview action, will convert the following Drafts checkbox structures to HTML checkboxes as follows.
- A Drafts empty checkbox marker
[ ]is converted to an empty HTML checkbox .
- A Drafts checked checkbox marker
[X]is converted to a checked HTML checkbox .
Preview in Action
Finally, let’s take a look at a worked example demonstrating some of these features.
Let’s start with a text file containing some Markdown formatted content.
# Example01: Content from a template file We have a small amount of text We also have some checkboxes - [ ] Unchecked - [X] Checked
Next up I created a draft with the following content.
# Batman Info Batman is also known as the 'Caped Crusader', the 'World's Greatest Detective', and the 'Dark Knight'. !(/images/batman.png)
Followed by a draft like this.
# Wonder Woman Info Wonder Woman is the princess of a race of female warriors known as the Amazons. !(/images/wonderwoman.png)
A draft with a UUID of BCD84FB2-1AD2-4D3B-A64E-BCE6D29ED932 like this.
# Superman Info *This draft has a UUID of BCD84FB2-1AD2-4D3B-A64E-BCE6D29ED932* Superman's middle name from Earth is Joseph. !(/images/superman.png) [[u:2DABA2B9-5DB5-40BA-AE9D-D64070E83FE2]]
And then a draft with a UUID of 2DABA2B9-5DB5-40BA-AE9D-D64070E83FE2 like this.
# Green Lantern Info *This draft has a UUID of 2DABA2B9-5DB5-40BA-AE9D-D64070E83FE2* Hal Jordan's arch-nemesis Sinestro's look was based on actor David Niven. !(/images/greenlantern.png)
You may have noted the references above to image files. These have been added to an
images folder at the root of my Drafts iCloud folder, and the base64 counterparts generated.
Finally, I created a draft like this to be previewed.
This is the Draft that will be previewed --- > Here we will bring in the file. [[template|example01.txt]] --- > Here we will bring in the draft for Batman specified using a basic wiki-link. [[Batman info]] --- > Here we will bring in the draft for Wonder Woman specified using a title based wiki-link. [[d:Wonder Woman info]] --- > Here we will bring in the draft for Superman specified using a UUID based wiki-link. [[u:BCD84FB2-1AD2-4D3B-A64E-BCE6D29ED932]] --- > Here we will try to bring in a non-existent draft for Aquaman specified using a a basic wiki-link. [[Aquaman Info]] ---
When the TAD-Exploded Markdown Preview action was run (on my iPhone), it produced the following.
There are three things to highlight from this example.
- As expected, nothing loaded for the Aquaman Info draft that didn’t exist, and the wiki-style link was left in place.
- The Green Lantern information information was absent and the link was displayed instead, but the draft exists. This is because that link was included via the Superman Info draft and not the top level draft. I mentioned earlier that there is only a sweep at the root level to explode (or transclude) the draft and file content into the preview. I implemented it as a non-recursive feature so as to avoid arbitrary depth restriction / infinite loops.
- It works.
That’s a bit of a peak into how the preview action works, and some details, tools and examples to help you get something similar working for you. you can of course just use the preview action as it stands, but I suspect that the people who will make most use of this will probably have benefit to tailoring the CSS and HTML, and so I would recommend duplicating the action to another action group and tailoring it to your own particular needs.
Hopefully this will satiate at least a few Drafts users’ requirements around rich previews on the go.