Plugin API Reference Documentation

Note as of March 2023: If you're interested in building plugins (even if only for your own use) and would be interested in a chance to influence the plugin system, shoot a quick email to support@amplenote.com from the email address associated with your account. Feedback can be provided in the #plugin-atelier channel on our discord.



Amplenote provides support for client-side plugins that execute in the application on all platforms, allowing for enhancement of the default client behavior. A plugin is defined by a single note in a user's account - as the note is changed and updated, the plugin will be updated as well.


This document provides an overview of what plugins are capable of, and describes the available options for plugins.


If you would like to read a more general guide to getting started writing plugins, check out this related page on writing your first plugin.



linkPlugin creation

To create a plugin, you'll need a note that contains two things: a table of plugin information, and a code block containing the Javascript code of the plugin.



linkMetadata table

A plugin metadata table contains (at least) two columns: the name of the setting, and a value for the setting.

setting name

setting value

The setting name is not case sensitive. All columns are interpreted as strings.


linkname

The only required setting is the name of the plugin, which can be defined as:

name

Plugin name here

This is the name that the user will see when invoking the plugin, and will be used as a prefix in cases where the plugin defines multiple options presented to the user.


linkicon

The name of a Material Design Icon that will be used to identify the plugin. If not provided, a generic "extension" icon will be used.

icon

search


linkdescription

A short description shown when installing or configuring a plugin.

description

Count the number of words in a note.


linkinstructions

More detailed information about using the plugin that will be shown if the plugin is published to the Plugin Directory.

instructions

Here are some helpful words of advice on using this plugin:
1. Instruction one
2. Instruction two
3. Instruction three


linksetting

Defines settings that the user can provide to configure the plugin. The user will be able to supply a string for each setting when configuring the note as a plugin. When plugin code is invoked, it has access to the settings values that the user has provided. All setting values are provided as strings.

setting

API Key


This setting name can be repeated multiple times to define multiple settings.

setting

API Key

setting

Name


linkCode

The first code block in the note will be used as the plugin's code. Any subsequent code blocks will be ignored. The plugin code should define a Javascript object. Any functions defined on this object that match the name of an action will register the plugin to handle that action.

{
insertText() {
return "Hello World!"
}
}


To define multiple actions for a single action type, make the action-named field an object. The keys should be the name of the action, with the corresponding function as the value.

{
insertText: {
"one word": function() {
return "hello";
},
"two words": function() {
return "hello world";
}
}
}


When plugin code is invoked, the plugin object will be this, for example:

{
insertText() {
return this._text();
},

_text() {
return "hello world";
}
}


The plugin object will be instantiated when the plugin is installed in the client, retaining any state until it is reloaded (e.g. due to the plugin code being changed in the source note).

{
insertText() {
this._counter++;
return "hello " + this._counter;
},

_counter: 0,
}


Plugin action functions can return promises, which will be awaited.

{
insertText() {
return new Promise(function(resolve) {
setTimeout(resolve, 2000);
}).then(function() {
return "hello world, eventually";
});
}
}


The first argument passed to a plugin action function is an application interface object that can be used to access settings and call into the host application itself.


Given a plugin with the following metadata table entry:

setting

API Key

A plugin can access the setting through app.settings:

{
insertText(app) {
return app.settings["API Key"];
}
}


For action functions that receive arguments, the app argument will still be the first argument, before any other arguments:

{
replaceText(app, text) {
return text + " more";
}
}




linkActions

Plugin actions are the interaction points that define how a plugin's code gets called. Actions functions that return values can either return the value directly, or return a Promise that resolves to the value when ready. All action functions are passed an App Interface object as the first argument.


linkappOption

Adds app-wide options that can be invoked from the "jump to note" (web) or quick search (mobile app) dialogs.

Arguments

Returns

Nothing

{
appOption(app) {
app.alert("hello");
}
}





linkimageOption

Adds an option to the drop-down menu on each image in a note.

Arguments

image image object describing the selected image

Returns

Nothing

See also

app.context.updateImage to update the properties of the image

{
async imageOption(app, image) {
await app.alert("image: " + image.src);
}
}





linkinsertText

Called to insert text at a specific location in a note, when using an {expression}.

Arguments

Returns

String new text to insert in the note in place of the {expression}

{
insertText(app) {
return "hello world";
}
}

The auto-complete menu for expression will include any installed plugins:


The plugin's insertText action will be called to replace the {Test Plugin} expression, where "Test Plugin" is the configured plugin name.


linknoteOption

Adds options to the per-note menu, shown when editing a specific note.

Arguments

noteUUID the UUID of the note that the menu option was invoked in

Returns

Nothing

{
noteOption(app, noteUUID) {
app.alert(noteUUID);
}
}



linkreplaceText

Called to replace some highlighted text, invoked via the selection menu.

Arguments

text the String of selected text

Returns

String new text to replace the selected text with

null to cancel the replacement. Note that the replacement will also be cancelled if the user changes the selection before this action returns a value.

{
replaceText(app, text) {
return "new text";
}
}




linkApp Interface

The app interface provides the means of interacting with the application. It is passed to all plugin action functions as the first argument. All app interface functions should be considered asynchronous, returning a Promise that will either resolve to the result, or reject if there is an error.


linkapp.alert

Show the user a message. The name of the plugin is shown in the title of the dialog.

Arguments

message the String to show the user

options optional object, with any of the following properties:

preface a String to show before the main message

actions optional Array of action objects that will be added as buttons on the dialog. Each action object can have the following properties:

label the String text to show on the button

icon options String name of a Material Icon to show on the button

Returns

null if the user presses the "Done" button or dismisses the dialog

If options.actions is provided:

The integer index corresponding to the action the user selected

{
noteOption(app, noteUUID) {
app.alert("This is an alert");
}
}



{
async noteOption(app, noteUUID) {
const actionIndex = await app.alert("This is an alert", {
actions: [
{ icon: "post_add", label: "Insert in note" }
]
});

if (actionIndex === 0) {
// "Insert in note"
}
}
}



linkapp.attachNoteMedia

Upload a media file, associating it with a the specified note. This function uploads the file directly, so the user must be online for it to work, and it may take a long time, depending on the size of the media and connectivity.

Arguments

noteHandle describing the note to attach the media to

dataURL a data URL describing the media file data

Returns

String URL of uploaded media

Throws

If the media file is too large, or otherwise not allowed

If there are network errors that prevent upload

{
async insertText(app) {
const noteHandle = { uuid: app.context.noteUUID };

const response = await fetch("https://source.unsplash.com/featured/300x200");
const blob = await response.blob();
const dataURL = await this._dataURLFromBlob(blob);

const fileURL = await app.attachNoteMedia(noteHandle, dataURL);
app.context.replaceSelection(`![](${ fileURL })`);
return null;
},

_dataURLFromBlob(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();

reader.onload = event => {
resolve(event.target.result);
};

reader.onerror = function(event) {
reader.abort();
reject(event.target.error);
};

reader.readAsDataURL(blob);
});
}
}


linkapp.createNote

Create a new note, optionally specifying a name and/or tags to apply.

Arguments

name - optional String name to give the new note

tags - optional Array of String tag names to apply to the new note

Returns

uuid of the newly created note. This is typically a local-prefixed UUID that may change once persisted to the remote servers, but can continue to be used on the same client to identify the note. Calling findNote with this uuid will return a noteHandle with a non-local-prefixed UUID if the note has since completed persistence.

{
async noteOption(app, noteUUID) {
const uuid = await app.createNote("some new note", [ "some-tag" ]);
app.alert(uuid);
}
}


linkapp.context

Provides details about where the plugin action was invoked, and allows for interaction at that location.


linkapp.context.noteUUID

The String UUID of the note the plugin action was invoked from. This will include the note UUID that a task is in when invoking an action (e.g. insertText or replaceText) in the tasks view, or other editable task lists.


linkapp.context.replaceSelection

Replaces the selection with markdown content. This function will not be present for plugin actions that are not invoked with a user selection/cursor placed in a note - i.e. it is only defined for insertText and replaceText plugin actions. Note that the user can navigate away from a note while a plugin action is executing, in which case, calling this function is not guaranteed to do anything.

Arguments

String of markdown content to replace the selection with

Returns

boolean indicating if the given markdown content replaced the selection. Returns false if the selection has been completely removed from the note, or if the markdown content can't be inserted at the current location (e.g. a table in a task).

Throws

If the context in which the selection existed is no longer available, e.g. the note is completely closed

{
async insertText(app) {
const replacedSelection = await app.context.replaceSelection("**new content**");
if (replacedSelection) {
return null;
} else {
return "plain text"; // Fall back in cases where the markdown content wouldn't be valid at the selection position
}
}
}


linkapp.context.taskUUID

If the plugin action was invoked from a position in a task, this will be the String UUID of the task in question.

{
insertText(app) {
return app.context.taskUUID || "not in a task";
}
}


linkapp.context.updateImage

If the plugin action was invoked on an image (i.e. imageOption actions), can be called to update and image properties.

{
async imageOption(app, image) {
await app.context.updateImage({ caption: "and " + image.caption.toString() });
}
}


linkapp.filterNotes

Find noteHandles for all notes matching a set of filter criteria.

Arguments

(optional) object describing filter parameters, containing any of the following properties:

group - filter group to apply. This corresponds to the group= query string parameter when viewing https://www.amplenote.com/notes and filtering on a specific group or set of groups. Multiple groups can be specified with a , separator.

Examples


tag - tag filter to apply. This corresponds to the tag= query string parameter when viewing https://www.amplenote.com/notes and filtering on a specific tag or set of tags. Multiple tags can be specified with a , separator - matching notes must have all specified tags. A tag prefixed with ^ will only match notes that do not have the tag.

Examples

{ tag: "daily-jots" }

{ tag: "daily-jots,todo" } - matches notes that have the daily-jots tag and the todo tag.

{ tag: "daily-jots,^todo/next" } - matches notes that have the daily-jots tag and do not have the todo/next tag.

Returns

An array of noteHandles for all notes that match the filter parameters.

{
async insertText(app) {
const noteHandles = await app.filterNotes({ tag: "daily-jots" });
return `note count: ${ noteHandles.length }`;
}
}


linkapp.findNote

Finds the noteHandle of a note, if the note is extant and not marked as deleted. In addition to verifying whether a note exists, this can be used to fill in some additional details for a note, e.g. if the plugin only has a noteUUID it can call this to get the name and tags applied to the note.

Arguments

object identifying the note to find, with the following properties:

uuid the UUID identifying a specific note, if provided, will be used regardless of other properties

name String name of the note to find. If uuid is not provided, this must be supplied.

tags optional Array of tag filter Strings that the note must match, in addition to the name. Each array entry can be the name of a tag e.g. [ "some-tag" ] or can include a negation operator to only match notes that don't have that tag, e.g. [ "^not-this-tag" ]

Returns

noteHandle of the note, or null if the note does not exist or has been marked as deleted

{
async noteOption(app, noteUUID) {
const noteHandle = await app.findNote({ uuid: noteUUID });
app.alert(noteHandle.name);
}
}


linkapp.getNoteContent

Get the content of a note, as markdown.

Arguments

noteHandle identifying the note

Returns

The content of the note, as markdown.

{
async noteOption(app, noteUUID) {
const markdown = await app.getNoteContent({ uuid: noteUUID });
app.alert(markdown);
}
}


linkapp.getNoteSections

Gets a list of the sections in a note. Sections are areas of the note delimited by either a heading or a horizontal rule. Sections are identified by the heading (if any) that opens the section, and when relevant, an index to disambiguate between multiple sections with matching headings.

Arguments

noteHandle identifying the note

Returns

An Array of the sections in the note, with each array entry containing an object with the following properties

heading - will be null if the section does not start with a heading; otherwise, will be an object describing the heading, with the following properties:

anchor - the String anchor that can be used to navigate to the heading in the note`

level - the integer level of the heading (i.e. for H1-H3)

text - the String text of the heading, without any formatting that may be applied (bold/italic/etc)

index - if there are multiple sections with headings that otherwise match (either because they are all heading: null or the text of the heading is the same), sections after the first section will include this integer index of the heading.

{
async noteOption(app, noteUUID) {
const sections = await app.getNoteSections({ uuid: noteUUID });
app.alert("Section count: " + sections);
}
}


Examples:



[
{
"heading": null
},
{
"heading": {
"anchor": "Heading_1",
"level": 1,
"text": "Heading 1"
}
}
]




[
{
"heading": null
},
{
"heading": null,
"index": 1
},
{
"heading": {
"anchor": "Heading_1",
"level": 1,
"text": "Heading 1"
}
},
{
"heading": {
"anchor": "Heading_2",
"level": 2,
"text": "Heading 2"
}
},
{
"heading": null,
"index": 2
},
{
"heading": {
"anchor": "Heading_3",
"level": 2,
"text": "Heading 3"
}
}
]


linkapp.getNoteTasks

Returns the tasks that are present in the specified note.

Arguments

noteHandle identifying the note to get tasks from

options object, with the following optional properties:

includeDone - boolean indicating whether completed and dismissed tasks in the note should be returned in addition to the un-done tasks. Defaults to false.

Returns

Array of task objects

{
async noteOption(app, noteUUID) {
const tasks = await app.getNoteTasks({ uuid: noteUUID });
app.alert(`Note has ${ tasks.length } tasks`);
}
}


linkapp.getTask

Get the details of a single task.

Arguments

UUID String identifying the task

Returns

task object, or null if no task with the given UUID exists

{
async noteOption(app, noteUUID) {
const taskUUID = "e6adcbcb-04dc-4fbd-857e-dbf63a253e7e";
const task = await app.getTask(taskUUID);
app.alert(JSON.stringify(task));
}
}


linkapp.insertNoteContent

Inserts content into a note.

Arguments

noteHandle identifying the note to insert the text into

content: String of markdown-formatted content to insert

(optional) object of additional options, with the following properties:

atEnd boolean indicating that content should be inserted at the end of the note. Defaults to false.

Returns

Nothing

Throws

If markdown content is over 100k characters

If the target note is readonly

{
noteOption(app, noteUUID) {
app.insertNoteContent({ uuid: noteUUID }, "this is some **bold** text");
}
}


linkapp.insertTask

Inserts a new task at the beginning of a note. See also: note.insertTask.

Arguments

noteHandle identifying the note to insert the task into

task object, with the following attributes (all are optional):

content: String of markdown-formatted content to use in the task

hideUntil: Number a unix timestamp (seconds) to use for the "Hide until" time

startAt: Number a unix timestamp (seconds) to use for the "Start at" time

Returns

The UUID of the newly created task

Throws

If the provided content is not valid in a task (e.g. - a bullet list item)

If note is readonly/locked

{
async noteOption(app, noteUUID) {
const taskUUID = await app.insertTask({ uuid: noteUUID }, { text: "this is a task" });
app.alert(taskUUID);
}
}

link

linkapp.navigate

Opens the app to the location corresponding tot he given Amplenote app URL. Amplenote app URLs start with https://www.amplenote.com/notes. Examples:

Jots area: "https://www.amplenote.com/notes/jots"

Notes area: "https://www.amplenote.com/notes"

Notes list filtered to a tag: "https://www.amplenote.com/notes?tag=some-tag"

A specific note: "https://www.amplenote.com/notes/NOTE_UUID" (replacing NOTE_UUID with a specific note's UUID).


Arguments

url an Amplenote URL string

Returns

true if the given url was a valid Amplenote URL and was navigated to, false otherwise

{
noteOption(app) {
app.navigate("https://www.amplenote.com/notes/jots");
}
}


linkapp.notes

The notes object provides an alternative - and simpler - way to interact with specific notes. Depending on the purpose, it may be preferable than the noteHandle-based functions available on the main app interface. Functions on the notes object return Note interface objects. As with noteHandles, a note interface object may represent a note that does not (yet) exist. Calling any note interface function that requires an extant note will create the note first, if it doesn't already exist.


linkapp.notes.create

Create a new note. This is an alternative interface to app.createNote.

Arguments

name the String to use as the new note's name

tags an Array of String tag names to apply to the new note

Returns

Note interface object for the newly created note

{
async noteOption(app, noteUUID) {
const note = await app.notes.create("some new note", [ "some-tag" ]);
app.alert(note.uuid);
}
}


linkapp.notes.dailyJot

Gets a note interface for the daily jot note on the day corresponding to the given timestamp.

Arguments

timestamp unix timestamp Number (seconds) indicating any time on the day the daily jot note should be for

Returns

Note interface object for the daily jot note

{
async noteOption(app, noteUUID) {
const tomorrowTimestamp = Math.floor(Date.now() / 1000) + 60 * 60 * 24;
const note = await app.notes.dailyJot(tomorrowTimestamp);
app.alert(note.name);
}
}


linkapp.notes.filter

Find noteHandles for all notes matching a set of filter criteria. This is an alternative interface to app.filterNotes.

Arguments

(optional) object describing filter parameters, containing any of the following properties:

group - filter group to apply. This corresponds to the group= query string parameter when viewing https://www.amplenote.com/notes and filtering on a specific group or set of groups. Multiple groups can be specified with a , separator.

Examples


tag - tag filter to apply. This corresponds to the tag= query string parameter when viewing https://www.amplenote.com/notes and filtering on a specific tag or set of tags. Multiple tags can be specified with a , separator - matching notes must have all specified tags. A tag prefixed with ^ will only match notes that do not have the tag.

Examples

{ tag: "daily-jots" }

{ tag: "daily-jots,todo" } - matches notes that have the daily-jots tag and the todo tag.

{ tag: "daily-jots,^todo/next" } - matches notes that have the daily-jots tag and do not have the todo/next tag.

Returns

An array of noteHandles for all notes that match the filter parameters.

{
async insertText(app) {
const noteHandles = await app.notes.filter({ tag: "daily-jots" });
return `note count: ${ noteHandles.length }`;
}
}


linkapp.notes.find

Find a specific note. This is an alternative interface to app.findNote.

Arguments

uuid the String UUID identifying the note

Returns

Note interface object for the note, or null if the note does not exist or has been marked as deleted

{
async noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
app.alert(note.name);
}
}


linkapp.prompt

Show the user a message and input fields - defaulting to a single text input - receiving the user-selected/user-entered value(s).

Arguments

message String to show the user

options optional object of additional options. Supported properties:

inputs an optional Array of input objects describing the input fields to show. If not provided, a single text input field will be shown (equivalent to inputs: [ { type: "text" } ]).

input object, with the following properties:

label a String to use as a label for the input. Examples: checkbox, select, and text

options Array of options to use in a type: "select" drop-down input or type: "radio" input each option an object with the following properties:

label the String to show as a label on the input field

value the value corresponding to the option. Will be returned as the result (verbatim) when the user selects the option.

image a String URL of an image to show with the option. Only used in type: "radio" inputs.

placeholder a String to use as a placeholder value for a type: "text" input

type String one of the following values:

"checkbox" a check box field

"note" a field to select a single note from the user's notes list

"radio" a set of radio buttons to select one option from. When specified, the options property should also be provided.

"select" a drop-down select field. When specified, the options property should also be provided.

"text" a text area field

Returns

null if the user selected "Cancel", or otherwise closed the dialog without pressing "Submit"

If no inputs option is provided

The String text the user entered

If a single inputs option is provided, one of:

The String text the user entered for a type: "text" input

The Bool value corresponding to the user's selection for a type: "checkbox" input

The noteHandle of the selected note for a type: "note" input

The value corresponding to the value field of the selected type: "select" option

If multiple inputs are provided, an Array of values corresponding to value selected for each input (in the same order)

{
insertText(app) {
return app.prompt("Enter text").then(function(text) {
return text;
});
}
}




{
async noteOption(app, noteUUID) {
const result = await app.prompt("This is the message", {
inputs: [
{ label: "This is the label", placeholder: "This is the placeholder", type: "text" },
{ label: "This is the label", type: "checkbox" },
{ label: "This is the label", type: "select", options: [ { label: "something", value: 1 }, { label: "other", value: 2 } ] },
]
});

if (result) {
const [ textResult, checkboxResult, selectResult ] = result;

} else {
// User canceled
}
}
}




linkapp.replaceNoteContent

Replace the entire content of a note with new content, or replace the content of a single section of the note (see app.getNoteSectionsfor discussion of sections).

Arguments

noteHandle describing the note to replace content in

Markdown content String of new content

(optional) object of additional options, supporting the following properties

section an object describing the section of the note to replace content in. See app.getNoteSections for more description of sections. For sections that start with a heading, the heading will not be replaced, only the content of the section.

Returns

bool indicating whether the replacement was performed. Note that the only failure state for a replacement is when a section is specified and that section is not found in the note.

Throws

If markdown content is over 100k characters

If the target note is readonly

{
async noteOption(app, noteUUID) {
const newContent = "**new content**";
await app.replaceNoteContent({ uuid: noteUUID }, content);
}
}


linkapp.saveFile

Save a file.

Arguments

The Blob/ File object describing the content of the file

String filename to use for the file

Returns

A promise that resolves when the request to save the file has been sent. Note that the user may see a prompt to accept/name/save the file, in which case this will resolve as soon as the request has been displayed to the user - it does not wait for the user to actually save the file.

{
async noteOption(app) {
const file = new Blob([ "some text" ], { type: "text/plain" });
await app.saveFile(file, "test.txt");
}
}


linkapp.settings

An object containing the user-configured settings for the plugin. All values will be strings.


{
insertText(app) {
return app.settings["Some setting"];
}
}


This will insert the string the user has entered for "Some setting", assuming the plugin metadata table includes:

setting

Some setting



linkapp.updateTask

Update the properties or content of a single task.

Arguments

String UUID identifying the task

object containing (optional) updates to apply to the task. All properties listed for a task are supported, except the uuid

Returns

boolean indicating whether the task could be updated. If the given task UUID doesn't correspond to any existing task, will return false

{
async insertText(app) {
const taskUUID = app.context.taskUUID;
if (!taskUUID) return null; // Cursor is not in a task

await app.updateTask(taskUUID, { completedAt: Math.floor(Date.now() / 1000) });
return "";
}
}




linkNote interface

The note interface provides a more convenient way to interact with the app interface functions that operate on a specific noteHandle. Note that - like a noteHandle - the note described by the note interface may or may not exist. Calling functions that modify the note content will create the note if it doesn't already exist.


linknote.attachMedia

Attach a media file (image or video) to the note. See app.attachNoteMedia for more details.

{
async noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
// 1x1 gray PNG (from http://png-pixel.com)
const dataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mPcsv/MfwAHrgNAOkBd9gAAAABJRU5ErkJggg==";
const imageURL = await note.attachMedia(dataURL);
app.alert(`ImageURL: ` + imageURL);
}
}


linknote.content

Get the content of the note, as markdown. See app.getNoteContent for more details.

{
async noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
app.alert(await note.content());
}
}


linknote.insertContent

Inserts content at the beginning of a note. See app.insertContent for more details.

{
noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
note.insertContent("this is some **bold** text");
}
}


linknote.insertTask

Inserts a new task at the beginning of a note. See app.insertTask for more details.

{
async noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
const taskUUID = await note.insertTask({ content: "this is a task" });
app.alert(taskUUID);
}
}


linknote.replaceContent

Replaces the content of the entire note, or a section of the note, with new content. See app.replaceNoteContent for more details.

{
async noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
await note.replaceContent("**new content**");
}
}


linknote.sections

Gets the sections in the note. See app.getNoteSections for more details.

{
async noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
const sections = await note.sections();
app.alert("Section count: " + sections);
}
}


linknote.tasks

Gets the tasks in the note. See app.getNoteTasks for more details.

{
async noteOption(app, noteUUID) {
const note = await app.notes.find(noteUUID);
const tasks = await note.tasks();
app.alert(`Note has ${ tasks.length } tasks`);
}
}



linkimage

Describes an image displayed inline in a note. image objects can have the following properties:

caption markdown String describing the content of the image caption

src the String source URL of the image

text the String of OCR text recognized in the image



linknoteHandle

Some app interface functions take a noteHandle argument. Note handles are objects that can identify notes even if they do not yet exist (e.g. future daily jot notes). Calling an app interface function with the noteHandle of a note that does not yet exist is likely to create that note, for example when inserting content into the note.


An individual note that exists in identified by a uuid - however there are some cases where a note may not yet have been acknowledged by the server (e.g. in an extended period of offline usage), in which case the note's uuid may be prefixed with the string "local-" and may change once the note is fully persisted to the Amplenote servers. The local-prefixed uuid can continue to be used on the same client that created the note, even after the note has been fully persisted.



linktask

Some app interface functions return - or accept - task objects that can have the following properties:

completedAt integer unix (seconds) timestamp describing the (UTC) time at which the task was completed - only present if the task has been completed.

content markdown String describing the content of the task

dismissedAt integer unix (seconds) timestamp describing the (UTC) time at which the task was dismissed - only present if the task has been dismissed.

hideUntil integer unix (seconds) timestamp describing the (UTC) time that the task will be hidden until, or null if the task is not hidden.

score Number corresponding to the total task score attributed to the task at the current moment. Note that this accumulates once daily, depending on interaction with the task and/or note the task is in.

startAt integer unix (seconds) timestamp describing the (UTC) time that the task starts at, or null if the task does not have a start time set.

uuid String UUID uniquely identifying this task.



linkPlugin code execution environment

Plugin code is executed in a sandboxed iFrame, preventing direct access to the outer page/application. In the native mobile applications, plugin iFrames are loaded inside an isolated (hidden) WebView, with each plugin iFrame providing isolation from any other plugins that may be loaded.


Plugin code is executed in the user's browser on web and in the system WebView on mobile, so it's worth keeping browser compatibility in mind. There are no polyfills applied in the plugin code sandbox, nor is any processing performed on plugin code.



linkMarkdown content

Markdown content that is passed to plugins - e.g. via app.getNoteContent - matches the feature set of converting a note to markdown (via per-note menu option or export to markdown).


Note that the markdown passed by plugins to the app is very limited as of March 2023, but will continue to be expanded to eventually support all Amplenote features.



linkUpdate history

Unreleased

Add imageOption actions

Add app.context.updateImage

May 22nd, 2023 (mobile app version 3.70)

Add app.saveFile

Add score to task

May 12th, 2023 (mobile app version 3.70)

Add appOption action

Add app.getNoteTasks, app.updateTask, and note.tasks

Include taskUUID in app.context when insertText or replaceText actions are invoked in a task.

April 30th, 2023 (mobile app version 3.67)

Add app.attachNoteMedia

Add type: "radio" to app.prompt

April 18th, 2023 (mobile app version 3.66)

Add app.context.replaceSelection

April 12th, 2023 (mobile app version 3.66)

Rename app.insertContent to app.insertNoteContent - app.insertContent will remain as an alias to support existing plugins using it.

Add options.atEnd to app.insertNoteContent

April 11th, 2023 (mobile app version 3.66)

Add app.getNoteSections/ note.sections

Add app.replaceNoteContent / note.replaceContent

April 7th, 2023 (mobile app version 3.66)

Add { type: "note" } input type to app.prompt

April 6th, 2023 (mobile app version 3.65)

Add app.navigate

March 31st, 2023 (mobile app version 3.65)

Add app.context

Throw exception from insertTask and insertContent when target note is locked/readonly

Handle markdown tables (e.g. "|||\n|-|-|\n|table|content|") in insertContent

March 30th, 2023 (mobile app version 3.65)

Update app.alert to allow for additional action buttons

Update app.prompt to allow for various input types

Removes options.placeholder from app.prompt

March 28th, 2023

Add options.preface to app.alert

Add options.placeholder to app.prompt

March 9th, 2023

insertText changed to insertContent, handling markdown content

insertTask text attribute changed to content, handling markdown content