jon-heard / obsidian-inline-scripts

Obsidian plugin: Type text shortcuts that expand into javascript generated text.
MIT License
134 stars 4 forks source link

Obsidian Plugin - Inline Scripts (open beta)

Demo animation


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.


Table of contents


Overview

The Inline Scripts plugin replaces typed shortcuts with "expansion" text generated by JavaScript, for example:

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.


REFERENCE: Settings


User support, bugs, feedback, donations, etc.

If you...

... then visit the discussions page.

Donations

If you find this plugin useful, then a donation lets me know
that I should keep working to make it better.

paypal Buy Me A Coffee



TUTORIAL: Setup the plugin and try it out

Setup the plugin

  1. Open the vault you want to install the plugin Inline Scripts into. If you don't yet have a vault ready, create one now.
  2. Open the Community plugins settings.
    1. Click the settings button at the lower-left of the Obsidian window (It looks like a gear).
    2. Click Community plugins in the left-hand menu (at the bottom of the Options section).
    3. If necessary, turn on community plugins (i.e. turn off "Restricted mode").
  3. Browse community plugins, find Inline Scripts and select it.
    1. Click the Browse button to the right of Community plugins.
    2. In the panel that pops up, click on the search textbox at the upper-left and type "inline scripts".
    3. Click "Inline Scripts" in the list below the search textbox.
  4. Install and enable Inline Scripts
    1. To the right of the menu is information about Inline Scripts. This includes a group of buttons near the top
    2. If one of the buttons is "Install", then click on that button and wait for it to go away.
    3. If one of the buttons is "Enable", then click on that button and wait for it to go away.
    4. Close the popup panels by clicking on the dark area outside of them. Repeat until you are back to viewing your notes.

Try out the plugin

  1. Open a note to try out the plugin.
  2. In the note, type ;;hi::. Notice that the shortcut expands to "Hello! How are you?" as soon as you've finished typing it.
  3. In the note, type ;;d100::. Notice that the shortcut expands to a roll-result as soon as you've finished typing it.
  4. Repeat step 3. Note that the roll result is (probably) different. If it is not different, then you got lucky. Try step 3 again.

Default shortcut samples

Inline Scripts comes with the following sample shortcuts defined by default:

Help shortcuts

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.



TUTORIAL: Create a new shortcut

Shortcut components

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 ^age ([0-9]+)$ 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.

Shortcut #1 - hi (basic)

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.

Shortcut #2 - date (intermediate)

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$.

Shortcut #3 - age (advanced)

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.

Step-by-step: Adding a shortcut

  1. Make sure that the Inline Scripts plugin is installed and enabled in your vault. (see the tutorial Setup the plugin and try it out.)

  2. Open the settings for the Inline Script plugin.

    1. click the settings button on the lower-left of the Obsidian window. This opens the settings panel.
    2. In the left-side menu of the settings panel, find and click Inline Scripts. It is beneath "Community plugins", near the bottom. This opens the settings for Inline Scripts.
  3. Go down to the "Shortcuts" setting. It's just after "Shortcut-files". (see picture below)

  4. 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)

  5. 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)

    Shortcuts

  6. Close the settings panel.

    • You can hit the X button on the top right of the settings panel to close it.
    • You can click outside of the settings panel to close it.
  7. Try typing your new shortcut into a note to make sure it works. Example: type ;;test::



TUTORIAL: Add existing shortcut-files to a vault

A warning

Shortcuts, by their JavScript nature, have a risk of being malicious. Make sure you trust a shortcut or shortcut-file before using it.

Shortcut-file sources

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.)

Step-by-step: Importing the entire shortcut-file library to the vault

  1. Make sure that the Inline Scripts plugin is installed and enabled in your vault. (see the tutorial Setup the plugin and try it out.)

  2. Open the settings for the Inline Scripts plugin.

    1. click the settings button on the lower-left of the Obsidian window. This opens the settings panel.
    2. In the left-side menu of the settings panel, find and click Inline Scripts. It is beneath "Community plugins", near the bottom. This opens the settings for Inline Scripts.
  3. Find the "Shortcut-files" setting. It is just beneath "Shortcut Sources".

  4. 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.

    Import full library

  5. 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.

    • You can hit the X button on the top right of the settings panel to close it.
    • You can click outside of the settings panel to close it.
  6. 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::.

  7. You can type ;;help:: to start learning about all of the shortcuts provided by the library.

  8. 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.

Step-by-step: Adding a SINGLE shortcut-file to the vault

  1. Make sure that the Inline Scripts plugin is installed and enabled in your vault. (see the tutorial Setup the plugin and try it out.)

  2. Get the contents of a shortcut-file into a note in your vault. You can do this in one of two ways.

    • Copy the shortcut-file's text content into an empty note.
      • If the shortcut-file is on github, I suggest copying the contents of the "raw file".
    • Copy the shortcut-file directly into your vault's folder.
  3. 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.

    • Example: support/inlineScripts/state.sfile. The name of this shortcut-file note is state.sfile, the folder-path is support/inlineScripts.
  4. Open the settings for the Inline Scripts plugin.

    1. click the settings button on the lower-left of the Obsidian window. This opens the settings panel.
    2. In the left-side menu of the settings panel, find and click Inline Scripts. It is beneath "Community plugins", near the bottom. This opens the settings for Inline Scripts.
  5. Add a reference to the shortcut-file.

    1. Find the "Shortcut-files" setting. It is just beneath "Shortcut Sources" (see picture below).
    2. In the "Shortcut-files" setting, click the "Add shortcut-file" button on the right side. This adds an empty entry to the bottom of the list. The entry should show the word "Filename" in grey text. (see picture below)
    3. Click on the textbox, then type in the shortcut-file note's address, determined in step 3. The textbox will be red until a valid note address has been entered. (see picture below)
      • Example: support/inlineScripts/state.sfile.
    4. (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.

      Shortcut-files setting

  6. Close the settings panel.

    • You can hit the X button on the top right of the settings panel to close it.
    • You can click outside of the settings panel to close it.
  7. The shortcuts defined in the shortcut-file should now work. Try typing one of the shortcuts to confirm this.

  8. You can type ;;help:: to start learning about all of the shortcuts provided by the new shortcut-file.



TUTORIAL: Create a new shortcut-file

NOTE: If you make a shortcut-file you think others would like, it'd be real nice if you could share it here! If it is polished and generally useful, then I'll even add it to the library of shortcut-files.

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).

Examples

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.

Developer mode

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.

Developer mode

Help shortcuts

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.



TUTORIAL: Use the buttons panel to run shortcuts

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).

An overview of the buttons panel user interface.

Button Panel

  1. Button ui: Button help - Toggle "Help mode". While in help mode, clicking any shortcut button (#6) will show help text related to the button.
  2. Button ui: Add button - Add a shortcut button to the list.
  3. Button ui: Edit button - Toggle "Edit mode". While in edit mode, clicking any shortcut button (#6) will open a panel for editing the button.
  4. Button ui: Re-order buttons - Toggle "Reordering mode". While in rordering mode, dragging a shortcut button (#6) will allow placing it elsewhere in the list of shortcut buttons.
  5. Button ui: Remove button - Toggle "Removing mode". While in removing mode, clicking a shortcut button (#6) will confirm, then remove the button.
  6. Button ui: The shortcut buttons - All shortcut buttons in the current group (#7) show up here.
  7. Group ui: Group selector - A dropdown list where you can select the current button group. Each button group is a list of shortcut buttons (#6).
  8. Group ui: Add group - Adds a new group with a generic name.
  9. Group ui: Rename group - Asks the user to enter a new name for the current group.
  10. Group ui: Import / export group - Brings up a string of text that holds all data for the current group's buttons. To "export" this group, copy this string of text. To "import" this group, replace the string of text with a different string representing different buttons.
  11. Group ui: Remove group - Clicking will confirm, then remove the current group.
  12. Groups: Custom groups - You can create custom groups. They start empty, but you can import buttons from a different group, and/or create custom buttons.
  13. Groups: shortcut-file groups - Each shortcut-file that is registered with the Inline Scripts plugin has a button group created for it automatically. The group includes one button for each shortcut in the shortcut-file.
  14. Shortcut-file group selected - If you select a shortcut-file group, much of the ui becomes locked. To modify the group's buttons, first export them into a custom group.
  15. Shortcut-file group options locked - When a shortcut-file group is selected, it can't be renamed or removed, and none of the Button ui buttons are available, except for the help button.

A shortcut button's options

ShortcutButtonSettings

  1. Display text - The text to be displayed on the front of the button
  2. Shortcut text - What will be run as a shortcut when the button is clicked. Can include "???" blocks, each of which will be resolved with a text-input popup asking the user what to replace the "???" with. Once all "???" are replaced, the shortcut is run.
  3. Help text - Help text to show when the user clicks on the button while in "Help mode".
  4. Add parameter details - Each time this is clicked, another "Parameter details" row is added to the list (#5).
  5. Parameter details - A list of settings for "???" blocks in the Shortcut text (#2). Each entry contains settings for one "???" block (going left to right). The Default value is what value shows up initially in the popup, to be replaced by whatever the user types. The Caption is what text to show to explain what the user needs to enter. Any blank entries are ignored.
  6. Ok/Cancel buttons - When all info is entered, or edited, to satisfaction click the "Ok" button. To discard the current Add or Edit action, click "Cancel".


TUTORIAL: Add links to a note to run shortcuts

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.

Examples

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".


DEVELOPMENT AID: The console

If a new shortcut doesn't work and it's not clear why, then the javascript console can help.

  1. Type ctrl-shift-i to open the dev-tools panel. (see picture below)
  2. Click on the "Console" tab at the top of the dev-tools panel. (see picture below)
  3. Review the console contents for a clue as to what is going wrong with the shortcut. (see picture below)
  4. Try typing the shortcut into a note while the console is open to see if an error is added to the console. You can review the error message for a clue as to what's wrong.
  5. 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)

    Console


DEVELOPMENT AID: The debugger statement

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.


DEVELOPMENT AID: Fenced code blocks

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:

Fencing test strings

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$

```

Warning

The fenced code block must be exact: ```js for Expansion string and ``` for Test string. ```javascript, ```JS, or anything else will break the shortcut.


DEVELOPMENT AID: Removing Inline Script's input block

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.

DEVELOPMENT AID: Helper functions

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:


ADVANCED SHORTCUTS: Adding the common expansion format to your shortcuts (optional)

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:

Examples

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.

Optional parameters

The full definition of expFormat is actually expFormat(expansionString, skipPrefix, skipLinePrefix, skipSuffix). The final three parameters are optional.


ADVANCED SHORTCUTS: The print() function

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.


ADVANCED SHORTCUTS: Running external applications and scripts

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.

The "Allow external" setting

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.

Allow external setting

Examples

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.

Command errors

When a command produces an error:

  1. The runExternal call returns null (instead of the console output)
  2. A popup notification tells the user that an error has occurred
  3. A console error provides detailed information:
    • The folder that the command was run from (always the vault's root folder)
    • The command that failed
    • The error message provided by the shell

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.

The working folder for commands

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.

Obsidian pauses until a command completes

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.

Cross-platform slashes

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.


ADVANCED SHORTCUTS: Syntax strings and regex patterns

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.

ADVANCED SHORTCUTS: Calling shortcuts from shortcuts

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.

Returning string arrays

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.

Calling one shortcut from another

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.

Nesting shortcuts

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.

Example

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.


ADVANCED SHORTCUTS: Helper scripts

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.

Helper-blockers

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.

Example

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.


ADVANCED SHORTCUTS: Various useful functions

There are a handful of functions that have been useful to the point where I thought it'd be good to document them here.

roll(max)

Returns a random number (evenly distributed) from 1 to "max".

    function roll(max)
    {
        return Math.trunc(Math.random() * max + 1);
    }

aPick(a)

Returns a random value (evenly distributed) from "a", an array of values.

    function aPick(a)
    {
        return a[roll(a.length)-1];
    }

aPickWeight(a, wIndex, theRoll)

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.


ADVANCED SHORTCUTS: Setup and shutdown scripts

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.

Example

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.

ADVANCED SHORTCUTS: Popup boxes for user info

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.

Examples


ADVANCED SHORTCUTS: Creating custom Popup boxes

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:

Example

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;

ADVANCED SHORTCUTS: Getting info about the current expansion

Shortcut Expansion scripts have access to a varible expansionInfo which contains information about the current expansion. expansionInfo includes:


ADVANCED SHORTCUTS: async and await

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.

async


ADVANCED SHORTCUTS: Using custom CSS

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().

Example

__
^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

ADVANCED SHORTCUTS: Hiding shortcuts

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.


ADVANCED SHORTCUTS: Plugin events

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.

onExpansion event

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.

Example shortcut-file

__
^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;
__

onShortcutsLoaded event

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.


Credits


Release notes

See the Changelist doc


Todo

See the todo doc for changes planned for future releases.