Exporting Markdown from OmniOutliner

Both for my day job as a researcher, as well as for this website, I often prepare papers and articles by first writing an outline. In an outline, it is easy to structure articles, re-arrange sections or subsections.

One of my favorite apps in the past has been OmniOutliner. It can export documents to text, XML, word, and others. Unfortunately, it is missing an exporter to Markdown. Luckily, there is OmniJS, the automation scripting language developed by OmniGroup.

In the following, I will showcase one way to use OmniJS, by creating a Markdown-like document from an outline, and exporting it to another app. The scripts works on both iOS and macOS.

Scripting languages on Apple platforms

Scripting to automate workflows has its own history, especially on Apple platforms. On macOS, AppleScript has been available for a long time, allowing for cross-application scripting in a somewhat JavaScript-like manner. It allows to access application APIs as well as some OS APIs, which makes automation on the macOS platform very customizable.

Unfortunately, a similar way of scripting has not yet commonly found its way into the mobile platform of iOS. On iOS, there is the Shortcuts app which allows some scripting capabilities. Unfortunately, it has it’s own limitations, being an app on its own.

OmniJS as a scripting spin-off by OmniGroup

OmniJS is a new scripting language used by OmniGroup to introduce more advanced scripting functionality to their applications. So far, OmniOutliner and OmniGraffle are supported.

While OmniJS is solely suppoted by OmniGroup applications, it comes with full API access and can thus be much more powerful than workflows created in Shortcuts. Additionally, OmniJS scripts can be called from Shortcuts, or return values back to it, so it is possible to create cross-application workflows including the OmniJS API.

OmniJS can also call URLs, including x-callback-urls which can be used to hand data to other applications.

For this API, I use the OmniJS API to create an OmniOutliner action. It can be run directly from inside the application and does not need Shortcuts. It runs from itself and can export data to another app.

A tutorial series from the developers is found on this website. There is also an API documentation available from within the apps. In OmniOutliner, there is a button in the top right for the automation menu, which includes a developer console as well as a link to an API documentation.

The API is only available in OmniOutliner Pro, but not in the cheaper Essentials edition.

The script

The script will take the currently open outline and create a Markdown-like heading for each title. The resulting Markdown document is exported to the application Drafts 5, which seems to be a popular choice for further post-processing. Below, there is a second version which exports to Ulysses instead.

Back to the idea. Exporting the outline as a markdown-like document. For example, the root note will become “# Document title”, its indented children nodes will become “## Something else”, and so on.

Additionally, any notes attached to each object will be processed and added below its heading. For example, a heading called “Related work”, with the note  “Remember to cite [Xyz]” attached, will become:

## Related work

Remember to cite [Xyz]

To keep the script simple and reduce its complexity, it will ignore any other columns and fields which may exist in your OmniOutliner document. It will also ignore styling made to the title or note.

For transparency purposes, this is the full script. If you do not understand JavaScript, just skip ahead, as I put a download link below.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*{
 "type": "action",
 "targets": ["omnioutliner"],
 "author": "Marc A. Kastner",
 "description": "Create a markdown compatible format from the current document and export it to Drafts 5.",
 "label": "Drafts - New Draft with Markdown format",
 "paletteLabel": "Drafts Doc"
}*/

var _ = function() {
  var action = new PlugIn.Action(function(selection, sender){
  var topics = new Array()
  rootItem.descendants.forEach(function(item){
    level = item.level
    itemString = '#'.repeat(level) + " " + item.topic
    topics.push(itemString)
    
    noteString = '\n'
    if(item.note) {
        noteString = noteString + item.note + '\n'
    }
    topics.push(noteString)
  })
  topics.join('\n')
  encodedStr = encodeURIComponent(topics.join("\n"))
  urlStr = "drafts5://x-callback-url/create?text=" + encodedStr
  url = URL.fromString(urlStr)
  url.call(function(result){console.log(result)})
  });
  
  action.validate = function(selection, sender){
    if(rootItem.descendants.length > 0){return true} else {return false}
  }
  
  return action
}();
_;

The script is available to download here.

Installation and usage

On iOS, click the link. Then, click on the “More…” button below. Lastly, select “Copy to OmniOutliner”. You may need to scroll to the right to actually find the entry “Copy to OmniOutliner”. This will copy the script into the app and install it accordingly.

On macOS, download and unpack the ZIP archive. In OmniOutliner, choose “Plug-Ins…” from the automation menu. An empty finder window will open. Place the “drafts.omnijs” from the ZIP archive into this folder.

After installation, the action will be available in the automation dropdown on the top right (third buttom from the right.)

Example output

Here is a short example run in iOS. The outline on the left will result in the Drafts 5 document on the right.

Advanced: Modifying the script

If you want the Markdown document in another app, you can modify the X-Callback-URL.

In the bottom third, there is a file which looks like this:

1
urlStr = "drafts5://x-callback-url/create?text=" + encodedStr

You can change it to another app as wanted. X-Callback-URLs are widely supported for various iOS apps like editors, note apps, or archival apps. Beware, that each app has their individual syntax, so just changing the name in front of “://” will likely not work. Consult the documentation of each individual application for more insights on that.

Example: Ulysses

For example, to export to the markdown editor Ulysses, you can change the line to:

1
urlStr = "ulysses://x-callback-url/new-sheet?text=" + encodedStr

This will create a new sheet in Ulysses based on your outline; already preset with the headings and notes as outlined in OmniOutliner.

As Ulysses is another popular app which might be used by many, I prepared a ZIP archive with the Ulysses version of the script. You can download it here. It can be installed as explained above for the Drafts version.

Support

Any questions? If you need any help, please feel free to ask in the comments! Otherwise, share your own scripts or ideas on how to use OmniJS!


PS: If you found this content valuable and want to return the favor, you can support me by buying me a coffee.


See Also

Creating OmniFocus projects from templates and calendar events

Today I want to showcase one of my most favorite automation workflows I am regularly using on my iPad.

When preparing …

Evernote web clipping plus todo lists as a reliable read-later tool

There are often things you read in web, which are actually interesting, but just come in the wrong moment. Directly …

An overview on automation

When using a multiple of productivity apps, the number of inboxes can get overwhelming. An email from a supervisor and a …


About the author

Marc A. Kastner
Marc A. Kastner

Marc is the founder and editor-in-chief on Productived.net. He is computer science researcher and PhD student in Nagoya, Japan. Always interested in improving his own workflows, he is on the journey to discover new productivity utilities. On Productived.net, he writes articles on productivity and digital workflows.

Twitter: @productivednet | E-mail: marc@productived.net

Comments