This Obsidian plugin lets the user type text shortcuts, each of which triggers a script (JS). The script generates text which then replaces the typed shortcut.
This plugin works on all platforms, including mobile. It works with the new and legacy editors (CM6 and CM5).
This plugin is currently in open beta.
The Inline Scripts plugin replaces typed shortcuts with "expansion" text generated by JavaScript, for example:
;;date::
can cause the text to be replaced with 6/7/2022
;;name male european::
can cause the text to be replaced with Bill Harrington
The second example shows how shortcut text can include parameter text (male european
) which can affect the resulting expansion text.
Shortcuts can be defined in the settings. Inline Scripts comes with some sample shortcuts defined by default. See the tutorials "Setup the plugin and try it out" and "Create a new shortcut" for details.
Shortcuts can also be defined in shortcut-files, to be added to the vault as notes. This requires a bit more work, but allows for better organization and sharing of shortcuts. Users can download prewritten shortcut-files into their vault, or write their own. See the tutorials "Add existing shortcut-files to a vault" and "Create a new shortcut-file" for details.
Finally, Inline Scripts provides a number of ways to trigger shortcuts beyond typing them directly in the note. This includes a panel where buttons can be added to trigger shortcuts. It also includes the ability to write links into a note that trigger a shortcut each time they are clicked. See the tutorials "Use the buttons panel to run shortcuts" and "Add links to a note to run shortcuts" for details.
;;
and ::
.
;
, suffix=-;.
). If it does then these settings will revert when you leave the Inline Scripts plugin options."
and [
.If you...
... then visit the discussions page.
If you find this plugin useful, then a donation lets me know
that I should keep working to make it better.
Community plugins
in the left-hand menu (at the bottom of the Options
section).Browse
button to the right of Community plugins
.;;hi::
. Notice that the shortcut expands to "Hello! How are you?" as soon as you've finished typing it.;;d100::
. Notice that the shortcut expands to a roll-result as soon as you've finished typing it.Inline Scripts comes with the following sample shortcuts defined by default:
Inline Scripts has a collection of "help" shortcuts that describe all shortcuts currently loaded. The shortcut help
is a good place to start. It shows a list and summary of the "help" shortcuts.
Each shortcut is defined by three strings.
Id | Test string | Expansion string | About string |
---|---|---|---|
1 | hi | return "Hello! How are you?"; | hi - Expands into "Hello! How are you?". A simple shortcut to see if the Inline Scripts plugin is running. |
2 | ^date$ | return new Date().toLocaleDateString(); | Expands into the current, local date. |
3 | return "I am " + $1 + " years old."; | age {how old: >=0} - Expands into "I am {how old} years old". {how old} can be any positive integer. This is a demo shortcut written for this documentation. |
At its most basic, a Test string can just be the shortcut itself. In this example, the shortcut will be triggered when the user types ;;hi::
. Once triggered, the Expansion string's javascript is run. In this example the javascript produces the string "Hello! How are you?". The shortcut that the user typed (;;hi::
) will be replaced with Hello! How are you?
.
Note the format of the About string. It contains the syntax hi
, a dash, and then the description: the shortcut's expansion and purpose.
This shortcut is a bit more involved. The Test string contains the symbols ^
and $
. These are regex tokens to ensure that this shortcut isn't used for text like "mydate" and "datetomorrow", only "date". I suggest using ^
and $
in all of your test strings, unless there is a good reason not to. The Expansion string is also less obvious, but is just a JavaScript way to get the current date. The result of this example shortcut is: if the user types ;;date::
it will be replaced with the current date.
This shortcut's About string contains no syntax or purpose, only the expansion. The purpose is obvious and so is left out. The syntax, if ommitted, always defaults to the Test string. For this shortcut, the About syntax is ^date$
.
This shortcut's Test string has some advanced regex. If it's not clear then there is plenty of documentation online to learn regex syntax. Notice the parenthesis (
, )
. These are regex tokens to collect whatever is recognized within them and put it into a variable. The first parenthesis are put into variable $1
, a second parenthesis would be put into variable $2
, and so on. These variables are available to the Expansion string. In this shortcut, the Expansion string does reference variable $1
. The result of this shortcut is: if the user types ;;age 3::
the shortcut will be replaced with I am 3 years old.
If the user types ;;age 21::
, it will be replaced with I am 21 years old
.
The About string starts with the syntax, including a named descriptor {how old: >=0}
representing a parameter. >=0
lets us know that the parameter is an integer greater than or equal to 0. There is no "default" listed, so this parameter is required.
After the syntax (and dash) is the expansion and purpose.
Make sure that the Inline Scripts plugin is installed and enabled in your vault. (see the tutorial Setup the plugin and try it out.)
Open the settings for the Inline Script plugin.
Go down to the "Shortcuts" setting. It's just after "Shortcut-files". (see picture below)
The setting has two buttons: "Add shortcut" and "Add defaults". Click on the "Add shortcut" button. This adds a shortcut entry to the bottom of the list. The new entry should include three textboxes with the greyed text "Test (regex)", "Expansion (JavaScript)" and "About (text)". (see picture below)
Enter a shortcut's Test and Expansion strings into the new entry. I suggest starting with something simple like: test
and return "The test worked.";
. The About string is optional, but is required for the autocomplete feature to recognize the shortcut. If entered, follow the format: syntax, dash, description. (see picture below)
Close the settings panel.
Try typing your new shortcut into a note to make sure it works. Example: type ;;test::
Shortcuts, by their JavScript nature, have a risk of being malicious. Make sure you trust a shortcut or shortcut-file before using it.
There is a library of shortcut-files for Inline Scripts here. You can bring individual shortcut-files into your vault from this library, or from any other source. Alternately, the Inline Scripts plugin has a button to import the entire library to your vault at once.
Video tutorials have been added for the more stable and complex shortcut-files in the library. Type "help x" (where "x" is the shortcut-file's name) to get links to any tutorial videos for the shortcut-file.
For beginners, I'd advise watching these tutorial videos:
(Note - The shortcut-file "states.sfile" has two tutorial videos, one for users and one for developers.)
Make sure that the Inline Scripts plugin is installed and enabled in your vault. (see the tutorial Setup the plugin and try it out.)
Open the settings for the Inline Scripts plugin.
Find the "Shortcut-files" setting. It is just beneath "Shortcut Sources".
To the right of the setting is the button "Import full library". Click on that button and then click the "Ok" button. This will trigger the import. It might take a minute to download, depending on your internet connection and device.
Once the import is finished, you should see a bunch of shortcut-file entries added beneath the "Shortcut-files" setting (unless they were already there from a previous import). Close the settings panel.
All the shortcuts defined in the shortcut-file library should now work. Try typing one of the shortcuts to confirm this, such as ;;event::
or ;;une::
.
You can type ;;help::
to start learning about all of the shortcuts provided by the library.
If you find that you need to disable a shortcut-file listed in the settings, you can do so by turning off the toggle button to the left of the entry.
Make sure that the Inline Scripts plugin is installed and enabled in your vault. (see the tutorial Setup the plugin and try it out.)
Get the contents of a shortcut-file into a note in your vault. You can do this in one of two ways.
Determine and remember the shortcut-file note's address in your vault. This is the note's folder-path, followed by the note's name.
support/inlineScripts/state.sfile
. The name of this shortcut-file note is state.sfile
, the folder-path is support/inlineScripts
.Open the settings for the Inline Scripts plugin.
Add a reference to the shortcut-file.
support/inlineScripts/state.sfile
.(optional) Turning off the toggle button to the left of the entry will disable this shortcut-file. This makes Inline Scripts act as if the shortcut-file was removed from this list. This is is useful to stop using a shortcut file, without fully removing it from the list.
Close the settings panel.
The shortcuts defined in the shortcut-file should now work. Try typing one of the shortcuts to confirm this.
You can type ;;help::
to start learning about all of the shortcuts provided by the new shortcut-file.
This tutorial assumes that you have read and understood the tutorial "Create a new shortcut", and are at least aware that the tutorial "Add existing shortcut-files to a vault" shows how to setup an existing shortcut-file.
A shortcut-file contains multiple shortcuts. Each shortcut contains three strings: Test, Expansion and About. A shortcut-file will typically bundle collections of shortcuts that work toward a common goal, such as a particular functionality (saving & loading) or a particular system (Dungeons and Dragons).
Here is a minimal example of a shortcut-file:
test
return "The test worked.";
__
This shortcut-file contains a single shortcut. Notice that __
separate each section.
Here is another, more meaty, example:
This is a test shortcut-file.
It was written as an example for the plugin's HOW-TO documentation.
^name$
return "Maggie Smith";
name - Expands to "Maggie smith".
^repeat ([a-zA-Z])$
return $1.repeat(5);
repeat {to repeat: single character} - Expands to 5 repetitions of {to repeat}: "aaaaa".
This shortcut-file begins with an About string, a description of the shortcut-file. After the About string, it contains two shortcuts. Notice that the first __
is placed after the shorcut-file's About string. Every shortcut-file has an About string, including the minimal example above, though in that case the About string is empty. Also notice that there are empty lines between each section of this shortcut-file. Inline Scripts ignores empty lines in shortcut-files, so use them to organize your shortcut-files.
At the bottom of the Inline Scripts settings is a switch named Developer mode (see picture below). When Developer mode is on, all shortcuts will be reloaded whenever you leave a shortcut-file note, either by selecting a different note, or closing the shortcut-file note. This lets you edit a shortcut-file note, then move to another note to try out your changes without needing to manually refreshing anything. "Developer mode" adds a slight delay when switching notes, so I suggest keeping it off unless you are actively developing a shortcut-file.
A shortcut-file's About text, and each shortcut's about text are used to create help shortcuts for the user. See the section Help shortcuts for more information about this.
The Inline Scripts plugin adds an optional panel to Obsidian onto which you can setup buttons to trigger different shortcuts. It is turned on by default, and can be found in Obsidian's right sidebar. If it's not there you can open it by going to the Inline Scripts plugin settings and clicking the button "Open buttons view" (at the top).
The Inline Scripts plugin allows writing links into notes that, when clicked, will run predefined shortcuts. There are a few options:
The link's displayed text starts out as the shortcut text by default, though a custom display text can be defined. See examples #5 & #6.
Links can contain a block-id to change where the expansion result is placed. A block-id in iscript and iscript-once has the block replaced by the expansion result. A block-id in iscript-append and iscript-prepend gets added to the end or beginning of the block respectively. See examples #7, #8, #9 & #10.
Links can contain custom JS code to format the shortcut expansion before printing it. See examples #11 & #12.
Links will accept "???" blocks in their shortcut text (similar to how the Button Panel handles them). Each "???" triggers a text input popup asking the user to enter the text with which to replace the "???". The shortcut link can even define a caption to show for each "???" block. See exmples #13, #14, #15, #16 & #17.
Example id | Link text | Description |
---|---|---|
1 | `iscript: d100` | The link's display text is "d100". Each time the user clicks the link, the shortcut "d100" is run and the link's displayed text is set to the shortcut's expansion. |
2 | `iscript-once: d100` | Same as example #1, except the link is entirely replaced by the shortcut's expansion text, meaning only the first click has any effect. This is due to the "iscript-once" heading. |
3 | `iscript-append: d100` | Same as example #1, except the link's display text does not change. Instead, the shortcut's expansion text is appended to the end of the note. This is due to the "iscript-append" heading. |
4 | `iscript-prepend: d100` | Same as example #3, except the shortcut's expansion text is not appended to the end of the note. Instead, the shortcut's expansion text is prepended to the beginning of the note. This is due to the "iscript-prepend" heading. |
5 | `iscript: d100 | rollem` | Same as example #1, except that the link's initial display text is "rollem", instead of "d100". |
6 | `iscript-append: d100 | rollem` | Same as example #3, except that the link's display text begins and remains "rollem", instead of "d100". |
7 | `iscript ^dstblock: d100` | Sample as example #1, except that the note block with id of "dstblock" has its text set to the expansion, instead of the link's display. |
8 | `iscript-once ^dstblock: d100&` | Same as example #2, except that the note block with id of "dstblock" has its text set to the expansion, instead of the link's display. The link still goes away, though. |
9 | `iscript-append ^dstblock: d100` | Same as example #3, except that the note block with id of "dstblock" is appended to, instead of the entire note. |
10 | `iscript-prepend ^dstblock: d100` | Same as example #4, except that the note block with id of "dstblock" is prepended to, instead of the entire note. |
11 | `iscript: d100 | rollem | "Result: " + $$` | Same as example #5, except that custom JS code is included to print the expansion prepended with "Result: ". Notice that "$$" is replaced with the expansion in the JS code. |
12 | `iscript-append: d100 | rollem | "- " + $$ + "\n"` | Same as example #6, except that custom JS code is included to print the expansion prepended with a dash, and appended with a newline. If the expansion is "83/D100", then the output is "- 83/D100\n". Notice that "$$" is replaced with the expansion in the JS code |
13 | `iscript: d???` | Same as example #1, except that when the link is clicked, and before the shortcut is run, a popup asks to enter text to replace the "???" in the shortcut text. The popup has a caption of "Parameter #1". |
14 | `iscript: ???d???+???` | Same as example #13, except that not one, but THREE text entry popups are shown to replace the three "???" in the shortcut text. The popups have captions of "Parameter #1", "Parameter #2" and "Parameter #3", in that order. |
15 | `iscript: d??? | | | Enter the dice size` | Same as example #13, except that the text entry popup has the caption "Enter the dice size". |
16 | `iscript: ???d???+??? | | | Enter the dice count` | Same as example #14, except that the text entry popups are "Enter the dice count", "Parameter #2" and "Parameter #3". |
17 | `iscript: ???d???+??? | | | Enter the dice count | Enter the dice size | Enter the result modifier` | Same as example #14, except that the text entry popups are "Enter the dice count", "Enter the dice size" and "Enter the result modifier". |
If a new shortcut doesn't work and it's not clear why, then the javascript console can help.
If you are struggling with too much information in the console, you can always clear it. There's a button to do so on the top-left of the dev-tools panel. (see picture below)
If you have the console open (see The console), then you can pause an expansion mid-script, view the variables at that point, and then resume the script. You can do this by adding the statement "debugger;" to the script where you want to the expansion to pause. When the expansion is run and hits "debugger;", the script will pause.
If you want a nicer experience while developing a shortcut, you can surround the Expansion string in a "JavaScript fenced code block". For example, this Expansion string:
return "Hello! How are you?";
can be written as:
```js
return "Hello! How are you?";
```
Note: The `
characters (before the "js") are backticks, the character that typically shares a key with tilde (~).
The result of the expansion is the same for both Expansion strings above, even though the second uses a "JavaScript fenced code block".
Benefits to using a "JavaScript fenced code blocks":
Drawbacks:
You can also surround a Test string in a basic "fenced code block". This provides no syntax highlighting, but still prevents unwanted markdown formatting. For example, this Test string:
^date$
can be written as:
```
^date$
```
The fenced code block must be exact: ```js
for Expansion string and ```
for Test string. ```javascript
, ```JS
, or anything else will break the shortcut.
When running a shortcut, Inline Scripts blocks user input. This is signified by dimming the Obsidian UI. The input block is lifted when the shortcut is finished, but if a shortcut breaks wrong, sometimes, the input block is never lifted. One solution is to restart Obsidian, but a faster solution, especially while developing a shortcut, is to open the console and run the helper function _inlineScripts.inlineScripts.HelperFncs.unblock()
. This will immediately remove the input block, and allow the user to continue working with Obisidian.
InlineScripts provides various helper funcstions that can be useful in different situations. A few are so useful that they are available directly within shortcut scripts, like expand()
. Some helper functions are explained elsewhere in this readme, but here is a comprehensive list, along with their purpose:
_inlineScripts.inlineScripts.HelperFncs.addCss(id, css)
- See Using custom CSS for details._inlineScripts.inlineScripts.HelperFncs.addIcon(id, svg)
- Registers the string of svg {svg} as an icon with an id of {id}. This is useful for with the "ItemView" class._inlineScripts.inlineScripts.HelperFncs.addToNote(toAdd, targetPosition, path)
- Modifies a file. {toAdd} is the text to insert into the file. {path} is an optional path string, which defaults to the active note. It tells which file to modify. {targetPosition} is an optional range object {start: ? end: ?} for which characters to replace with {toAdd}. It defaults to both "start" and "end" being at the end of the document, meaning {toAdd} is appended. If the modified file is the active note, it gets the focus. If {toAdd} is appended to the modified file (targetPosition points to the end), then the inupt caret is moved to the end of the document._inlineScripts.inlineScripts.HelperFncs.asyncFilter(arr, fnc)
- Equivalent to array.filter()
, except that the passed function is run asynchronously._inlineScripts.inlineScripts.HelperFncs.asyncMap(arr, fnc)
- Equivalent to array.map()
, except that the passed function is run asynchronously._inlineScripts.inlineScripts.HelperFncs.asyncForEach(arr, fnc)
- Equivalent to array.forEach()
, except that the passed function is run asynchronously._inlineScripts.inlineScripts.HelperFncs.callEventListenerCollection(id, collection, parameters, onReturn)
- Takes an object filled with callback functions {collection} and calls each function. {id} is a string to add to any error that occurs (to help identify this event). {parameters} is an optional JS value. If provided, it is passed as a parameter to each function call. {onReturn} is an optional function. If provided, it is called when callEventListenerCollection is finished._inlineScripts.inlineScripts.HelperFncs.confirmObjectPath(path, leaf)
- Makes sure that the object chain described by {path} exists, where the first string is an object stored in the "window" variable, the next string is an object stored in the first string, etc. If any objects described by {path} do not exist, then they are created. The final name of {path} is assigned to the optional parameter {leaf}, which can be any JS value, and defaults to an empty object._inlineScripts.inlineScripts.HelperFncs.DragReorder(parent, onreorder)
- A class that can be instantiated to manage a drag-reordering feature in html elements. {parent} is the element that holds all elements to be reordered. {onreorder} is a function called after each drag-reorder occurs._inlineScripts.inlineScripts.HelperFncs.expand(shortcutText)
- Provided directly to shortcuts. See Calling one shortcut from another for details._inlineScripts.inlineScripts.HelperFncs.expFormat(expansion, skipPrefix, skipLinePrefix, skipSuffix)
- Provided directly to shortcuts. See Adding the common expansion format to your shortcuts (optional) for details._inlineScripts.inlineScripts.HelperFncs.expUnformat(expansion, skipPrefix, skipLinePrefix, skipSuffix)
- Provided directly to shortcuts. See Adding the common expansion format to your shortcuts (optional) for details._inlineScripts.inlineScripts.HelperFncs.getLeavesForFile(file)
- Takes a file object {file} and returns all the editor ui's that are open to that file in Obsidian._inlineScripts.inlineScripts.HelperFncs.getSettings()
- Returns a copy of the current settings object._inlineScripts.inlineScripts.HelperFncs.ItemView(id, css)
- An Obsidian class that can be extended to create your own side-panels_inlineScripts.inlineScripts.HelperFncs.parseMarkdown(md)
- Takes a string of markdown {md} and returns that string with all markdown converted to html._inlineScripts.inlineScripts.HelperFncs.popups
- Provided directly to shortcuts. See Popup boxes for user info and Creating custom Popup boxes for details._inlineScripts.inlineScripts.HelperFncs.print(toPrint)
- Provided directly to shortcuts. See The print() function for details._inlineScripts.inlineScripts.HelperFncs.registerView(id, viewMaker)
- The ItemView class can be subclassed to create custom Obsidian panels. Once you have your subclass, this function lets you register that subclass. It takes a unque string {id} to identify the Obsidian panel with the system, and {viewMaker} is a function that instantiates an instance of the class each time it's called._inlineScripts.inlineScripts.HelperFncs.removeCss(id)
- See Using custom CSS for details._inlineScripts.inlineScripts.HelperFncs.runExternal(command, failSilently, dontFixSlashes)
- Provided directly to shortcuts. See Running external applications and scripts for details._inlineScripts.inlineScripts.HelperFncs.unblock()
- See Removing Inline Script's input block for details._inlineScripts.inlineScripts.HelperFncs.fileWrite(filepath, content)
- Sets the content to the file at {filepath} to {content}, creating the file if necessary.Many shortcuts take advantage of a simple system that adds a "common expansion format" to their results. This is not required, but allows your shortcut's output to look pleasant and similar to other shortcut results.
expFormat(expansionString)
is a function that takes "expansionString" (a string or string array) and returns it, but adjusted it to adhere to the "common expansion format". A simple use-case is to adjust your shortcut by replacing return result;
with return expFormat(result);
.
expUnformat(expansionString)
is a counterpoint to expFormat(expansionString)
. It takes a string with the "common expansion format" added to it, and strips out the formatting. This is useful when you need to work with the data from a formatted expansion.
The "common expansion format" is defined in the settings, which allows the user to define how they want shortcuts to print into their notes. There are three entries in the "Common expansion format" section of the settings:
Shortcut expansion script | Expansion result |
---|---|
return "result"; | ... result ... |
return expFormat("result"); | ... > result\n\n ... |
Note: The "\n\n" in the results are displayed in the note as a blank line between the result and the following content. Note: The Second expansion result assumes that the "common expansion format" settings are at defaults.
The full definition of expFormat is actually expFormat(expansionString, skipPrefix, skipLinePrefix, skipSuffix)
. The final three parameters are optional.
print(message)
is a function that can be called from within a shortcut's Expansion script. It creates both a console entry and a popup notification that display the given message. It then returns the given message.
This feature is unavailable on mobile (Obsidian's backend doesn't allow it).
runExternal(command)
is a function that can be called from within a shortcut's Expansion string. It executes the command
parameter as a shell command and then returns the resulting console output. This lets executables and scripts (python, M, bash, etc.) be run from within Obsidian.
NOTE: The full function is actually runExternal(command, failSilently, dontFixSlashes)
. The second two parameters are optional and are explained later in this section.
Be aware that the runExternal function will always fail with an authorization error, unless the switch setting "Allow external" is turned on in the plugin options. It is off by default.
Test string | Expansion string | Overview |
---|---|---|
^test shell$ | return runExternal("echo Hello from the shell"); | When the user types ;;test shell:: , the shell command echo Hello from the shell is run, which prints "Hello from the shell" to the console. Once the echo command is finished, its console output is returned from runExternal, then that is returned from the Expansion script and, finally, expanded into the note. |
^runMyScript$ | return runExternal("python myscript.py"); | When the user types ;;runMyScript:: , the command will execute python on "myscript.py", which may print something to the console. Once the command is finished, runExternal will return any console output (or an empty string if there was none), which is then returned from the Expansion script and, thus, expanded into the note.If Python is setup properly, the Expansion script could have simply been return runExternal("myscript.py"); .If python is not installed, or myscript.py is not in the vault's root folder, or even if myscript.py has a python error, then the shell command will fail. This will cause runExternal to return null, and an error notification and console log to show up. |
^exec (.*)$ | let result = runExternal($1); if (result === null) { result = "FAILED"; } return "Shell command result = \"" + result + "\"."; |
This shortcut allows the user to run any shell command. For example, typing ;;exec dir:: will print the vault root-folder's contents and expand them into the note. |
When a command produces an error:
The second, optional, parameter of runExternal(command, failSilently, dontFixSlashes)
is "failSilently". When failSilently is true and the command produces an error, runExternal still returns null, but no notification or console error are created.
runExternal always runs commands at the vault's root folder. This allows you to run scripts that are within the vault, meaning the scripts can be copied/synced as part of the vault.
When runExternal is used to run a command, Obsidian will freeze until that command is completely finished. This can be disconcerting if you are not ready for it, but it is harmless... unless your command runs forever, of course.
By default, on Windows, any forward-slashes in the shell command are automatically flipped to back-slashes. This helps keep commands cross-platform (always use forward-slashes in commands). If this slash-flipping isn't wanted, though, runExternal(command, failSilently, dontFixSlashes)
third parameter, "dontFixSlashes" can be set to true to disable it.
Each shortcut has an About string (which may be empty). If the About string is not empty, it should start with a Syntax section, then a dash, then a description of the shortcut. The About string is used for user help and reference. The Syntax section is ALSO used by autocomplete and the button panel. IN FACT, a shortcut without a Syntax section will not show up in autocomplete at all.
Here's an example:
lipsum {paragraph count: >0, default: 1} - Generates a lorem ipsum text with {paragraph count} paragraphs.
In the example, lipsum {paragraph count: >0, default: 1}
is the Syntax section, while everything after the dash is the shortcut description.
The Syntax section shows what shortcut-text will trigger this shortcut. Any undetermined text is called a "shortcut parameter" and is represented by curly braces surrounding details of the shortcut parameter text. For example:
{paragraph count: >0, default: 1}`
shortcut parameter details always start with a parameter name. In this case paragraph count
. After the name is a colon, then a comma separated list. The first item of the list usually defines the data type. In this case >0
means an integer over 0. The second item, if included, is usually a default value. In this case default: 1
means that if the user does not include a value for the parameter text then "1" is used. If a default is given, then the shortcut parameter is optional. If no default is given then the shortcut parameter is required.
So, for this example, these are valid shortcut-texts to trigger this shortcut:
lipsum
lipsum 1
lipsum 10
while these are invalid shortcut-texts:
lipsum 0
lipsum 4.5
In addition to >0
, there are many other types used in the Inline Scripts library. Here is a list of the most common, along with their regex pattern.
Type string | Regex | Description |
---|---|---|
text | (.+) |
Any text including any spacing. |
non-space text | ([^ ]+) |
Same as "text", but no spaces allowed. |
name text | ([_a-zA-Z][_a-zA-Z0-9]*) |
A name: no space. The first character is an alpha or underscore character. The remaining characters can be alpha, underscore or number characters. |
path text | ("[^ \t\\:*?"<>\|][^\t\\:*?"<>\|]*"\|[^ \t\\:*?"<>\|]+) |
The shortcut expansion text should also start with the statement $1 = $1.replace(/^"(.*)"$/, "$1"); where the first two $1 (not the third) are set to whatever parameter this is. This statement removes any quotes the user puts around the value. |
>0 | ([1-9][0-9]*) |
Any integer greater than 0. |
>=0 | ([0-9]+) |
Any integer greater than or equal to 0. |
x TO z | (x\|y\|z) |
A range of integers, from x to z. Example: 3 TO 18 |
x OR y OR z | (x\|y\|z) |
A list of possible values. Example: red OR green OR blue. |
There are two features that work in tandem to allow you to nest shortcuts (i.e. use shortcut results as part of other shortcuts). The first is the ability for an Expansion script to return a string array. The second is the ability for an Expansion script to trigger another shortcut expansion, then get and use the result.
Firstly: an Expansion script typically returns a string. This string is what replaces the user-typed shortcut. An Expansion script can, instead, return an array of strings. This collection of strings gets joined into a single string when replacing a user-typed shortcut.
Secondly: within an Expansion script you can call the function expand(text)
. This function takes some text and tries to (a) find a matching shortcut (b) call the matching shortcut's Expansion script (c) get the Expansion script's result value (c) return that value. This works just like a shortcut text that you type directly into a note, except that expand(text)
returns the result (a string or string array), instead of writing the result into the note.
Given these features, here's how you can nest a shortcut within another. The first shortcut's Expansion script calls expand(), passing in the second shortcut. What it gets back is the second shortcut's Expansion result: a string or array of strings. It can then use that result, or a piece of that result, as needed.
id | Test string | Expansion string |
---|---|---|
1 | firstname | return ["FirstName: ", "Maggie"]; |
2 | lastname | return ["LastName: ", "Smith"]; |
3 | fullname | return [ "FullName: ", expand("firstname")[1], " ", expand("lastname")[1] ]; |
Notice that shortcut #1 returns an array of strings, but if you type ;;firstname::
, then the expansion resut is "FirstName: Maggie", since the array gets combined into a single string. This is true for shortcut #2 as well (expanding into "LastName: Smith").
If you type ;;fullname::
, the expansion is "FullName: Maggie Smith". This is because the array it returns is ["FullName: ", "Maggie", " ", "Smith"]. THIS is because the two calls to expand(text)
get the result from shortcuts #1 and #2, which are arrays, then the following [1]
gets the second string of the array.
If you add a shortcut with an empty Test string, then that shortcut is a "helper script". A helper script provides common code that any shortcuts listed after it can use.
If you add a shortcut with an empty Test string AND an empty Expansion string, then that shortcut is a "helper-blocker". A helper-blocker prevents any helper scripts above it from being available to any shortcuts after it. You probably won't need helper-blockers, but they are there in case you do. They are also inserted between shortcuts from different shortcut-files so that the helper scripts from one shortcut-file won't affect the shortcuts of any other files.
id | Test string | Expansion string |
---|---|---|
1 | hi | return "Hello! How are you?"; |
2 | function roll(x) { return Math.trunc(Math.random() * x) + 1; } | |
3 | d10 | return "Rolled " + roll(10) + " on a D10."; |
4 | d20 | return "Rolled " + roll(20) + " on a D20."; |
5 | ||
6 | bye | return "Goodbye. Thanks for your time!"; |
In this list of shortcuts, the shortcut #2 has an empty Test string. That means that it is a "helper script". The code in its Expansion string (a function called "roll") is available to shortcuts after it. Shortcut #5 is empty in both its Test AND Expansion strings. That means that it is a "helper-blocker". Shortcuts after it do not have access to helper scripts before it. The net effect is that shortcuts #3 and #4 have access to the helper script, while shortcuts #1 and #6 do not.
There are a handful of functions that have been useful to the point where I thought it'd be good to document them here.
Returns a random number (evenly distributed) from 1 to "max".
function roll(max)
{
return Math.trunc(Math.random() * max + 1);
}
Returns a random value (evenly distributed) from "a", an array of values.
function aPick(a)
{
return a[roll(a.length)-1];
}
Returns a weighted random value from "a", an array of values. Each item in the array is, itself, array containing the wanted value (usually the first element) and one or more weights (usual the elements after the first). A number is randomliy picked (or "theRoll" is used if it is provided), and the first item that matches the number is returned.
aPickWeight([ ["red", 3], ["blue", 10], ["green", 20] ])
- picks a random number from 1 to 20 (the weight of the last item). If the number is at or below 3, the first item is returned. If the number is from 4 to 10, the second item is returned. If the number is from 11 to 20, the third item is returned.aPickWeight([ ["red", 3, 2], ["blue", 10, 40], ["green", 20, 45] ], 2)
- same as the prior example, except that "wIndex" is 2, so the second number is used for picking heights. Pick ranges are 1 to 2 for the first item, 3 to 40 for the second item and 41 to 45 for the third item.aPickWeight([ ["red", 3, 2], ["blue", 10, 40], ["green", 20, 45] ], 2, 32)
- same as the the prior example, except that the parameter "theRoll" is 32. This means that the second item is always returned, since "theRoll" is inside the item's range (3 to 40, based on the second numbers of the items).
function aPickWeight(a, wIndex, theRoll)
{
wIndex = wIndex || 1;
theRoll = theRoll || roll(a.last()[wIndex]);
for (const item of a)
{
if (item[wIndex] >= theRoll)
{
return item;
}
}
return a.last();
}
A shortcut-file can contain a "setup script". A setup script is defined as a shortcut with a specific Test string of ^sfile setup$
. A setup script will run whenever the shortcut-file is loaded, including when switching notes while in "Developer mode". This feature is useful if your shortcut-file requires initialization before its shortcuts will work. Also, if the setup script returns true (or something evaluating to true), this signals that the shortcut-file should NOT be loaded: the shortcut-file's shortcuts will not be available.
A shortcut-file can contain a "shutdown script". A shutdown script is defined as a shortcut with a specific Test string of ^sfile shutdown$
. A shutdown script will run when a shortcut-file is being disabled: when it is removed from the shortcut-file list, when it is "turned off" in the shortcut-file list, or when Inline Scripts is being disabled or uninstalled. This feature is useful if your shortcut-file needs to clean-up when being disabled.
Test string | Expansion string | Overview |
---|---|---|
^sfile setup$ | const confirmObjectPath = _inlineScripts.inlineScripts.helperFncs. confirmObjectPath; confirmObjectPath("_inlineScripts.state"); inlineScripts.state.flag ||= 1; |
This setup script creates some global variables that shortcuts in the shortcut-file presumably rely upon. Notice that the setup script ONLY creates the global variables if they don't yet exist ( \|\|= ). This is important as a setup script may be run many times during a session. We don't want later runs to wipe out anything that was created earlier.This script also uses confirmObjectPath, a function provided by Inline Scripts to make sure that a specific object-within-object chain exists. |
^sfile shutdown$ | delete _inlineScripts.state; | This shutdown script removes a variable that was created by the setup script. |
Sometimes an Expansion script needs information from the user that is not feasable using a shortcut's parameters. This can include getting confirmation before a dangerous action, having the user select from a list of options or asking for too much information to reasonably type up in shortcut text. Popup panels can help. There are 4 kinds of popup panels:
Usage is simple: add one of the above statements into an Expansion text. You should also use the return value of the statement in some way, except when using the alert popup.
popup.alert("Don't forget to save!")
if (popup.confirm("Are you sure?")) { doIt(); }
let userName = popup.input("What is your name?", "John Smith");
let color = popup.pick("What is your favorite?", [ "red", "orange", "yellow", "green", "blue", "indigo", "violet" ], 4);
With some effort, and html/js skill, you can create your own custom popup boxes. This can be useful if you need something more complicated than an alert, confirm, input or pick popup box. Here's the syntax:
The definition object for a custom popup box can include any of the following properties. They are all optional:
[ "Ok", "Cancel" ]
.popups.custom()
. This is also passed to onClose()
so it can be used to transfer data from onOpen()
to onClose()
.popups.custom()
. This is also passed to onOpen()
so it can be used to transfer data from onOpen()
to onClose()
.popups.custom()
call. Pass it the value you want popups.custom()
to return.const definition =
{
buttons: [ "Ok" ],
onOpen: function(data, parent, firstButton, SettingType)
{
let s = document.createElement("select");
s.appendChild(new Option("Fighter", "strong"));
s.appendChild(new Option("Mage", "smart"));
s.appendChild(new Option("Rogue", "nimble"));
s.selectedIndex = data.defaultValue || 0;
s.classList.add("dropdown");
s.style["margin-bottom"] = "1.5em";
s.addEventListener("keypress", (e) =>
{
if (e.key === "Enter")
{
firstButton.click();
}
});
data.resultUi = s;
parent.appendChild(s);
},
onClose: function(data, resolveFnc, buttonText)
{
if (buttonText)
{
resolveFnc(data.resultUi.value);
}
else
{
resolveFnc(null);
}
}
};
result = popups.custom("Pick a character", definition, { defaultValue: 1 });
return result;
Shortcut Expansion scripts have access to a varible expansionInfo
which contains information about the current expansion. expansionInfo includes:
expand(x)
.If you aren't familiar with the "async" and "await" keywords in JavaScript, this section will not make much sense to you. You can most-likely skip it and be ok.
Shortcut Expansion scripts are run asynchronously. You can safely add "await" before asynchronous statements. In addition, "async" and "await" are automatically prepended to certain statements. This allows a novice JS scripter to write shortcuts without needing to understand asynchronicity. Here are details about when this automatic prepending occurs.
const a = function() { }
function a() { }
const a = () => { }
If your shortcut-file needs custom CSS, then you can use the "addCss()" and "removeCss()" functions. Put "_inlineScripts.inlineScripts.helperFncs.addCss()" into the "^sfile setup$" shortcut and "_inlineScripts.inlineScripts.helperFncs.removeCss()" into the "^sfile shutdown$" shortcut of your shortcut-file. addCss() takes an id (usually the shortcut-file's name) and a string of css and adds the css to Obsidian. removeCss() takes the id passed to addCss() and removes the css that was added by addCss().
__
^sfile setup$
__
_inlineScripts.inlineScripts.helperFncs.addCss("example", ".cm-scroller { background-color: blue; }");
__
Setup this shortcut-file
__
^sfile shutdown$
__
_inlineScripts.inlineScripts.helperFncs.removeCss("example");
__
Shutdown this shortcut-file
If the Syntax string at the start of a shortcut's About string is the text "hidden", then that shortcut will not show up in the help system (the "ref" shortcuts), though it can still be called within a note. This is helpful to prevent cluttering the help system with shortcuts that are not useful to the user, only to other shortcuts.
Inline Scripts has a few events that shortcuts can react to: onExpansion and onShortcutsLoaded. To react to one of these events, add a callback function to the object window._inlineScripts.inlineScripts.listeners.X
(where "X" is the name of the event). Add the callback under the key that is identical to your shortcut-file. Note that that object must be created if it doesn't exist. See below for examples.
If a shortcut-file needs to react to shortcut expansions, it can setup a callback function to be called on such an event. The callback can take one parameter: expansionInfo
. See Getting info about the current expansion for details on expansionInfo
.
In addition, if the callback function returns a string, then that string is expanded as a shortcut and the result replaces the old expansion.
__
^sfile setup$
__
const confirmObjectPath =
_inlineScripts.inlineScripts.helperFncs.confirmObjectPath;
confirmObjectPath(
"_inlineScripts.inlineScripts.listeners." +
"onExpansion.testCallback",
(expansionInfo) =>
{
if (expansionInfo.shortcutText.contains("d"))
{
// Force expansion to be result of the "hi" shortcut
return "hi";
}
else
{
print(
"Shortcut input '" +
expansionInfo.shortcutText +
"' expanded to '" +
expansionInfo.expansionText + "'.");
}
});
__
__
^sfile shutdown$
__
delete _inlineScripts.inlineScripts?.listeners?.
onExpansion?.testCallback;
__
See onExpansion event above for details. This event works the same, except that it occurs each time the full list of shortcuts is loaded from all registered shortcut-files. Also, it passes no parameters into the callback functions.
See the Changelist doc
See the todo doc for changes planned for future releases.