Matt Gemmell

TOLL is available now!

An action-thriller novel — book 2 in the KESTREL series.

★★★★★ — Amazon

Keyboard-Driven Cross-Linking in Drafts

development & tech 5 min read

I use Drafts as my text-processing tool of choice. I do all my actual writing in Ulysses, but when it’s time to prepare articles for my blog, my weekly site-members’ newsletter and so on, it’s Drafts I turn to. I think of it as the BBEdit for my iPad.

With the recent version 20.0, Drafts introduced cross-linking. You can use wiki-style links to open other documents (also called drafts, by the way) within the app; for example, a link like this: [[Some Other File]] can be tapped or clicked to open the draft entitled “Some Other File” (creating it if necessary). Needless to say, this is very useful for creating a repository of knowledge, a body of research, a plain-text household database, and so on.

Drafts is also very scriptable and customisable, so I decided to extend this functionality to fit how I work. First and foremost, I don’t like to take my hands off the keyboard, so I needed a way to navigate cross-links without needing to use a pointing device. I created an action for Drafts to do that, to which you can assign whatever keyboard shortcut you prefer. It works on a link that’s either part of the text selection in the frontmost draft, or is immediately adjacent to the selection or the insertion point. There are various other options you can tweak for yourself, as detailed below.

Please note that this action requires Drafts version 20.1 (not 20.0). Ensure you’ve got Drafts installed and that it’s up to date, and then you can download the action from the Drafts actions repository here. I think you might also need the Pro (subscription) version to use third-party actions, but I’m not sure.

How it works

The default functionality is as follows:

  • When invoked while you’re editing a draft, the action will try to find a [[Cross-Link Like This]] adjacent to the text insertion-point, or overlapping the text selection.

  • If it finds such a link, it will try to identify an existing draft with the specified title. If it finds (exactly) one, it’ll open it for you.

  • If it finds more than one draft with a matching title (partial matching is allowed, and it’s case-insensitive), it’ll let you choose which one to open.

  • If no corresponding draft is found, it’ll create a new one for you with the given title, and open it for editing.

You can also customise the action’s behaviour in various ways. To do so, edit its script within Drafts, and you’ll see several variables at the top of script, along with explanatory text. Here’s a description of each variable and what it does. The default values are shown in parentheses.

Configuration options

chooseBestMatch (false)

When more than one draft could be the destination of a link, this setting determines what happens. If true, the best match (according to Drafts’ own internal logic — usually the most recently modified one with a matching title) will be opened without user intervention. If false, the user will be shown a list of matching drafts to choose from.

ensureInitialCapitals (true)

If true, newly-created drafts will have their title’s first letter made uppercase if necessary.

askBeforeCreatingNewDrafts (false)

If true, when a link’s target document isn’t found, the user will be asked before a new draft with the link’s title is created. If false, new drafts will be created implicitly.

Template support

By default, any drafts which are created as the result of this action have a simple format: just the title of the link that produced them as a level-1 Markdown header, with a couple of blank lines afterwards. You might want something a bit more flexible; I certainly did. The action has support for Drafts’ own lightweight template system, with some custom additions of my own. There are some additional variables you can customise to control this, as follows.

newDraftTemplateName (“”)

This is the title of an existing draft which will be used as a template when creating new ones. If the template is not found, a blank draft will be created with the appropriate title as usual, and you can also use an empty string (“”) to disable the template functionality if you don’t need it. I’ve added some useful details:

  • The new draft’s title — i.e. the copied template’s first non-blank line — will be replaced appropriately.

  • Any leading hash/pound symbols (i.e. “#”) will be preserved when setting the new title. This lets you have Markdown headings the way you like them.

  • Drafts template tags in the template will be processed. This is very useful for date-stamping the new drafts, including external files, and so on.

  • I added some custom template tags to help those who want to manage an interconnected corpus of documents, for example a Zettelkasten system:

    • source_display_title is the display title of the source draft which created the new draft.

    • source_uuid is the UUID of the source draft which created the new draft.

    • source_title_link is a preformatted [[backlink]] to the source draft’s display title.

    • source_uuid_link is a preformatted [[u:backlink]] to the source draft’s UUID.

As an example, here’s a possible template you could use:

# Cross-Linked Draft Template

Created on: [[date|%Y-%m-%d %H:%M:%S]]  
Linked from: [[source_title_link]]

When a cross-link such as [[My New Document]] was triggered via the action with the above template configured, and the source document containing that link was entitled “My Old Document”, the following new draft would be created:

# My New Document

Created on: 2020-05-26 17:49:14
Linked from: [[My Old Document]]

Lastly, as a convenience, you can also set the name of the template in the action’s Define Template Tag step (before the script itself), which is visible when you edit the action in Drafts. This is easier than having to change the actual script whenever you want to choose a new template to use.

inheritTagsFromTemplate (true)

Drafts allows you to apply tags to drafts as metadata. If this variable is true, any tags which are applied to the template will also be applied to each new draft created from it. As a convenience, the tag “template” (if present) will not be inherited from the template.

excludedTags ([“template”])

The tags present in this array will not be inherited by new drafts created from the template. Has no effect if inheritTagsFromTemplate (above) is false. By default, only the tag “template” is excluded from being inherited.

prependToNewTitles (“”)

A string to prepend to the titles of new pages (but after any leading Markdown headers and whitespace from the template’s own title, as discussed previously). This string will be processed for Drafts template tags first, in the contetx of the new draft. A useful example for Zettelkasten-style unique identifiers would be: “[[date|%Y%m%d%H%M%S]] ”.

Closing thoughts

That’s about it. If you’re keen on using Drafts’ cross-linking functionality, you should also take a look at this action group from the app’s makers. You might also like:

Once again, to use my own action, make sure you’ve got Drafts 20.1 or later installed, then you can download the action from the Drafts actions repository here.