[Mac] MarginNote ⨉ AppleScript: Test out AppleScript on MarginNote!

MarginNote ⨉ AppleScript: An Invite to Test out AppleScript on MarginNote

@ExtsTester

Hi everyone!

We are really excited to bring support for AppleScript to MarginNote 3. The features are almost complete, but before rolling them out, we would love to have them tested. If you are interested, download the beta version below.

:warning::warning::warning: Note that you will have to have a license for the website version of MarginNote to test it out. If you purchased MarginNote from the Mac AppStore, we are sorry that we are unable to offer you a chance to have a look at it early. (No worries though as this version will be rolling out soon.)

If you’ve encountered any problems or have any specific needs while testing out AppleScript, PLEASE SHARE THEM IN THIS THREAD as we would love to improve this.

To get you started with using AppleScript with MarginNote, we have prepared a brief guide. Although this guide serve no intention to teach you the needy-greedy details of AppleScript, you can probably get a brief idea of how to automate MarginNote 3 using AppleScript.

What is AppleScript, and why?

Briefly speaking, it is a scripting language on the MacOS platform that allows for the automation of the system itself and supported apps. While official support for AppleScript ended in 2014 or so, it is still the most popular scripting language on the MacOS platform and still enjoys a wide range of support, especially in pro applications. By supporting AppleScript, we hope that our pro-users on the Mac could optimize their workflows and accomplish things that they previously weren’t able to.

MarginNote ⨉ AppleScript

If you are not a programmer, learning a scripting language may sound very intimdating to you. However, you do not need to feel worried as AppleScript is very easy to work with as it is very close to natural language.

Preparations

To use AppleScript with MarginNote 3, you will have to have the beta version of MarginNote 3 installed. You will also need to have Script Editor which should already be installed on your make. If you need better code-highlighting, you can use Visual Studio code and the AppleScript plugin.

Launching MarginNote

Open up Script Editor, type in the following line of code:

tell application "MarginNote 3" to activate

Once finished, click “run” (Or press Cmd + R on your keyboard). Notice that this single line of code will open MarginNote. It doesn’t sound too bad to understand, does it?

Checking the Dictionary

In order to use some of the specific AppleScript commands with MarginNote, we will have to open the dictionary for MarginNote 3. While having Script Editor open in the foreground, select “File” → “Open Dictionary” from the top menu (or press Shift + Cmd + O on your keyboard), and you will be prompted to select the dictionary for an app. Select MarginNote 3.

Then, you will have the dictionary open.

The dictionary serves as the reference for writing AppleScript for MarginNote 3. Here, we will not go into the details as to how you can use the dictionary, but instead we will give a few samples and explain how the sample works. You can compare the the sample code with the dictionary to have a better understanding of how to use it.

Searching For A Notebook

Now, with the dictionary open, let’s have a look at how to search for a specific notebook.

tell application "MarginNote 3"
	return search notebook "Sample"
end tell

After typing the code into Script Editor, run it, and your should be getting something similar to the picture below.

Note that down in the output section, I have:

{notebook id "03ABACC5-D2AF-4C9F-980C-A7336035C55E" of application "MarginNote 3", notebook id "CAF0F280-B443-412C-8CAF-2520E9084B44" of application "MarginNote 3", notebook id "E4420563-333C-4A7A-94F8-23154EA5FDB9" of application "MarginNote 3"}

This segment of code essentially tells MarginNote 3 to search for all the notebooks that has “Sample” in its name and return them. If you didn’t get anything or gets an error, no worries. It essentially means that no notebook in your collection has the word “Sample” in its name. Simple change the word “Sample” to the name of a notebook in your collection.

If we would dive a little deeper into the code, you realize that it has two layers:

tell application "MarginNote 3"

end tell

This outer layer serves as an enclosure for all the actions that needs to be done using the APIs provided by MarginNote 3, and inside it lies the code that deals with MarginNote’s AppleScript API.

Now that we can get the notebooks whose name contains “Sample”, but how can we get a single notebook? Well, to do this, we can use the following code.

tell application "MarginNote 3"
	return item 1 of (search notebook "Sample")
end tell

What this line of code does is that it returns the first item of the result of search notebook "Sample". Simple enough?

Okay, you may be asking, how do we get all the notebooks? To do this, you simply change the word Sample to an empty text, like the code shown below.

tell application "MarginNote 3"
	return search notebook ""
end tell

Note that you could also limit your search to document notebooks, mind-map notebooks, and card decks.

tell application "MarginNote 3"
	return search notebook "" type DocumentNotebook
end tell
tell application "MarginNote 3"
	return search notebook "" type MindMapNotebook
end tell
tell application "MarginNote 3"
	return search notebook "" type CardDeck
end tell

Getting Notes of a Notebook

Now that we have the notebooks, we could get the notes of a notebook through the code below:

tell application "MarginNote 3"
	set nbk to item 1 of (search notebook "Sample") 
	# you should be familiar with the code above:-)
	set nLst to note ids of nbk
	# It gets the ids of all the notes in the notebook that we get from the code above
	set nLst to fetch dictionaries nLst
	# The code above returns a dictionary for each of the ids provided
	set titleLst to {}
	# The code above creates an empty list for all the note titles.
	# We now then iterate through nLst, the list of notes, with the repeat statement
	repeat with n in nLst
		# We do not want add the title of a note to the list if the note does not have a title, so we will need to use an if statement to tell if a note has a title or not. If it has, we would want to add it to the titleLst.
		if title of n is not "" then
			# Add the title of n to the end of our titleLst.
			set end of titleLst to title of n
		end if
	end repeat
	# return the titleLst
	return titleLst
end tell

Note that in AppleScript, all comments start with #. As the comments are pretty detailed, we will not explain the code again here.

There is something to be noted though. Look at the following two lines of code:

set nLst to note ids of nbk
set nLst to fetch dictionaries nLst

For this code, we are reading all the ids of the notebook, and then get the dictionary of for each id. You can use the following code to achieve a similar purpose:

set nLst to notes of nbk

However, using this single-line version below would be significantly slower than the two-line version, because in the second one, each time you iterate through the notes, you will have to communicate with MarginNote, which is quite slow. This can make a massive difference when you are dealing with a large number of notes. Don’t quite understand? No worries, just use the version with two lines.

Getting Notes from Main Mind-map and Child Mind-maps

If you want to separate the notes that you get from the main mind-map and the child mind-maps, you can use the code below:

tell application "MarginNote 3"
	# Get the notebook with name "Sample"
	set nbk to item 1 of (search notebook "Sample")
	# Get the child mind-maps of the notebook
	set nLst to child mindmaps of nbk
	# Create a list that would contain the notes of the different mind-maps
	set childmindmapLst to {}
	# Create a list for the notes of the main mind-map
	set titleLst to {}
	# Get the notes in the main (root) mind-map
	set rLst to note ids in root mindmap of nbk
	set rLst to fetch dictionaries rLst
	
	# Add the title of the notes to titleLst
	repeat with m in rLst
		if title of m ≠ missing value then
			set the end of titleLst to title of m
		end if
	end repeat
	
	# add titleLst to the end of the childmindmapLst
	set the end of childmindmapLst to titleLst
	
	# for each child mind-map, add the title list of the child mind-map to the end of the childmindmap list
	repeat with childmindmap in nLst
		set titleLst to {}
		set bLst to note ids in branch of childmindmap 
		set bLst to fetch dictionaries bLst
		repeat with m in bLst
			if title of m ≠ missing value then
				set the end of titleLst to title of m
			end if
		end repeat
		set the end of childmindmapLst to titleLst
	end repeat
	# return the child mind-map list
	return childmindmapLst
end tell

Getting Json Of the Notes

It is actually quite a hassle sometimes trying to get the different attributes of a note. Therefore, we’ve provided a json API that allows you to get the json for each single note. To do this, change the code fetch dictionaries (listname) to fetch dictionaries (listname) with json. Afterwards, the code should something like this:

tell application "MarginNote 3"
	set nbk to item 1 of (search notebook "Sample") 
	set nLst to note ids of nbk
	set nLst to fetch dictionaries nLst with json # Look at here!
	set jsonLst to {}
	repeat with n in nLst
		if title of n is not "" then
			# We need to get the json of a note and add it to our json list using the code below
			set end of jsonLst to json of n
		end if
	end repeat
	return jsonLst
end tell

The support for json opens up a wide variety of opportunities. You can now use MarginNote with your python, JavaScript, and many other languages.

Manipulating the Notes

Besides reading and fetching notes, it is also quite easy to manipulate the notes. Here is an example:

tell application "MarginNote 3"
	set nbkid to id of item 1 of (search notebook "MarginNote 3 User Guide") # gets the notebook id of the MarginNote 3 User Guide notebook
	set p to item 1 of (search note in notebook nbkid text "Powerful Excerpt") # search for the first note with text Powerful Excerpt in the notebook
	set nn to new note in notebook nbkid # create a new note
	add child notes {nn} target note p # add nn as a child note to the note p from the search result
	set title of nn to "NewNote" # change the title of nn to "NewNote"
	set n2 to fetch note "1806EAE8-7581-4B91-B3D7-E0E188DD0BF9" # Fetch the note with the id specified.
	drag notes {n2} target note p method Clone # clone note n2 to the note p
	append text comment "test comment" target note nn # add text comment to nn
end tell

Bugs and Requests

If you’ve encountered any bugs or have any requests for us to have a look regarding the AppleScript, feel free to create a post in this thread.

Thanks so much for testing this out, and the MarginNote team sincerely wish that we could grow together into a brighter future.

3 Likes

@Edward_Support-Team
I am checking out the AppleScript support in 3.7.8. I am unable to retrieve comment dictionaries.

For example, in this snippet that you published above

ScriptDebugger reports this:

In no case have I been able to retrieve comment dictionaries, when I know for certain comments exist in the notebooks.

1 Like

It’s weird.
I checked and found that the code is able to get the comment dictionary of notes properly. Can you send me a screenshot of your notes?

Regards.

Personal. No.

is there still the option to buy MarginNote from the website? I don’t see this option in the store?!

Hi,
I found the download link myself and started to play with AppleScript in MN3.
How can I extract a link to open a note in MN3 from within another app?
Any ideas or hints?

Thanks!

I know how to pass a link to an AppleScript, open MN3 - but I found no way to select and show an note. I do have the ID of the note - so for example:

set myNote to fetch note theNoteID

works - I get the note object, but MN3 will not show this single note.
I tried:

set theLink to get link of myNote

with no success so far. Any idea?

P.S.:
found the URL schema myself:

marginnote3app://note/89705AAE-450B-4FA6-88BD-E81DAA244969

:wink: