Drafts: Script Development

The Drafts application is an amazing tool for capturing and working with text. One of its greatest strengths comes from its extensibility through its actions system. And certainly one of the most powerful action steps is the Script action step which allows you to program actions in JavaScript utilising much of the core iOS JavaScript engine and interfaces into the Drafts app itself.

I have a few things that I do to make it easier for me to work with JavaScript in Drafts, and I’m going to share some of them in this post.

The ThoughtAsylum Action Group and TADpoLe

Before I get into the details, the first thing I need to do is to mention a set of tooling that I use. This is the ThoughtAsylum Action Group, which in turn utilises the Thought Asylum Drafts Library (TADpoLe).

You can get download links and find out more about these on the TADpoLe page on this site. But they are core to my workflows, and I would advise you grab a copy of the action group. Everything that follows is based on utilising this and explaining how the development tools work.

TAD

To explain everything properly I need to start at the base-level of the ThoughtAsylum Action Group and with a key action in that group called TAD.

TAD is an action that is included, via an Include Action step in most of the actions in the group. It is what links in the TADpoLe library, a set of JavaScript functionality, to actions that use them. But this is more than simply a collection of code or an inclusion. It is actually a switcher that allows you to change the source of the library.

The action consists of four steps.

  1. Script
  2. Define Template Tag
  3. Script
  4. Include Action

The first action step is actually just a script comment providing some instructions for the action.

// INSTRUCTIONS
//==============

// Set the TAD-MODE template tag to one of the following modes:

// - ACTION = load the library information from an action copy.
// - FILE = load the library from a file copy.
// - DEV = load the library from a list of separate files.

The setting of the TAD-MODE template tag occurs in step two. As you can see from the comment above, it can be set to ACTION, FILE or DEV. By default, this is set to FILE.

The third step is where the action pulls in the JavaScript code, and where it pulls in the code from is based on the value of TAD-MODE.

The ACTION mode links into the fourth step, Include Action. The fourth step includes the action named by the TA-LIB-ACT template tag. If the mode is ACTION, step 3 sets this to TAD-LIBRARY. If it is not set to ACTION, it will be set to TAD-Null Action; a bona fide action that is configured to do absolutely nothing!

The FILE and DEV actions both pull in the JavaScript from files, and this is actively accomplished in step 3. If set to FILE, the step will require the “tad.js” library file (located in the Drafts Library/Scripts/ folder). If set to DEV, it will attempt to load the component files that make up the library when it is built.

The DEV setting is probably only of direct use to me, but if you are doing any similar development, this structure may be useful allowing you to switch between various set-ups and versions.

The TAD-LIBRARY action contains a copy of tad.js and may be useful should anyone experience performance issues reading from file (which shouldn’t happen but I did have some issues like that at one point during development). It is also used by the TAD-Setup/Refresh action to retrieve the latest version of the “tad.js” file; but that’s also exactly why most people should use the FILE option. It is easy to have an action update the underlying library file, but updating the Action Group requires more effort from the end user.

Hopefully, you now have some insight into some of the structure and incorporation of the TADpoLe library into actions. We’re going to build on this in the following sections.

Test Libraries

I’ve also incorporated a way to load in additional library files by using an index file and a function. I find this approach particularly useful when I’m at my Mac and I can develop the JavaScript code in a dedicated code editor.

Now, this isn’t anything revolutionary. This is simply an extension of the inbuilt require function. The TA_libraryTestLibLoad function from the TadLibrary class.

The TAD-Execute Test Function shows this being utilised. After including the TAD action, the second (script) step executes the following code.

//Only need to load the test libraries if they haven't been loaded already.
if (!tadLib.testLibsEnable) tadLib.TA_libraryTestLibLoad();

//The standard function we'll look to run is this one:
TA_test();

It first of all checks that the list of test libraries from the index file have not already been loaded and then loads them if they have not. After that it runs a function called TA_test(); this is actually just the name of a function I use as a trigger function for my latest testing. I write code into that function, then when I trigger the TAD-Execute Test Function, it always executes the code I’ve been working on.

The index file being used is specified in the TADpoLe library settings as testLibsListPath, and defaults to the Drafts iCloud path /Library/Scripts/tad-testlibs.txt. Add your library names to the file (including the .js file extension) with one file per line, and you’re good to go.

Executing from Draft

It isn’t quite as convenient to edit JavaScript library files on my iPad, and certainly not on my iPhone. In those instances, I tend to fall back to writing my JavaScript code in the body of a Draft. As well as writing the code there, I also have some actions to let me work with the code.

Specifying the Executable Draft

While I have an entire workspace and set of drafts set aside for scripting, I have just one executable draft at any one time, which then ultimately enables me to run the code in that draft on another draft with a single click or keyboard shortcut.

As a first step there is an action called TAD-Make Draft Executable that takes a draft and sets it up to be the executable draft.

The way I chose to do this was primarily using a specific template tag. I originally began by storing and referencing the UUID of the draft, but I this meant I couldn’t tell at a glance (e.g. in a list of drafts, or in the on screen details of the draft) which was the currently executable draft. For me, a visual element was useful, and so I switched to using a template tag instead.

The TAD-Make Draft Executable action uses the ta_devsetrundraft() function of the TadMiscellaenous class. This uses a set of settings in the TadLibrary class to set-up the current draft, and update other drafts that may be a match to no longer match.

1. runTag:

  • This is the tag that is assigned to a draft to make it executable.
  • Only one draft at a time should ever be assigned this tag.
  • The default library setting is #run#

2. runArea:

  • This is the Drafts folder that the function queries to find drafts.
  • The default library setting is inbox, but you may wish to set it to all if you want to search the archive as well.

3. runSyntax:

  • This is the syntax type for the draft. Since we’re coding in JavaScript, typically that’s what you would set this to so that you get all of the syntax highlighting benefits.
  • However, once Drafts introduces custom syntaxes (due later in 2020), this may be something that is worth modifying to your own preferred JavaScript syntax preference.
  • The default library setting is JavaScript.

4. runTagScript:

  • This is an additional tag that I use to help keep all of my Drafts scripting tags in a workspace. When I set a draft as executable, this will then ensure that it is associated with the appropriate workspace.
  • If you have a scripting workspace, you should set this to match your workspace tag search. If you don’t have one, now is a great time to create one.
  • The default library setting is javascript.

When the TAD-Make Draft Executable action is run, the runTag is removed from all other matching drafts in the runArea folder, and all of the above tags and syntax are applied to the current draft.

Loading the Executable Draft

As a convenience, there is also a TAD-Load Executable Draft that will load the executable draft into the editor. It will report an error if there are no matches, and it will also pick the last modified draft should it happen to find multiple matches.

Executing the Draft

The TAD-Execute Draft works in a similar way to the TAD-Load Executable Draft action in the way it determines which draft to execute., But the premise is very simple. It loads the content of the executable draft and evaluates it using a JavaScript eval() function.

Now there are obviously security issues around evaluating arbitrary code - so don’t evaluate arbitrary code. Only run the execution on drafts containing code that you know to be safe - much as you would executing code from a library/file.

Again, I have an action that calls this one set-up on another action group that allows me to execute the draft with one click/tap, or a keyboard shortcut. Do also remember that when you are executing, the executable draft does not need to be loaded into the editor.

Beautiful Code

Code formatting is something that can cause endless debate. Be it from tabs vs. spaces to layouts of parentheses and use of different string delimiters. I have my way of doing things, and that has served me well in the years I spent as a professional developer and since in my various other IT roles. But one thing that everyone agrees on is that formatted code is much easier to read than unformatted code.

To that end I added in an action that makes use of a version of the JS Beautifier library. The TAD-Beautify JavaScript action will take the code in the active JavaScript draft and format it as per the defined settings.

But, as I said, code formatting is typically a personal preference, so for those of you who do want to tailor the settings, there are details provided in the documentation for the TA_devBeautifyJSDraft() function. You can specify additional settings in a JSON file (default is beautifier.json placed in the Library/Scripts/ directory), that when present are passed to the beautification function.

Summary

Hopefully, that gives you a bit of insight as to how I script for Drafts, and provides you through the ThoughtAsylum Action Group and the TADpoLe library, with some tooling to help you expand your own scripting with Drafts workflow.

Author: Stephen Millard
Tags: | drafts | tadpole |

Buy me a coffeeBuy me a coffee



Related posts that you may also like to read