The Outline

In OmniFocus for macOS and OmniFocus 4.x for iOS/iPadOS, the materials displayed in the “Content View” are organized as an outline.

An outline is a hierarchical structure comprised of ordered nodes, each of which may contain other nodes, each of which may contain other nodes, and so on.

A node is an organizational wrapper for an instance of the OmniFocus DatedObject class: folder, project, tag, or task.

In essence, outlines are used to display ordered content in a relational manner. The following documentation details how to access the displayed elements of the content outline and control the manner in which they are displayed.

The Two Trees

The Window class has two properties whose values are hierarchical collections of ordered nodes, content and sidebar.

Instance Properties of DocumentWindow Class (subset)

Focus Specified Project


project = flattenedProjects.byName("My To Do List") if(project){ window = document.windows[0] window.perspective = Perspective.BuiltIn.Projects window.focus = [project] }

Instance Functions of DocumentWindow Class (subset)

The Tree Class (outline)

The value of the content and sidebar properties of the DocumentWindow class are instances of the Tree class. The following details the instance properties and functions of this class:

Instance Properties

Instance Functions

Reveal and Select Specified Inbox Task


window = document.windows[0] window.perspective = Perspective.BuiltIn.Inbox var targetTask = null inbox.apply(task => { if(task.name === "My Special Task"){ targetTask = task return ApplyResult.Stop } }) if(targetTask){ tree = window.content node = tree.nodeForObject(targetTask) tree.reveal([node]) tree.select([node]) }

The TreeNode Class (node)

The TreeNode class represents the physical display of the individual database objects as nodes in the outline tree.

TreeNode Instance Properties

TreeNode Instance Functions

omnifocus://localhost/omnijs-run?script=tree%20%3D%20document%2Ewindows%5B0%5D%2Econtent%0Atree%2ErootNode%2Echildren%2EforEach%28node%20%3D%3E%20node%2Eexpand%28true%29%29
Expand Displayed Nodes
 

tree = document.windows[0].content tree.rootNode.children.forEach(node => node.expand(true))
omnifocus://localhost/omnijs-run?script=tree%20%3D%20document%2Ewindows%5B0%5D%2Econtent%0Atree%2ErootNode%2Echildren%2EforEach%28node%20%3D%3E%20node%2Ecollapse%28true%29%29
Collapse Displayed Nodes
 

tree = document.windows[0].content tree.rootNode.children.forEach(node => node.collapse(true))

The control of the display of notes is independant of the expansion or contraction of the node display:

omnifocus://localhost/omnijs-run?script=tree%20%3D%20document%2Ewindows%5B0%5D%2Econtent%0Atree%2ErootNode%2Echildren%2EforEach%28node%20%3D%3E%20node%2EexpandNote%28true%29%29
Expand All Notes
 

tree = document.windows[0].content tree.rootNode.children.forEach(node => node.expandNote(true))
omnifocus://localhost/omnijs-run?script=tree%20%3D%20document%2Ewindows%5B0%5D%2Econtent%0Atree%2ErootNode%2Echildren%2EforEach%28node%20%3D%3E%20node%2EcollapseNote%28true%29%29
Collapse All Notes
 

tree = document.windows[0].content tree.rootNode.children.forEach(node => node.collapseNote(true))

Examples

Since the content display in the iPadOS/iOS version of OmniFocus has been redesigned using Swift UI, it now exposes the underlying “tree” structure of the content outline to automation.

For example, the following script will select all tasks in the current content view.

Note:

omnifocus://localhost/omnijs-run?script=tree%20%3D%20document%2Ewindows%5B0%5D%2Econtent%0Atree%2Eselect%28%5B%5D%29%0Atree%2ErootNode%2Eapply%28node%20%3D%3E%20%7B%0A%09if%28node%2Eobject%20instanceof%20Task%29%7Btree%2Eselect%28%5Bnode%5D%2C%20true%29%7D%0A%7D%29
Select All Tasks
 

tree = document.windows[0].content tree.select([]) tree.rootNode.apply(node => { if(node.object instanceof Task){tree.select([node], true)} })

Here's a variation of the previous script concept that selects only tasks whose title begins with the string "2nd-":

Select Tasks Whose Name Begins With…


tree = document.windows[0].content tree.select([]) tree.rootNode.apply(node => { item = node.object if(item instanceof Task){ if(item.name.startsWith("2nd-")){tree.select([node],true)} } })

Here's how to focus all projects in the content view whose title begins with "Get":

Focus Projects Whose Name Begins With…


document.windows[0].focus = [] tree = document.windows[0].content items = [] tree.rootNode.apply(node => { item = node.object if(item instanceof Project){ if(item.name.startsWith("Get ")){items.push(item)} } }) if (items.length > 0){document.windows[0].focus = items}

How to copy the selected nodes to the clipboard:

omnifocus://localhost/omnijs-run?script=tree%20%3D%20document%2Ewindows%5B0%5D%2Econtent%0Atree%2EcopyNodes%28tree%2EselectedNodes%2C%20Pasteboard%2Egeneral%29
Copy Selected Nodes to Clipboard
 

tree = document.windows[0].content tree.copyNodes(tree.selectedNodes, Pasteboard.general)

Select all sidebar items. Works with all perspectives that have a corresponding sidebar:

omnifocus://localhost/omnijs-run?script=window%20%3D%20document%2Ewindows%5B0%5D%0Atree%20%3D%20window%2Esidebar%0Atree%2Eselect%28%5B%5D%29%0Atree%2ErootNode%2Eapply%28item%20%3D%3E%20%7B%0A%09if%28%21item%2EisRootNode%29%7B%0A%09%09tree%2Eselect%28%5Bitem%5D%2C%20true%29%0A%09%7D%0A%7D%29
Select All Sidebar Items
 

window = document.windows[0] tree = window.sidebar tree.select([]) tree.rootNode.apply(item => { if(!item.isRootNode){ tree.select([item], true) } })

Create Markdown from Contents

This script will create markdown for the current window contents, and place the created content on the clipboard:

omnifocus://localhost/omnijs-run?script=tree%20%3D%20document%2Ewindows%5B0%5D%2Econtent%0Amarkdown%20%3D%20%22%22%0Acounter%20%3D%200%0Atree%2ErootNode%2Eapply%28node%20%3D%3E%20%7B%0A%09if%28node%2EisRootNode%20%3D%3D%3D%20false%29%7B%0A%09%09item%20%3D%20node%2Eobject%0A%09%09level%20%3D%20node%2Elevel%0A%09%09name%20%3D%20item%2Ename%0A%09%09type%20%3D%20item%2Econstructor%2Ename%0A%09%09if%28type%20%3D%3D%3D%20%22Project%22%29%7B%0A%09%09%09prefix%20%3D%20%22%22%20%0A%09%09%7D%20else%20%7B%0A%09%09%09prefix%20%3D%20%22%3E%22%2Erepeat%28level%20%2D%201%29%0A%09%09%7D%0A%09%09entry%20%3D%20%60%24%7Bprefix%7D%28%24%7Btype%7D%29%20%2D%20%24%7Bname%7D%60%0A%09%09if%28counter%20%3D%3D%3D%200%29%7Bmarkdown%20%2B%3D%20entry%7D%20else%20%7Bmarkdown%20%2B%3D%20%60%5Cn%5Cn%24%7Bentry%7D%60%7D%0A%09%09counter%20%3D%20counter%20%2B%201%0A%09%7D%0A%7D%29%0APasteboard%2Egeneral%2Estring%20%3D%20markdown
Markdown from Content
 

tree = document.windows[0].content markdown = "" counter = 0 tree.rootNode.apply(node => { if(node.isRootNode === false){ item = node.object level = node.level name = item.name type = item.constructor.name if(type === "Project"){ prefix = "" } else { prefix = ">".repeat(level - 1) } entry = `${prefix}(${type}) - ${name}` if(counter === 0){markdown += entry} else {markdown += `\n\n${entry}`} counter = counter + 1 } }) Pasteboard.general.string = markdown
 

And the script as a plug-in:

Markdown of Contents to Clipboard (@)
   

/*{ "type": "action", "targets": ["omnifocus"], "author": "Otto Automator", "identifier": "com.omni-automation.of.markdown-of-contents-to-clipboard", "version": "1.0", "description": "Places markdown of the contents of the window on the clipboard.", "label": "Markdown of Contents", "shortLabel": "Markdown of Contents", "image": "paragraphsign" }*/ (() => { const action = new PlugIn.Action(function(selection, sender){ tree = document.windows[0].content markdown = "" counter = 0 tree.rootNode.apply(node => { if(node.isRootNode === false){ item = node.object level = node.level name = item.name type = item.constructor.name if(type === "Project"){ prefix = "" } else { prefix = ">".repeat(level - 1) } entry = `${prefix}(${type}) - ${name}` if(counter === 0){markdown += entry} else {markdown += `\n\n${entry}`} counter = counter + 1 } }) Pasteboard.general.string = markdown new Alert("COMPLETED", "Markdown is on clipboard.").show() }); action.validate = function(selection, sender){ return (document.windows[0].content.rootNode.children.length > 0) }; return action; })();

Make the Selection Visible (iOS)

To make the selected items visible in the content view, use the scrollSelectionToVisible() function:

Make Selection Visible (iOS)


document.windows[0].content.scrollSelectionToVisible()