August 23, 2020

RoamPageSearch

1. Summary

Quickly jump to and interact with your Roam Research pages from anywhere on your Mac, using Alfred.

Download from packal Don’t forget to read the setup instructions below! Please make sure you are using the latest version of Alfred.

Currently supports:

  1. Go to daily page by hotkey/keyword
  2. Search and jump to page by Alfred filter
  3. Go to named page by hotkey/keyword
  4. Jump to named tag/block under daily page, by hotkey/keyword. Creates block if it does not exist
  5. Jump to tag/block under daily page, using dynamic filter based on a configuration page in Roam . Creates block if it does not exist. Supports multi level menu configuration.
  6. For 4/5 above: optionally paste clipboard under block on daily page after jumping to it
  7. For 4/5 above: optionally add timestamp under block on daily page after jumping to it
  8. For 4/5 above: optionally make TODO under block on daily page after jumping to it
  9. Any combinations of 6,7,8 above
  10. Display and jump to list of favorite pages, based on a configuration page in Roam.
  11. Navigate through pages in search using ⌘ and ⌥ to find outbound and inbound links
  12. Hit ⌘C on page name to copy bracketed page name for pasting
  13. Paste contents of any page into front application, using 2, 3, 10, 11 above (hold ⇧). Means you can create template pages and quickly access using Alfred.

Example use cases:

  • Grab a screenshot based on a hotkey, have Alfred prompt for work” or home” tags, paste screenshot under that tag on daily page
  • Grab selected text based on hotkey, paste under todo” tag on daily page with timestamp and checkbox
  • Use a hotkey to show a list of your favorite pages. Use another hotkey to show a list of your least favorite pages. :)
  • Create template pages and paste their contents into other pages or apps. Create menus of templates, bring them up with a hot key. Bind templates to a hot key, keyword.

2 Release Notes

  • 23 Aug 2020: V2.0 released.
    • Added dynamic menus, paste content, timestamps, todos
  • 25 Aug 2020: V3.0 released.
    • Added ability to navigate through page inbound and outbound links in Alfred using ⌘ and ⌥, as well as ability to copy current page reference using ⌘-C
  • 26 Aug 2020: V 3.1 released.
    • Added better subtitle text.
    • Fixed chrome jumping to foreground when run.
  • 27 Aug 2020: V 3.3 released.
    • Fixed bug that stopped links to favorites (loaded from config) from showing. Now you can have a favorite tags section on your config page, target it with a hotkey, and hold ⌘ to show all pages tagged with selected tag.
  • 28 Aug 2020: V4.0 released.
    • Added option to paste contents of currently visible page into front app (thanks Viktor!), giving easy fast templates accessed via dynamic menus.
    • Fixed two nasty bugs: wrong chrome tab was targeted, and all page caching did not behave as expected.
  • 15 Sep 2020: V 5.0 released.
    • Greatly improved search: oam” will now match Roam” [[Roam]]” Foam” etc, as well as now supporting Chinese, Japanese character search. Thanks to aitsc for the PR.
    • Removed caching of all page results. Too much trouble to support for very small response time gains.
    • Added many keyword examples
  • 16 Sep 2020: V 5.1 released.
    • Fixed bug on showing linked pages

3 Setting up

3.1 Permissions

For this workflow to work, you need to enable AppleScript->Javascript in your browser of choice.

To enable AppleScript->javascript in Chrome/Brave:

View>Developer>Allow Javascript from Apple Events

To enable AppleScript->javascript in Vivaldi: Settings->Privacy->Allow Javascript from Apple Events

To enable AppleScript->javascript in Safari:

Safari Preference>Advanced>Show Developer Menu in menu bar Then Develop>Allow Javascript from Apple Events

3.2 Environment Variables

The workflow has several environment variables. These can be found by clicking the [x] in Alfred, in the top left of the workflow page.

DBName: the name of your roam db. This can be found at the end of the url of your roam pages: https://roamresearch.com/#/app/YOURDBNAME . This MUST be set before running.

configPageName: the name of a page in your database where you can build dyamic Alfred menus for favorite pages and tags. See below. Defaults to RoamSearchConfig”- I recommend you create this page as per the Sample”RoamResearchConfig” page” section below to experiment.

keydelay: the script uses keyboard automation to add items to Alfred, as there is no data entry API as yet. This is error prone, so the script introduces delays between each step. This value represents the time in seconds between key presses. Depending on you computer, you may get strange behavior. If this happens increase this value to 0.4, 0.5 etc. Please let me know if you experience issues let me know here and I will increase the default.

Note: if your computer is set to a locale where decimals are represented as 0,3” rather than 0.3”, you will need to change this value to use 0,3”

preferredBrowser: must be either Chrome”,“Safari”,“Vivaldi”, or Brave”. Local webapp support does not seem possible. Firefox might be, but needs a lot of coding (PRs welcome :)

3.3 Create RoamResearchConfig” page

If you want to use dynamic configuration (and you should!), paste the following onto a page called RoamResearchConfig” in your Roam to run the example workflows.

<snip>

  • Favorite Pages
    • [[RoamSearchConfig]]
    • [[Roam]]
    • [[📽Projects]]
    • [[[[Roam]] [[Alfred]] script]]
  • Screenshot Categories
    • [[Work]]
      • [[Project 1]]
      • [[Project 2]]
    • [[Home]]
      • [[Tech]]
      • [[Fun]]
  • Quick Capture Categories
    • #thoughts
    • [[work]]
    • [[webclips]]
      • [[Tech Log]]
      • [[Articles]]

</snip>

4 General Usage Notes

Requires a roam tab to be open pointing to your database before it will work (script will open one on first run if not already open).

When run, the workflow will query roam dynamically. When you select an action that jumps to a page, one of two things will happen: 1) If your preferred browser has a roam tab open and active, that tab will go to that page 2) If you do not have an active roam tab open, the first roam tab you have open will be navigated to that page. The idea here is that if you are working in a tab, you can use the hotkeys and stay in that tab.

The workflow is configured with what I consider to be sensible defaults, and can be used simply by configuring hotkeys or keywords. However, all sections of the workflow that contain yellow boxes are examples and should be copied to your own workflow- these will invoke the main workflow using external triggers. This a) gives you flexibility to relate multiple hotkeys for the same use case, and b) gives you some level of protection against workflow updates breaking any custom hotkeys you create (though no promises :)

When targeting blocks under daily pages: if the named bloc does not exist, it will be created (at the top of the daily page). If multiple copies of the named bloc exist, the oldest will be targeted.

5 Example Use Cases

In general there are three flavors of usage:

  1. Jump directly to a page. You can map hotkeys or keywords to individual pages within your database. Daily page is a special case.

  2. Search graph by page title, and navigate through the graph from within Alfred. Navigation is restricted to either showing outbound page links within a page, and page that have links back to the current page. Selecting a page will jump to it, ⌘ and ⌥ will swap between inbound and outbound links, and ⇧will paste contents of target page into the current app.

Additionally, you can set up hotkeys/keywords to take you into the navigation at any point: eg show me pages that contain a link to [[Favorites]]”

  1. Add content to daily notes page. These use cases allow adding content under tags on daily notes. There are variations to add timestamp, paste current clipboard content, and make into a todo. These can be combined.

These tags can be targeted individually by hotkey or keyword. Alternatively you can set up a page in roam containing lists of tags to target. These lists can contain a nested hierarchy that you can navigate in Alfred.

More details follow below.

5.1 Jump to daily page

The simplest feature. When invoked by hotkey or keyword, the workflow will find the first roam tab in the preferred browser, and jump to the daily page in that tab.

If you are already working in a roam tab in that browser, the script will target that tab. You can set this hotkey to ⇧^D, which is the same as Roam’s go to daily page” key. This means it is the same key whether you are working in Roam or outside of it.

5.2 Search and jump to page by filter

When invoked by hotkey or keyword, the workflow will find the first tab pointing to Roam in the preferred browser, and use javascript to query the roamAlphaApi to get the titles of all pages. These pages will then be available for filtering in Alfred as you type.

Selecting a page will jump Roam to that page.

Holding down ⌘ and hitting enter will show pages that link to the current entry.

Holding down ⌥ and hitting enter will show outbound links to other pages from the current page.

Hitting ⌘-C will copy the current page name with braces around it. If your use case requires something else (eg markdown links) please let me know (see bottom of page).

5.3 Go to named page by hotkey/keyword

See GO TO NAMED PAGE EXAMPLE When invoked by a hotkey or keyword, the workflow will jump directly to this page. This is useful if you have pages that you jump to a log- you can assign them hotkeys.

Set an arg which is the name of the page you want to jump to. As above, any section containing yellow blocks should ideally be moved to your own workflow.

5.4 Add Web Bookmark Example

See BOOKMARK EXAMPLE This example runs an applescript that grabs the title and url of the front browser window, dumps it in clipboard as a markdown link, then adds it to the daily page under the heading [[webclips]].

A couple of notes about this: The applescript is scrounged from the web, and I’m unlikely to support this going forward. YMMV- it probably doesn’t have widespread browser support. I strongly recommend looking into Hook (see below) for this use case. This example shows the argument (the tag to target on daily notes) and vars (paste=true) being set. Setting paste means when we arrive at the target block on daily page, we paste the payload.

5.5 Add TODO example

See TODO EXAMPLE This is the largely the same as the example above - it grabs selected text and sends to a [[todo]] block on daily notes. It shows the addition of extra variables addtimestamp=true” and todo=true”. These add timestamp and todo markers to the target block.

5.6 Hook Example

See HOOK EXAMPLE I use this rather than the bookmark example above. This uses the versatlie Mac utility Hook to grab the title and url of the current window as a markdown link, and sends it to a tag called [[webclips]] on the daily page.

However what is neat about this is that Hook supports a LOT of apps, and this shortcut (or variations of) will work in all of them. You can use it for local files, emails, things tasks, etc. See the full list of apps hook supports here.

5.7 Grab Screenshot, with Prompt Example

See GRAB SCREENSHOT EXAMPLE Now we get fancy. The first step in the script grabs a screenshot into the copy buffer for pasting into Roam. However instead of jumping straight to today’s daily page and pasting, it will prompt you where to put it, based on a dynamic menu generated from the page (by default) called RoamSearchConfig”.

On my RoamSearchConfig” page, I have the following:

I pass Screenshot Categories” as the arg, and the config page is searched for a matching block, with the children being displayed as options:

Alfred then prompts me as follows:

If I choose Work” or Home” here the screenshot will be pasted under the Work” or Home” tag on my daily page. However if I hold down ⌥ and choose Work”, Alfred will drop down a bullet level:

Choosing one of these jumps to or creates the appropriate bullet on Daily Pages. Note that you can pass todo=true” addtimestamp=true” etc to customize the behaviour.

5.8 Jump to daily page tag example

See JUMP TO DAILY TAG EXAMPLE

This is a simplified version of the screenshot example above. It only jumps to a block on the daily page (no paste), after prompting for which block to jump to.

Note the arg (the name of the block on the config page to use as the menu root) is set in the hotkey here, and no vars are set.

5.9 Dynamic Favorite Example

See DYNAMIC FAVORITE EXAMPLE This example queries the config page, searching for the named block passed as an arg (in this case set in the hotkey to Favorite Pages”). Children of the Favorite Pages” block are then searched for reference to other pages. Only one reference per child block is allowed. Nesting is not supported here.

6 External Targets

Following is more detail on the targets that are exposed by the script, and should be targeted by your own triggers. See examples above for how to call them.

You don’t need to know how these work, only that they exist, and what to pass to make them go.

6.1 Jump To Block:

Jumps to a block on the daily page, and optionally adds content under it. If the block does not exist anywhere under your current daily page, it will be created at the top of the daily page.

Pass the string content of the block to be targeted as an argument, eg generated by your hotkey, or an arg and var block. This allows the script to find the block.

The following variables can be passed into the target to control what happens when the block is jumped to:

  • paste: can be set to true” to trigger pasting of copy buffer on arrival
  • todo: can be set to true” to trigger making new target block into todo
  • addtimestamp: can be set to true” to trigger adding current timestamp to new block

These three can be combined in any combination. The various features can be fairly flexibly parameterized.

6.2 Daily Page Config

Takes one arg which is the text content of a block expected to be present on the RoamSearchConfig page. Presents child blocks of this block as menu options. Allows drilling down to child block using opinion key.

On selection, will call Jump To Block (as above), which will jump to selected block on daily page.

Any variables get passed through, allowing the functionality of paste, todo, timestamp as per Jump To Block.

6.3 Favourite Page Config

Takes one arg which is the text content of a block expected to be present on the RoamSearchConfig page. Presents child blocks of this block as menu options. Each child block is expected to link to one and only one page

On selection, linked page will be jumped to.

No vars are used used for this.

6.4 Jump To Named Page

Takes one arg, the name of a page in Roam. Jumps to this page. Takes no vars.

7 Caveats, bugs, and warnings

7.1 Use of keyboard automation

This script uses keyboard automation to insert content into Roam. This is implicitly unreliable. Holding down keys/button mashing when running actions can have strange consequences.

In particular: when using the jump to block on daily notes feature, if the block already exists the script will focus on the block, select all, then hit down arrow. This allows the block to move the the bottom of the page. However if you button mash whilst this is happening and hit a key just after the select all, you could delete all content from the block. Undo will fix it, if you notice. Blast radius is one bullet under daily notes only.

If you have strange behavior, please increase the keydelay variable, and let me know here

7.2 Browser security settings

The script requires applescript to be able to invoke javascript. You should do your own research on the implications of this.

7.3 Multiple Windows

There is some strangeness I cannot pin down on multiple windows, especially with incognito.

7.4 Support and General Issues

Please raise bug reports etc here, or post on the roam slack here

8 Development Notes

Source code is available on github

Note this repo uses osagitfilter to manage the Applescript. Instructions available here.

The code is a hot mess of hacked up applescript, javascript, and datomic. I know none of these languages well :)


Next post
Introduction to the Roam Alpha API RoamAlphaAPI is a javascript API available within Roam that allows querying of a Roam database using Datomic/Datalog. This page is a summary of what