{
  "commands": [
    {
      "Name": "combinepath",
      "Description": "Combines and formats the specified folder and file names of the input as a filesystem path.",
      "Syntax": "combinepath [<folder or file name>(,?)]",
      "Arguments": [
        "The list of folder and file names to combine and format into a filesystem path."
      ],
      "Return": "The correctly formatted path built out of all the folder and file names.",
      "Category": "File"
    },
    {
      "Name": "log",
      "Description": "Logs some text to the dedicated script log file, or optionally, a text file of choice starting at CoyoteMIDI/logs/scripts/.",
      "Syntax": "log [<text>] (<file path>)",
      "Arguments": [
        "The message to be logged.",
        "The name of the file to log to. By default, this will be a shared script log file found at CoyoteMIDI/logs/scriptslog.txt"
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "writefile",
      "Description": "Writes to the specified file.",
      "Syntax": "writefile [<text>] [<target file>] (<overwrite file?>)",
      "Arguments": [
        "The text to write to the file.",
        "The absolute path of the file to write the text to.",
        "True or false for whether the target location should be overwritten if it already exists. The default value is false."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "if",
      "Description": "Checks the specified conditions before allowing the following block of commands to run or not.",
      "Syntax": "if [<value>] (<operator>) (<value>) (&&/||) (?)",
      "Arguments": [
        "The value to be compared with something else, or the condition in the form of true or false directly.",
        "The comparison operator to be used between the previous and following value. This parameter can be optional if the first provided value returns true or false by itself.",
        "The value to be compared with the previous provided value. This parameter can be optional if the first provided value returns true or false by itself.",
        "The logical operator to be used between any comparisons in the case of multiple comparisons in one if statement.",
        "Additional comparisons may follow."
      ],
      "Return": null,
      "Category": "Logic"
    },
    {
      "Name": "else",
      "Description": "Catches an if statement that does not pass and encapsulates a block of commands to run in such an event.",
      "Syntax": "else (if) (<value>) (<operator>) (<value>) (&&/||) (?)",
      "Arguments": [
        "Specify 'if' to start a second if statement when this else command is ran.",
        "The value to be compared with something else, or the condition in the form of true or false directly.",
        "The comparison operator to be used between the previous and following value. This parameter can be optional if the first provided value returns true or false by itself.",
        "The value to be compared with the previous provided value. This parameter can be optional if the first provided value returns true or false by itself.",
        "The logical operator to be used between any comparisons in the case of multiple comparisons in one if statement.",
        "Additional comparisons may follow."
      ],
      "Return": null,
      "Category": "Logic"
    },
    {
      "Name": "repeat",
      "Description": "Repeats a block of commands the specified amount of times.",
      "Syntax": "repeat [<#>]",
      "Arguments": [
        "The amount of times that the following block of commands should be executed."
      ],
      "Return": null,
      "Category": "Logic"
    },
    {
      "Name": "foreach",
      "Description": "Repeats a block of commands for every entry in a collection with a variable to read the current entry.",
      "Syntax": "foreach [<variable>(:<variable>)] [in] [<list>/<map>]",
      "Arguments": [
        "The variable to put the current entry that is the block of script is being executed for in. If the input is a map instead of a list, a second variable can be specified for storing the value of the map entry at the current iteration.",
        "Required literal ?in? text",
        "The list or map to perform the foreach on."
      ],
      "Return": null,
      "Category": "Logic"
    },
    {
      "Name": "while",
      "Description": "Repeats the following block of commands indefinitely while a condition is met.",
      "Syntax": "while [<value>] (<operator>) (<value>) (&&/||) (?)",
      "Arguments": [
        "The value to be compared with something else, or the condition in the form of true or false directly.",
        "The comparison operator to be used between the previous and following value. This parameter can be optional if the first provided value returns true or false by itself.",
        "The value to be compared with the previous provided value. This parameter can be optional if the first provided value returns true or false by itself.",
        "The logical operator to be used between any comparisons in the case of multiple comparisons in one if statement.",
        "Additional comparisons may follow."
      ],
      "Return": null,
      "Category": "Logic"
    },
    {
      "Name": "return",
      "Description": "Sets the return value of the script. This can be retrieved in a runscript command that ran the script.",
      "Syntax": "return (<value>)",
      "Arguments": [
        "The value for the script to return. If no value is specified, it is simply used as a signal to stop the script block from continuing."
      ],
      "Return": null,
      "Category": "Script"
    },
    {
      "Name": "switch",
      "Description": "Runs the block of script from the case statement that matches the input.",
      "Syntax": "switch [<value>]",
      "Arguments": [
        "The value to run the case statement of."
      ],
      "Return": null,
      "Category": "Logic"
    },
    {
      "Name": "case",
      "Description": "Defines the condition for a block of script within a switch statement.",
      "Syntax": "case [<value>/default(, <value>, ...)]",
      "Arguments": [
        "The value that identifies this case statement. 'default' may be used to make it the default case if no case matches the input value. Multiple values may be specified separated by ','."
      ],
      "Return": null,
      "Category": "Logic"
    },
    {
      "Name": "checkbox",
      "Description": "Defines a checkbox based script parameter.",
      "Syntax": "checkbox [<name>] [<description>] (<default value>)",
      "Arguments": [
        "The name of the script parameter to be displayed next to the checkbox.",
        "The description of the script parameter to be displayed in the tooltip.",
        "The default value to populate the checkbox with."
      ],
      "Return": "The map that describes the checkbox that was defined.",
      "Category": "UI"
    },
    {
      "Name": "combobox",
      "Description": "Defines a combobox based script parameter.",
      "Syntax": "combobox [<name>] [<description>] [<options>] (<default value>) (<width>)",
      "Arguments": [
        "The name of the script parameter to be displayed next to the combobox.",
        "The description of the script parameter to be displayed in the tooltip.",
        "The options available in the combobox.",
        "The default value to populate the combobox with.",
        "The width of the input combobox as a percentage of the total allocated width for the entire control, ranging from 0 to 1."
      ],
      "Return": "The map that describes the combobox that was defined.",
      "Category": "UI"
    },
    {
      "Name": "configurationparameter",
      "Description": "Adds a parameter input field to be shown as part of a configuration window for this script.",
      "Syntax": "configurationparameter [<id>] [<control>]",
      "Arguments": [
        "The ID under which the parameter's value will be mapped in the parameters included in the event that is triggered when the configuration is changed.",
        "The map that describes the parameter control, generated by a command that defines a control, like the textbox or checkbox commands."
      ],
      "Return": null,
      "Category": "UI"
    },
    {
      "Name": "inputwindow",
      "Description": "Displays a new window with the provided input fields to allow a script to take direct input data from a user.",
      "Syntax": "inputwindow [<title>] [<id>:<control>,...] (<position>)",
      "Arguments": [
        "The title of the window input window",
        "The map that describes the input controls, where each key is the ID of the control, and the value is the map describing the control, as generated by a command such as the textbox command.",
        "The position at which the input window should be shown. By default, this is centered on top of the app's main window. Valid options are: Center, AbsoluteCenter, TopLeft, and Pointer."
      ],
      "Return": "The map with the values of the input controls, with the provided IDs as keys and the user specified inputs at the time of when the window was closed as values.",
      "Category": "UI"
    },
    {
      "Name": "numberbox",
      "Description": "Defines a numberbox based script parameter.",
      "Syntax": "numberbox [<name>] [<description>] (<default value>) (<width>) (<allow decimals?>) (<min value>) (<max value>) (<increment>)",
      "Arguments": [
        "The name of the script parameter to be displayed next to the numberbox.",
        "The description of the script parameter to be displayed in the tooltip.",
        "The default value to populate the numberbox with.",
        "The width of the input numberbox as a percentage of the total allocated width for the entire control, ranging from 0 to 1.",
        "Whether the numberbox should allow decimal values or not.",
        "The minimum allowed value inside the numberbox.",
        "The maximum allowed value inside the numberbox.",
        "The amount with which the numberbox should increment when the up or down buttons are clicked."
      ],
      "Return": "The map that describes the numberbox that was defined.",
      "Category": "UI"
    },
    {
      "Name": "textblock",
      "Description": "Defines a textblock based script parameter. While classified as a script parameter, this element does not actually take an input from the user, and is only used to display text.",
      "Syntax": "textblock [<text>] [<description>]",
      "Arguments": [
        "The text of the script parameter to be displayed.",
        "The description of the script parameter to be displayed in the tooltip."
      ],
      "Return": "The map that describes the textblock that was defined.",
      "Category": "UI"
    },
    {
      "Name": "textbox",
      "Description": "Defines a textbox based script parameter.",
      "Syntax": "textbox [<name>] [<description>] (<default value>) (<width>)",
      "Arguments": [
        "The name of the script parameter to be displayed next to the textbox.",
        "The description of the script parameter to be displayed in the tooltip.",
        "The default value to populate the textbox with.",
        "The width of the input textbox as a percentage of the total allocated width for the entire control, ranging from 0 to 1."
      ],
      "Return": "The map that describes the textbox that was defined.",
      "Category": "UI"
    },
    {
      "Name": "animatetranslation",
      "Description": "Animates a translation's UI elements as if it was triggered.",
      "Syntax": "animatetranslation [<translation ID>]",
      "Arguments": [
        "The ID of the translation to animate. This is included with the trigger variable map in scripts ran by a translation."
      ],
      "Return": null,
      "Category": "Translations"
    },
    {
      "Name": "getselectedtranslationprofile",
      "Description": "Returns the currently selected translation profile.",
      "Syntax": "getselectedtranslationprofile",
      "Arguments": [],
      "Return": "The name of the currently selected translation profile.",
      "Category": "Translations"
    },
    {
      "Name": "gettranslationfolders",
      "Description": "Returns a map of existing translation folders.",
      "Syntax": "gettranslationfolders",
      "Arguments": [],
      "Return": "A map of the existing translation folders, where the key is the translation folder ID, and the value is a map describing the folder.",
      "Category": "Translations"
    },
    {
      "Name": "gettranslationprofiles",
      "Description": "Returns a map of existing translation profiles.",
      "Syntax": "gettranslationprofiles",
      "Arguments": [],
      "Return": "A map of the existing translation profiles, where the key is the profile name, and the value is a map describing the profile.",
      "Category": "Translations"
    },
    {
      "Name": "gettranslations",
      "Description": "Returns a map of existing translations.",
      "Syntax": "gettranslations",
      "Arguments": [],
      "Return": "A map of the existing translations, where the key is the translation ID, and the value is a map describing the translation.",
      "Category": "Translations"
    },
    {
      "Name": "selectedtranslationprofile",
      "Description": "Switches the selected translation profile to the specified profile.",
      "Syntax": "selectedtranslationprofile [<profile name>]",
      "Arguments": [
        "The name of the translation profile to switch to."
      ],
      "Return": null,
      "Category": "Translations"
    },
    {
      "Name": "translationactive",
      "Description": "Sets whether or not a translation is active.",
      "Syntax": "translationactive [<translation ID>] [<active?>]",
      "Arguments": [
        "The ID of the translation to change the active state of.",
        "Whether or not the translation should be active, as true or false."
      ],
      "Return": null,
      "Category": "Translations"
    },
    {
      "Name": "translationfolderactive",
      "Description": "Sets whether or not a translation folder is active.",
      "Syntax": "translationfolderactive [<translation folder ID>] [<active?>]",
      "Arguments": [
        "The ID of the translation folder to change the active state of.",
        "Whether or not the translation folder should be active, as true or false."
      ],
      "Return": null,
      "Category": "Translations"
    },
    {
      "Name": "translationparameter",
      "Description": "Adds a parameter input field in the advanced settings of any translation configured to use this script.",
      "Syntax": "translationparameter [<id>] [<control>] (<subscript>)",
      "Arguments": [
        "The ID under which the parameter's value will be mapped in the included parameter values when the script is ran.",
        "The map that describes the parameter control, generated by a command that defines a control, like the textbox or checkbox commands.",
        "The subscript for which this parameter should show."
      ],
      "Return": null,
      "Category": "Translations"
    },
    {
      "Name": "osc",
      "Description": "Sends an OSC message to the output OSC ports selected in CoyoteMIDI or the otherwise specified ports.",
      "Syntax": "osc [<address>] (<port>,...) (<argument list>)",
      "Arguments": [
        "The address of the OSC message.",
        "The ports to send the OSC message to. Multiple ports can be listed. The default values are the ports selected as output ports in the OSC settings.",
        "The list of arguments to include with the OSC message."
      ],
      "Return": null,
      "Category": "OSC"
    },
    {
      "Name": "getmodifierfolders",
      "Description": "Returns a map of existing modifier folders.",
      "Syntax": "gettranslationfolders",
      "Arguments": [],
      "Return": "A map of the existing modifier folders, where the key is the modifier folder ID, and the value is a map describing the folder.",
      "Category": "Modifiers"
    },
    {
      "Name": "getmodifiers",
      "Description": "Returns a map of existing modifiers.",
      "Syntax": "getmodifiers",
      "Arguments": [],
      "Return": "A map of the existing modifiers, where the key is the modifier ID, and the value is a map describing the modifier.",
      "Category": "Modifiers"
    },
    {
      "Name": "modifieractive",
      "Description": "Sets whether or not a modifier is active.",
      "Syntax": "modifieractive [<modifier ID>] [<active?>]",
      "Arguments": [
        "The ID of the modifier to change the active state of.",
        "Whether or not the modifier should be active, as true or false."
      ],
      "Return": null,
      "Category": "Modifiers"
    },
    {
      "Name": "modifierfolderactive",
      "Description": "Sets whether or not a modifier folder is active.",
      "Syntax": "modifierfolderactive [<modifier folder ID>] [<active?>]",
      "Arguments": [
        "The ID of the modifier folder to change the active state of.",
        "Whether or not the modifier folder should be active, as true or false."
      ],
      "Return": null,
      "Category": "Modifiers"
    },
    {
      "Name": "decrypt",
      "Description": "Decrypts a string. By default, the string will automatically be decrypted with a secret key unique to each script.",
      "Syntax": "decrypt [<string>] (<key>)",
      "Arguments": [
        "The string to decrypt.",
        "The key to decrypt the input string with. By default, the command will use a secret key unique to the each script. This is the most secure option."
      ],
      "Return": "The decrypted version of the string.",
      "Category": "Cryptography"
    },
    {
      "Name": "encrypt",
      "Description": "Encrypts a string. By default, the string will automatically be encrypted with a secret key unique to each script.",
      "Syntax": "encrypt [<string>] (<key>)",
      "Arguments": [
        "The string to encrypt.",
        "The key to encrypt the input string with. By default, the command will use a secret key unique to the each script. This is the most secure option."
      ],
      "Return": "The encrypted version of the string.",
      "Category": "Cryptography"
    },
    {
      "Name": "guid",
      "Description": "Generates a new GUID string (Globally Unique Identifier).",
      "Syntax": "guid (<hyphenate?>)",
      "Arguments": [
        "Whether the GUID should include hyphens or not."
      ],
      "Return": "The newly generated GUID.",
      "Category": "Core"
    },
    {
      "Name": "hash",
      "Description": "Hashes a string using the SHA256 algorithm.",
      "Syntax": "hash [<input>]",
      "Arguments": [
        "The string that should be hashed."
      ],
      "Return": "The hashed hex string derived from the string.",
      "Category": "Cryptography"
    },
    {
      "Name": "base64",
      "Description": "Encodes a hexadecimal string to Base64.",
      "Syntax": "base64 [<input>]",
      "Arguments": [
        "The hex string to encode."
      ],
      "Return": "The encoded version of the input string.",
      "Category": "String"
    },
    {
      "Name": "enforcetrigger",
      "Description": "Enforces that a script is ran from the specified trigger.",
      "Syntax": "enforcetrigger [<trigger type>,...]",
      "Arguments": [
        "A list of triggers that this script is allowed to be ran from, such as: 'translation' and 'manual'."
      ],
      "Return": null,
      "Category": "Core"
    },
    {
      "Name": "hex",
      "Description": "Converts a number to hexadecimal, or encodes plaintext as hexadecimal.",
      "Syntax": "hex [<input>]",
      "Arguments": [
        "The number or plaintext to convert."
      ],
      "Return": "The hexadecimal representation of the input.",
      "Category": "String"
    },
    {
      "Name": "httpencode",
      "Description": "Escapes the input string so that it can be safely passed inside of a URL.",
      "Syntax": "httpencode [<input>]",
      "Arguments": [
        "The string to escape."
      ],
      "Return": "The escaped version of the string that can be used inside of a URL.",
      "Category": "Web"
    },
    {
      "Name": "hubscript",
      "Description": "Returns information on the locally installed version of a Script Hub script with the given ID.",
      "Syntax": "hubscript [<script ID>] (<property>)",
      "Arguments": [
        "The ID of the script on the Script Hub.",
        "The propety of the Script Hub script to retrieve. By default, this will be the entire map describing all properties of the script."
      ],
      "Return": "The requested information of the locally installed Script Hub script, of nothing if the script was not installed.",
      "Category": "Script"
    },
    {
      "Name": "platform",
      "Description": "Returns the operating system platform that is currently being used.",
      "Syntax": "platform",
      "Arguments": [],
      "Return": "The operating system platform that is currently being used.",
      "Category": "Core"
    },
    {
      "Name": "scriptcontext",
      "Description": "Returns information about the script context that this command is ran in.",
      "Syntax": "scriptcontext",
      "Arguments": [],
      "Return": "A map containing various properties about the script context.",
      "Category": "Script"
    },
    {
      "Name": "tojson",
      "Description": "Converts a map or list into a JSON string.",
      "Syntax": "tojson [<map>/<list>]",
      "Arguments": [
        "The map or list to convert to JSON."
      ],
      "Return": "The JSON string representing the map.",
      "Category": "Core"
    },
    {
      "Name": "playsound",
      "Description": "Plays an audio file through CoyoteMIDI's audio output stream.",
      "Syntax": "playsound [<filename>] (<volume>)",
      "Arguments": [
        "The absolute file path and name of the audio file to play.",
        "The volume, relative to the CoyoteMIDI output volume, at which the audio file will be played, ranging between 0 and 100. Decimal values are allowed."
      ],
      "Return": "The ID assigned to this audio playback by CoyoteMIDI, which can be used to manipulate the audio playback at a later time.",
      "Category": "Audio"
    },
    {
      "Name": "stopsound",
      "Description": "Stops an audio playback stream created by the playsound script command with the given ID.",
      "Syntax": "stopsound [<sound ID>]",
      "Arguments": [
        "The ID of the audio playback stream to stop, as returned by the playsound script command."
      ],
      "Return": null,
      "Category": "Audio"
    },
    {
      "Name": "parsejson",
      "Description": "Parses a JSON string into a useable object.",
      "Syntax": "parsejson [<string>]",
      "Arguments": [
        "The string to be parsed as JSON."
      ],
      "Return": "The parsed JSON object.",
      "Category": "Collection"
    },
    {
      "Name": "save",
      "Description": "Manually saves all global script variables to disk.",
      "Syntax": "save",
      "Arguments": [],
      "Return": null,
      "Category": "Core"
    },
    {
      "Name": "web",
      "Description": "Sends an HTTP based web request.",
      "Syntax": "web [GET/POST/PUT/DELETE] [<web address>] (<data>) (<header_key>:<header_value>,...)",
      "Arguments": [
        "The type of HTTP request to execute. Valid options are 'GET', 'POST', 'PUT', and 'DELETE'.",
        "The complete web address to send the request to.",
        "The raw data string to send to send to the web address.",
        "A header map in the form of header names and header values. A ?:? is used in between names and values. A ?,? is used in between name value pairs."
      ],
      "Return": "The response from the HTTP server that the request was sent to.",
      "Category": "Web"
    },
    {
      "Name": "copyfile",
      "Description": "Copies a file to the specified path.",
      "Syntax": "copyfile [<source file>] [<destination file>] (<should overwrite?>)",
      "Arguments": [
        "The absolute path of the file to copy.",
        "The absolute path to copy the file to, including the file name and extension.",
        "True or false for whether the file should overwrite an already existing file at the destination. The default value is false."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "copyfolder",
      "Description": "Copies a folder and all its contents to the specified path.",
      "Syntax": "copyfolder [<source folder>] [<destination path>] (<should overwrite contents?>)",
      "Arguments": [
        "The absolute path of the folder to copy.",
        "The absolute path to copy the folder to, including the folder name.",
        "True or false for whether the contents should overwrite already existing files or folders with the same name. The default value is false."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "createfolder",
      "Description": "Creates a new folder at the specified path.",
      "Syntax": "createfolder [<folder path>]",
      "Arguments": [
        "The absolute path to create the folder at, including the folder name."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "deletefile",
      "Description": "Deletes a file.",
      "Syntax": "deletefile [<file path>] (<move to recycle bin?>)",
      "Arguments": [
        "The absolute path of the file to be deleted.",
        "True or false for whether the file should be moved to the recycle bin. The default value is true. If false is specified, the file will be permanently deleted with no undo."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "deletefolder",
      "Description": "Deletes a folder and all its contents.",
      "Syntax": "deletefolder [<folder path>] (<move to recycle bin?>)",
      "Arguments": [
        "The absolute path of the folder to be deleted.",
        "True or false for whether the folder should be moved to the recycle bin. The default value is true. If false is specified, the folder will be permanently deleted with no undo."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "fileexists",
      "Description": "Returns whether the specified file exists.",
      "Syntax": "fileexists [<file path>]",
      "Arguments": [
        "The complete path of the file."
      ],
      "Return": "Whether the specified file exists.",
      "Category": "File"
    },
    {
      "Name": "folderexists",
      "Description": "Returns whether the specified folder exists.",
      "Syntax": "folderexists [<folder path>]",
      "Arguments": [
        "The complete path of the folder."
      ],
      "Return": "Whether the specified folder exists.",
      "Category": "File"
    },
    {
      "Name": "getfiles",
      "Description": "Returns the list of all files within a folder.",
      "Syntax": "getfiles [<path>]",
      "Arguments": [
        "The path of the folder to list all files of."
      ],
      "Return": "A list of file names found directly within the specified folder.",
      "Category": "File"
    },
    {
      "Name": "getfolders",
      "Description": "Returns the list of all folders within a folder.",
      "Syntax": "getfolders [<path>]",
      "Arguments": [
        "The path of the folder to list all child folders of."
      ],
      "Return": "A list of folder names found directly within the specified folder.",
      "Category": "File"
    },
    {
      "Name": "movefile",
      "Description": "Moves a file to the specified path.",
      "Syntax": "movefile [<source file>] [<destination file>] (<overwrite destination?>)",
      "Arguments": [
        "The absolute path of the file to be moved.",
        "The absolute path of the location to move the file to, including the file name and its extension.",
        "True or false for whether the destination should be overwritten if it already exists. The default value is false."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "movefolder",
      "Description": "Moves a folder and all its contents to the specified path.",
      "Syntax": "movefolder [<source folder>] [<destination path>]",
      "Arguments": [
        "The absolute path of the folder to move.",
        "The absolute path to move the folder to, including the name of the folder itself."
      ],
      "Return": null,
      "Category": "File"
    },
    {
      "Name": "specialfolder",
      "Description": "Returns the path of a special system folder, such as the user's documents or desktop folder.",
      "Syntax": "specialfolder [<folder name>]",
      "Arguments": [
        "The name of the special folder (not case-sensitive), for example: desktop, mydocuments, applicationdata, localapplicationdata, mymusic, mypictures, userprofile, commonapplicationdata, programfiles."
      ],
      "Return": "The full path of the special folder, or an empty string if the folder is not available on this system.",
      "Category": "File"
    },
    {
      "Name": "getpixel",
      "Description": "Gets the colour of a given pixel as a map with R, G, and B keys.",
      "Syntax": "getpixel [<x>,<y>]",
      "Arguments": [
        "The coordinates of the pixel to get the colour of. A list with 2 values is expected where the first value is the X coordinate and the second value is the Y coordinate."
      ],
      "Return": "The R, G, and B values of the pixel in the form of a map with 3 keys being R, G, and B, each with its value in the map being the colour value of the pixel.",
      "Category": "Graphics"
    },
    {
      "Name": "searchimage",
      "Description": "Finds instances of an image on the screen.",
      "Syntax": "searchimage [<image file path>] (<max amount of results>) (<bounds X1>,<bounds Y1>,<bounds X2>,<bounds Y2>) (<tolerance>)",
      "Arguments": [
        "The absolute path to the image file to search for.",
        "The maximum amount of instances of the image to include in the result. When the maximum amount of results is reached, the search will stop early. The default value is -1 (infinite results).",
        "he bounds in which to search for the image. The expected input is a list with 4 numbers of which the first 2 represent the X and Y coordinates of one corner of the bounds, and the last 2 values represent the X and Y coordinates of the opposite corner of the bounds. By default, every the entire virtual screen (across all monitors) is searched through.",
        "The tolerance amount (as an integer from 1 to 255) that controls by how much each pixel in the image its R, G, and B values can vary from the pixel it is being compared to on the screen. This can help improve reliability when dealing with compressed images or semi-transparent backgrounds. The default value is 0, meaning an exact match is required. In contrast, a value of 255 would cause the image to be considered found at every pixel. If you are having trouble with your image not being found on your screen when you believe it should be, it can help to increase this value a little at a time until you get a result."
      ],
      "Return": "A list of coordinates each representing the top left pixel of the image found on the screen. each entry of the list is a coordinate in the form of its own list with an X and a Y value.",
      "Category": "Graphics"
    },
    {
      "Name": "searchpixel",
      "Description": "Finds all pixels matching a specific colour value and returns a list of each X Y coordinate set.",
      "Syntax": "searchpixel [R:<#>,G:<#>,B:<#>] (<max amount of results>) (<bounds X1>,<bounds Y1>,<bounds X2>,<bounds Y2>)",
      "Arguments": [
        "A map containing the RGB values of the pixel to search for. The map is expected to have each one of the following keys: R, G, and B. Each key?s value should be an integer ranging from 0 to 255.",
        "The maximum amount of pixels to include in the result. When the maximum amount of results is reached, the search will stop early. The default value is -1 (infinite results).",
        "The bounds in which to search for the pixels. The expected input is a list with 4 numbers of which the first 2 represent the X and Y coordinates of one corner of the bounds, and the last 2 values represent the X and Y coordinates of the opposite corner of the bounds. By default, every the entire virtual screen (across all monitors) is searched through."
      ],
      "Return": "A list of coordinates. each entry of the list is a coordinate in the form of its own list with an X and a Y value",
      "Category": "Graphics"
    },
    {
      "Name": "click",
      "Description": "Executes mouse actions as if they were performed by a real mouse.",
      "Syntax": "click (left/right/middle) (<x>,<y>) (up/down/stroke) (<duration>)",
      "Arguments": [
        "The mouse button to simulate. Valid options are 'left', 'right', and 'middle'. The default value is 'left'.",
        "The coordinates of the pixel to click at. These can be prefixed with '~' to use coordinates relative to the current position of the mouse. The default value is '~0,~0'. This argument takes the form of a list with 2 values in which the first value is the X coordinate and the second value is the Y coordinate.",
        "The type of click action to perform. Valid options are 'up', 'down', and 'stroke'. The default value is 'stroke', which is a basic mouse down and up click.",
        "The duration in seconds for the click action. Decimal values are allowed. The default value is 0. The units are in seconds."
      ],
      "Return": null,
      "Category": "InputSimulation"
    },
    {
      "Name": "clipboard",
      "Description": "Returns the text stored in the clipboard, if any.",
      "Syntax": "clipboard",
      "Arguments": [],
      "Return": "The text stored in the clipboard, if any.",
      "Category": "InputSimulation"
    },
    {
      "Name": "getmouse",
      "Description": "Returns the current mouse position as a list of 2 values being the X and Y coordinate.",
      "Syntax": "getmouse",
      "Arguments": [],
      "Return": "the current mouse position as a list of 2 values with the first value being the X coordinate and the second value being the Y coordinate.",
      "Category": "InputSimulation"
    },
    {
      "Name": "key",
      "Description": "Executes key actions in windows as if they were pressed on a keyboard. If the action is 'stroke', the command will delay the script for the duration of the keystroke.",
      "Syntax": "key [up/down/stroke] [<key>(,?)] (<stroke duration>) (key separation delay)",
      "Arguments": [
        "The key action to perform. Valid actions are 'up', 'down', and 'stroke'.",
        "The key(s) to be pressed down. Multiple keys can be provided, separated by a comma. For a list of valid key names, visit https://coyotemidi.com/docs/keyboard-key-code-reference/.",
        "The duration in seconds that the key action should be performed for. Decimal values are allowed. The default value is 0.05 (50ms). This parameter only applies to the ?stroke? action.",
        "The duration in seconds of delay that should be inserted in between every key down and key up event to space them out in time. The default value is 0.002 (2ms). This parameter only applies to the ?stroke? action."
      ],
      "Return": null,
      "Category": "InputSimulation"
    },
    {
      "Name": "keystate",
      "Description": "Checks whether a specific key is pressed down.",
      "Syntax": "keystate [<key>]",
      "Arguments": [
        "The key to get the state of."
      ],
      "Return": "True if the key is pressed down. False if the key is not pressed down.",
      "Category": "InputSimulation"
    },
    {
      "Name": "movemouse",
      "Description": "Moves the mouse to the specified coordinates.",
      "Syntax": "movemouse [<x>,<y>]",
      "Arguments": [
        "The coordinates of the pixel to move the mouse to. These can be prefixed with '~' to use coordinates relative to the current position of the mouse. The default value is '~0,~0'. This argument takes the form of a list with 2 values in which the first value is the X coordinate and the second value is the Y coordinate."
      ],
      "Return": null,
      "Category": "InputSimulation"
    },
    {
      "Name": "scroll",
      "Description": "Performs a scroll action as if the mouse scroll wheel was moved.",
      "Syntax": "scroll (<delta>)",
      "Arguments": [
        "The amount to scroll. Negative values scroll downwards, while positive values scroll upwards. The default value is -2."
      ],
      "Return": null,
      "Category": "InputSimulation"
    },
    {
      "Name": "setclipboard",
      "Description": "Sets the text stored in the clipboard.",
      "Syntax": "setclipboard [<text>]",
      "Arguments": [
        "The text to store in the clipboard."
      ],
      "Return": null,
      "Category": "InputSimulation"
    },
    {
      "Name": "write",
      "Description": "Writes a sequence of text as if it was typed with a keyboard.",
      "Syntax": "write [<text>] (<speed>)",
      "Arguments": [
        "The text to be written down.",
        "The time in seconds to wait in between every character of the text. Decimal values are allowed. The default value is 0."
      ],
      "Return": null,
      "Category": "InputSimulation"
    },
    {
      "Name": "getrecordingvolume",
      "Description": "Gets the volume of the recording sampler.",
      "Syntax": "getrecordingvolume",
      "Arguments": [],
      "Return": "The current playback volume of the recording sampler ranging from 0 to 100. 0 is complete silence.",
      "Category": "MIDI"
    },
    {
      "Name": "heldnotes",
      "Description": "Returns a map with all the currently held down MIDI notes and their properties.",
      "Syntax": "heldnotes (<include sustain?>)",
      "Arguments": [
        "Whether or not notes that are not pressed down, but are still left playing due to the sustain pedal, should be included in the result."
      ],
      "Return": "A map with all the currently held down MIDI notes, where the map keys are the note numbers, and the map values are lists of maps with note properties.",
      "Category": "MIDI"
    },
    {
      "Name": "inputdevices",
      "Description": "Returns a list of available MIDI input devices. Optionally, only show the devices that are enabled in CoyoteMIDI.",
      "Syntax": "inputdevices (<selected only?>)",
      "Arguments": [
        "Whether only the input devices that are selected as input devices within coyoteMIDI should be returned or not. The default value is false."
      ],
      "Return": "The list of input devices.",
      "Category": "MIDI"
    },
    {
      "Name": "midi",
      "Description": "Sends a MIDI event to the output MIDI devices selected in CoyoteMIDI or the otherwise specified devices.",
      "Syntax": "midi [<eventType>] (<note number/control number/program/pitchbend value>) (<velocity/control value>) (<should be recorded?>) (<MIDI channel>) (<MIDI devices>)",
      "Arguments": [
        "The type of MIDI event to send. Valid event types are 'Note', 'ControlChange', 'PitchBend', 'ProgramChange', 'NoteAfterTouch', and 'ChannelAfterTouch'.",
        "The MIDI number for the event. For example, for 'Note', this is the note number, for 'ControlChange', this is the control change number, and for 'PitchBend', this is the pitch.",
        "The MIDI value for the event. For example, the note velocity, or the control change value. This parameter would be unused for pitchbend. The default value is 127.",
        "Whether or not the MIDI event should be recorded by the retrospective recorder. The default value is false.",
        "The MIDI channel of the event. The default value is 0.",
        "The list of MIDI output devices to send the event to. By default, these are the output devices selected in the CoyoteMIDI settings."
      ],
      "Return": null,
      "Category": "MIDI"
    },
    {
      "Name": "notename",
      "Description": "Returns the note name of the MIDI note number.",
      "Syntax": "notename [<note number>]",
      "Arguments": [
        "The MIDI note number to get the note name of."
      ],
      "Return": "The name of the note.",
      "Category": "MIDI"
    },
    {
      "Name": "notenumber",
      "Description": "Returns the MIDI note number of the note name.",
      "Syntax": "notenumber [<note name>]",
      "Arguments": [
        "The note name to get the MIDI note number of."
      ],
      "Return": "The note number of the note.",
      "Category": "MIDI"
    },
    {
      "Name": "outputdevices",
      "Description": "Returns a list of available MIDI output devices. Optionally, only show the devices that are enabled in CoyoteMIDI.",
      "Syntax": "outputdevices (<selected only?>)",
      "Arguments": [
        "Whether only the output devices that are selected as output devices within coyoteMIDI should be returned or not. The default value is false."
      ],
      "Return": "The list of output devices.",
      "Category": "MIDI"
    },
    {
      "Name": "rest",
      "Description": "Pauses the script for the specified musical period based on the recorder BPM.",
      "Syntax": "rest [<note value>] (<BPM>) (<PPQ>)",
      "Arguments": [
        "The note value or note length of the rest, assuming a 4/4 time signature. For example 1 would be a whole note which would last 4 bars and 1/4 would be a quarter note lasting 1 bar.",
        "The BPM to base the rest duration on. The default value is the BPM configured in the recording settings.",
        "The PPQ to base the rest duration on. The default value is the PPQ configured in the recording settings."
      ],
      "Return": null,
      "Category": "MIDI"
    },
    {
      "Name": "saverecording",
      "Description": "Saves the current MIDI buffer in the recorder as a new recording.",
      "Syntax": "saverecording",
      "Arguments": [],
      "Return": null,
      "Category": "MIDI"
    },
    {
      "Name": "setrecordingenabled",
      "Description": "Enables or disables the retrospective MIDI recorder.",
      "Syntax": "setrecordingenabled [true/false]",
      "Arguments": [
        "Wether the retrospective recorder should be enabled ot disabled. True will enable it."
      ],
      "Return": null,
      "Category": "MIDI"
    },
    {
      "Name": "setrecordingvolume",
      "Description": "Sets the volume of the recording sampler.",
      "Syntax": "setrecordingvolume [<volume>]",
      "Arguments": [
        "The playback volume for the sampler ranging from 0 to 100. 0 is complete silence."
      ],
      "Return": null,
      "Category": "MIDI"
    },
    {
      "Name": "sysex",
      "Description": "Sends a MIDI SysEx message to the specified MIDI device.",
      "Syntax": "sysex [<hex values>] [<MIDI devices>]",
      "Arguments": [
        "The SysEx data to send to the devices in the form of hex values. The F0 start byte and F7 end byte are automatically included in the data.",
        "The list of MIDI devices to send the SysEx MIDI Event to."
      ],
      "Return": null,
      "Category": "MIDI"
    },
    {
      "Name": "findprocesses",
      "Description": "Finds any process IDs with names that start with the input name.",
      "Syntax": "findprocesses [<process name>]",
      "Arguments": [
        "The part of the process name to find matching processes for. Any process starting with or fully matching with the input name will be returned."
      ],
      "Return": "All processes that match or start with the specified process name.",
      "Category": "Process"
    },
    {
      "Name": "getfocusedprocess",
      "Description": "Gets the process ID of the currently focused window.",
      "Syntax": "getfocusedprocess",
      "Arguments": [],
      "Return": "The ID of the process of the currently focused window.",
      "Category": "Process"
    },
    {
      "Name": "getprocess",
      "Description": "Gets the process ID by exact name, if any.",
      "Syntax": "getprocess [<process name>]",
      "Arguments": [
        "The name of the process to get the ID of."
      ],
      "Return": "The ID of the process with the specified name.",
      "Category": "Process"
    },
    {
      "Name": "isprocessfocused",
      "Description": "Checks whether a specific process is focused in windows or not.",
      "Syntax": "isprocessfocused [<process ID>/<process name>]",
      "Arguments": [
        "The ID or name of the process."
      ],
      "Return": "True if the process is focused. False if the proces is not focused.",
      "Category": "Process"
    },
    {
      "Name": "isprocessrunning",
      "Description": "Checks whether a specific process is currently running.",
      "Syntax": "isprocessrunning [<process ID>/<process name>]",
      "Arguments": [
        "The ID or name of the process."
      ],
      "Return": "True if the process is running. False if the process is not running.",
      "Category": "Process"
    },
    {
      "Name": "killprocess",
      "Description": "Forcibly stops a windows process.",
      "Syntax": "killprocess [<process ID>]",
      "Arguments": [
        "The ID of the process to stop. This value would be returned by the startprocess command."
      ],
      "Return": null,
      "Category": "Process"
    },
    {
      "Name": "processname",
      "Description": "Returns the name of a process by ID, if any.",
      "Syntax": "processname [<process ID>]",
      "Arguments": [
        "The ID of the process to get the name of."
      ],
      "Return": "The name of the process with the specified ID.",
      "Category": "Process"
    },
    {
      "Name": "setfocusedprocess",
      "Description": "Sets the focused window in windows to the window of the specified process.",
      "Syntax": "setfocusedprocess [<process ID>/<process name>]",
      "Arguments": [
        "The name or ID of the process."
      ],
      "Return": null,
      "Category": "Process"
    },
    {
      "Name": "startprocess",
      "Description": "Starts a new windows process.",
      "Syntax": "startprocess [<path/URL>] (<startup arguments>) (<wait for exit?>)",
      "Arguments": [
        "The path to the file of the process to run, or the URL to open in the default browser.",
        "The startup arguments for the process. These are optional and their notation may vary from program to program.",
        "Whether this command should hold until the process has stopped running. This can be true or false. The default value is false."
      ],
      "Return": "The process ID of the process that was ran, if any.",
      "Category": "Process"
    },
    {
      "Name": "indicator",
      "Description": "Manipulates the logo indicator in the CoyoteMIDI app, which can be used to show a circle of any colour over the CoyoteMIDI logo in the top left of the app. This can be useful to provide visual feedback for scripts.",
      "Syntax": "indicator [true/false] (R:<#>,G:<#>,B:<#>) (<duration>) (<layer>)",
      "Arguments": [
        "A 'true' or 'false' input value to determine whether this command should make the indicator show or stop showing.",
        "The RGB values for the colour of the indicator in the form of a map containing the R, G, and B entries. Each entry should have the letter of the colour as the key, and the colour value as the value of the entry. The colour values should range from 0 to 255.",
        "The duration in seconds for the indicator to show. Decimal values are allowed. The default value is 1.",
        "The layer that this indicator should display on. This can be useful for when multiple scripts are manipulating the indicator for different time periods. The lower the number, the higher the priority of the layer. The default value is 1000, While the default MIDI incidator is on the lowest possible layer.Lower layers take priority. The default value is 0."
      ],
      "Return": null,
      "Category": "UI"
    },
    {
      "Name": "messagebox",
      "Description": "Displays a windows style messagebox pop-up hosted by the CoyoteMIDI app. The messagebox will pause the script until a button is pressed to exit out of the messagebox.",
      "Syntax": "messagebox [<message>] (<title>) (<option>,...)",
      "Arguments": [
        "The text to display in the body of the messagebox.",
        "The title of the messagebox.",
        "The buttons to be included with the messagebox. Valid options are: 'Yes', 'No', 'Ok', 'Cancel', and 'Close'."
      ],
      "Return": "The button that was pressed in plain text. In the case of the close button in the titlebar being pressed, the return will also be 'Close'.",
      "Category": "UI"
    },
    {
      "Name": "audiooutputdevices",
      "Description": "Gets all the audio output devices on the system.",
      "Syntax": "audiooutputdevices",
      "Arguments": [],
      "Return": "A list of available audio output devices on the system.",
      "Category": "Audio"
    },
    {
      "Name": "getmute",
      "Description": "Gets whether the system audio output is muted or not. On Windows, you can specify a process and audio device to get the individual audio mute state of.",
      "Syntax": "getmute (<process ID>/<process name>) (audio device)",
      "Arguments": [
        "The ID or name of the process to get the mute state of. The default value will be the global volume of the current audio device. On Mac, this argument is ignored.",
        "The name of the audio device to get the mute state of. By default, this is the current audio device. On Mac, this argument is ignored."
      ],
      "Return": "The mute state of the specified process or audio device.",
      "Category": "Audio"
    },
    {
      "Name": "getvolume",
      "Description": "Returns the system output volume.  On Windows, you can specify a process and audio device to get the individual audio volume of.",
      "Syntax": "getvolume (<process ID>/<process name>) (<audio device>)",
      "Arguments": [
        "The ID or name of the process to get the volume of. The default value will be the global volume of the current audio device. On Mac, this argument is ignored.",
        "The name of the audio device to get the volume of. By default, this is the current audio device. On Mac, this argument is ignored."
      ],
      "Return": "The volume of the specified process or audio device, ranging from 0 to 100.",
      "Category": "Audio"
    },
    {
      "Name": "setmute",
      "Description": "Sets whether the system audio output is muted or not. On Windows, you can specify a process and audio device to set the individual audio mute state of.",
      "Syntax": "setmute [<mute?>] (<process ID>/<process name>) (audio device)",
      "Arguments": [
        "Whether or not the target should be muted, as opposed to being unmuted.",
        "The ID or name of the process to change the mute state of. The default value will be the global volume of the current audio device. On Mac, this argument is ignored.",
        "The name of the audio device to mute or unmute. By default, this is the current audio device. On Mac, this argument is ignored."
      ],
      "Return": null,
      "Category": "Audio"
    },
    {
      "Name": "setvolume",
      "Description": "Changes the volume of the system audio output. On Windows, you can specify a process and audio device to set the individual output volume of.",
      "Syntax": "setvolume [<volume>] (<process ID>/<process name>) (<audio device>)",
      "Arguments": [
        "The volume ranging between 0 and 100. Decimal values are allowed.",
        "The ID or name of the process to change the volume of. The default value will be the global volume of the current audio device. On Mac, this argument is ignored.",
        "The name of the audio device to change the volume of. By default, this is the current audio device. On Mac, this argument is ignored."
      ],
      "Return": null,
      "Category": "Audio"
    },
    {
      "Name": "runscript",
      "Description": "Runs a loaded script.",
      "Syntax": "runscript (<script name>) (<subscript name>) (<variable_name>:<value>,?) (<await completion?>)",
      "Arguments": [
        "The full name of the script as loaded in CoyoteMIDI.",
        "The path of the script sub section to run. If left to default, this will be the default entry point of a script.",
        "The variables to be included in the script to be ran. Each variable has to be included in the form of a variable name, and the variable value, separated by a ':'. Multiple entries can be specified, separated by a ','. This parameter is optional.",
        "Whether the command should await the completion of the script being ran. When set to 'false', the next command following the runscript command will be executed immediately, before the script being ran by the command has finished executing. The default value is 'true', causing the command to wait until the called script is finished executing. This will also mean that the command will not be able to wait for a return value to be returned from the script being ran."
      ],
      "Return": "The return value from the script being ran. This value would be set in the script to run with the ?return? command. This value will always be blank when the 'await completion?' argument is set to false.",
      "Category": "Script"
    },
    {
      "Name": "cancel",
      "Description": "Cancels all running script instances matching the script name. This may cause unexpected behaviors.",
      "Syntax": "cancel (<script name>) (<subscript name>)",
      "Arguments": [
        "The full name of the script as loaded in CoyoteMIDI.",
        "The path of the script sub section to cancel. The default value is 'default', which is the default entry point of a script."
      ],
      "Return": null,
      "Category": "Script"
    },
    {
      "Name": "count",
      "Description": "Counts the amount of items in a collection.",
      "Syntax": "count [<list/map/string>]",
      "Arguments": [
        "The collection to count the items of. This can be a list, a map, or a string. In the case of a map, the amount of keys in the map is returned. In the case of a string, the amount of characters in the string is returned."
      ],
      "Return": "The amount of items in the collection.",
      "Category": "Collection"
    },
    {
      "Name": "find",
      "Description": "Finds the position of a list item or string in a list or string.",
      "Syntax": "find [<list/string>] [<item/string>]",
      "Arguments": [
        "The list or string to find the index of a value in.",
        "The item or substring to find inside the list or string."
      ],
      "Return": "The index of the found item.",
      "Category": "Collection"
    },
    {
      "Name": "mapkeys",
      "Description": "Returns the keys of a map object.",
      "Syntax": "mapkeys [<map>]",
      "Arguments": [
        "The map object to list all keys of."
      ],
      "Return": "A list of keys found within the map object.",
      "Category": "Collection"
    },
    {
      "Name": "mapvalues",
      "Description": "Returns the values of a map object.",
      "Syntax": "mapvalues [<map>]",
      "Arguments": [
        "The map object to list all values of."
      ],
      "Return": "A List of values found within the map object.",
      "Category": "Collection"
    },
    {
      "Name": "sort",
      "Description": "Sorts a list alphanumerically.",
      "Syntax": "sort [<list>]",
      "Arguments": [
        "The list to sort alphanumerically."
      ],
      "Return": "The list sorted alphanumerically.",
      "Category": "Collection"
    },
    {
      "Name": "split",
      "Description": "Creates a list by splitting a string into multiple items separated by the given string section.",
      "Syntax": "split [<text>] (<spliting string>)",
      "Arguments": [
        "The input string to split into a list.",
        "The string or character to treat as the separator in between every section of text. If this is left blank, the text to split will be split at every character."
      ],
      "Return": "The list of string sections in between the separator string.",
      "Category": "Collection"
    },
    {
      "Name": "cooldown",
      "Description": "Sets a cooldown on the script at the line of this command. If a cooldown for this exact line in the script is already active, \nthe script will stop at this line and discard the rest of the script. Can optionally return a value while on cooldown.",
      "Syntax": "cooldown (<#.#>) (<return>)",
      "Arguments": [
        "The time in seconds that the cooldown will be active for. Decimals are allowed.",
        "The optional value to return in this script if the script were to be stopped by the cooldown being active."
      ],
      "Return": null,
      "Category": "Script"
    },
    {
      "Name": "error",
      "Description": "Simulates a script error.",
      "Syntax": "error (<message>)",
      "Arguments": [
        "The message to include with the script error."
      ],
      "Return": null,
      "Category": "Script"
    },
    {
      "Name": "abs",
      "Description": "Returns the absolute value of a number.",
      "Syntax": "abs [<number>]",
      "Arguments": [
        "The number to get the absolute value of."
      ],
      "Return": "The absolute value of the number.",
      "Category": "Math"
    },
    {
      "Name": "ceiling",
      "Description": "Rounds up a decimal number.",
      "Syntax": "ceiling [<#.#>]",
      "Arguments": [
        "The number to round up."
      ],
      "Return": "The rounded up number.",
      "Category": "Math"
    },
    {
      "Name": "cos",
      "Description": "Returns the cosine of the specified angle.",
      "Syntax": "cos [<#.#>]",
      "Arguments": [
        "The angle to return the cosine of."
      ],
      "Return": "The cosine of the specified angle.",
      "Category": "Math"
    },
    {
      "Name": "floor",
      "Description": "Rounds down a decimal number.",
      "Syntax": "floor [<#.#>]",
      "Arguments": [
        "The number to round down."
      ],
      "Return": "The rounded down number.",
      "Category": "Math"
    },
    {
      "Name": "isnumber",
      "Description": "Returns whether the input is a number or not.",
      "Syntax": "isnumber [<input>]",
      "Arguments": [
        "The input to be checked whether it is a number."
      ],
      "Return": "Whether the input is a number or not.",
      "Category": "Math"
    },
    {
      "Name": "max",
      "Description": "Returns the greater of 2 values.",
      "Syntax": "max [<#.#>] [<#.#>]",
      "Arguments": [
        "The first number to pick the greater value from.",
        "The second number to pick the greater value from."
      ],
      "Return": "The greater value out of the 2 specified values.",
      "Category": "Math"
    },
    {
      "Name": "min",
      "Description": "Returns the lesser of 2 values.",
      "Syntax": "min [<#.#>] [<#.#>]",
      "Arguments": [
        "The first number to pick the lesser value from.",
        "The second number to pick the lesser value from."
      ],
      "Return": "The lesser value out of the 2 specified values.",
      "Category": "Math"
    },
    {
      "Name": "mod",
      "Description": "Returns the modulo between 2 numbers.",
      "Syntax": "mod [<number>] [<modulus>]",
      "Arguments": [
        "The number to perform the modulo operation on.",
        "The modulus number to perform the modulo operation with on the first number."
      ],
      "Return": "The remainder of a division between 2 numbers.",
      "Category": "Math"
    },
    {
      "Name": "random",
      "Description": "Generates a random number between the specified values. \nOptionally, the resulting number can be rounded to an integer.",
      "Syntax": "random (<#.#>) (<#.#>) (<round?>)",
      "Arguments": [
        "The minimum value for the randomly generated number. Decimals are allowed. The default value is 0.",
        "The maximum value for the randomly generated number. Decimals are allowed. The default value is 1.",
        "Whether the returned value should be rounded or not. The default value is false."
      ],
      "Return": "The randomly generated number.",
      "Category": "Math"
    },
    {
      "Name": "round",
      "Description": "Rounds a decimal number to the nearest integer number.",
      "Syntax": "round [<#.#>]",
      "Arguments": [
        "The number to round."
      ],
      "Return": "The rounded number.",
      "Category": "Math"
    },
    {
      "Name": "sin",
      "Description": "Returns the sine of the specified angle.",
      "Syntax": "sin [<#.#>]",
      "Arguments": [
        "The angle to return the sine of."
      ],
      "Return": " The sine of the specified angle.",
      "Category": "Math"
    },
    {
      "Name": "tan",
      "Description": "Returns the tangent of the specified angle.",
      "Syntax": "tan [<#.#>]",
      "Arguments": [
        "The angle to return the tangent of."
      ],
      "Return": "The tangent of the specified angle.",
      "Category": "Math"
    },
    {
      "Name": "print",
      "Description": "Prints some text to any relevant output platform such as a console or debuging display.",
      "Syntax": "print (<text>)",
      "Arguments": [
        "The string to be printed to the debugbar."
      ],
      "Return": null,
      "Category": "Script"
    },
    {
      "Name": "after",
      "Description": "Returns the part of the string after the specified search string.",
      "Syntax": "after [<search>] [<full string>]",
      "Arguments": [
        "The part of the string to look for and get the text after.",
        "The complete string to find the text after the search string in."
      ],
      "Return": "The part of the string afterthe specified search string.",
      "Category": "String"
    },
    {
      "Name": "before",
      "Description": "Returns the part of the string before the specified search string.",
      "Syntax": "before [<search>] [<full string>]",
      "Arguments": [
        "The part of the string to look for and get the text before.",
        "The complete string to find the text before the search string in."
      ],
      "Return": "The part of the string before the specified search string.",
      "Category": "String"
    },
    {
      "Name": "endswith",
      "Description": "Returns whether the input string ends with a given string.",
      "Syntax": "endswith [<end>] [<full string>]",
      "Arguments": [
        "The part of the string to look for at the ends of the input string.",
        "The complete string to check the ends of."
      ],
      "Return": "whether the full string ends with the search input.",
      "Category": "String"
    },
    {
      "Name": "lowercase",
      "Description": "Returns the input text with any uppercase characters converted to lowercase characters.",
      "Syntax": "lowercase [<text>]",
      "Arguments": [
        "The part of the string to convert to lowercase characters."
      ],
      "Return": "The version of the input text with only lowercase characters.",
      "Category": "String"
    },
    {
      "Name": "replace",
      "Description": "Replaces instances of a string with a new string inside a larger string.",
      "Syntax": "replace [<text>] [<to replace>] (<replace with>)",
      "Arguments": [
        "The whole text to replace parts of.",
        "The piece of text that you want to replace.",
        "The text to replace the matching values with. The default value is blank, causing the matched pieces of text to be removed."
      ],
      "Return": "The modified version of the text with the pieces of text replaced.",
      "Category": "String"
    },
    {
      "Name": "startswith",
      "Description": "Returns whether the input string starts with a given string.",
      "Syntax": "startswith [<start>] [<full string>]",
      "Arguments": [
        "The part of the string to look for at the start of the input string.",
        "The complete string to check the start of."
      ],
      "Return": "whether the full string starts with the search input.",
      "Category": "String"
    },
    {
      "Name": "titlecase",
      "Description": "Returns the input text with any starting character of each word converted to an uppercase character.",
      "Syntax": "titlecase [<text>]",
      "Arguments": [
        "The part of the string to convert to titlecase text."
      ],
      "Return": "The version of the input text with the start of each word converted to an uppercase character.",
      "Category": "String"
    },
    {
      "Name": "trim",
      "Description": "Removes any leading and trailing whitespace from the input string.",
      "Syntax": "trim [<string>]",
      "Arguments": [
        "The string to remove leading and trailing whitespace from."
      ],
      "Return": "The input string with any leading or trailing whitespace removed..",
      "Category": "String"
    },
    {
      "Name": "uppercase",
      "Description": "Returns the input text with any lowercase characters converted to uppercase characters.",
      "Syntax": "uppercase [<text>]",
      "Arguments": [
        "The part of the string to convert to uppercase characters."
      ],
      "Return": "The version of the input text with only uppercase characters.",
      "Category": "String"
    },
    {
      "Name": "wait",
      "Description": "Pauses the script for the specified amount of time.",
      "Syntax": "wait (<#.#>)",
      "Arguments": [
        "The time in seconds that the script should be paused for. Decimals are allowed. The default value is 1 second."
      ],
      "Return": null,
      "Category": "Script"
    },
    {
      "Name": "date",
      "Description": "Returns a formatted date for the specified unix time.\nFor standard formatting options visit: https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings.\nFor custom formatting visit: https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings.",
      "Syntax": "date [<timestamp in seconds>] (<format>) (UTC?)",
      "Arguments": [
        "The unix timestamp, in seconds, for which to return the date.",
        "The format preset or custom format to use for the date. The default value is G, which will format the time as 'mm/dd/yyyy hh:MM:ss'.",
        "Whether to return the UTC date for the unix time, instead of the local date. By default, this is false and will show the local date."
      ],
      "Return": "The formatted date for the timestamp.",
      "Category": "Time"
    },
    {
      "Name": "parsedate",
      "Description": "Returns the unix time of the provided date string representation.",
      "Syntax": "parsedate [<date>] (<format>) (UTC?)",
      "Arguments": [
        "The string representation of the date to get the unix time for.",
        "The format preset or custom format to use for parsing date, identical to the formats available in the date command. By default, the command will dynamically try to interpret the specified date.",
        "Whether to parse the date as a UTC date instead of a local date. By default, this is false and will parse as a local date."
      ],
      "Return": "The Unix time of the provided date, in seconds.",
      "Category": "Time"
    },
    {
      "Name": "time",
      "Description": "Returns the current Unix time, in seconds.",
      "Syntax": "time",
      "Arguments": [],
      "Return": "The current Unix time, in seconds.",
      "Category": "Time"
    }
  ],
  "events": [
    {
      "Name": "configchanged",
      "Description": "Triggers when the configuration provided for this script through the configurationparameter script command was changed by the user.",
      "Syntax": "configchanged",
      "Variables": {
        "parameters": "A map with all parameter IDs and their values."
      }
    },
    {
      "Name": "key",
      "Description": "Triggers when a windows computer key has been pressed down or lifted up.",
      "Syntax": "key (up/down) (<key>)",
      "Variables": {
        "action": "The type of key action, such as down or up.",
        "key": "The key that was modified.",
        "simulated": "Wether the key event was simulated by CoyoteMIDI or another app instead of hardware.",
        "pressedkeys": "A list of keys that are currently pressed down."
      }
    },
    {
      "Name": "process",
      "Description": "Triggers when a process is started or stopped. On Windows, this event requires administrator privileges. On Mac, this event is currently not available.",
      "Syntax": "process (start/stop) (<process name>)",
      "Variables": {
        "action": "The action of the process, either 'start' or 'stop'.",
        "processname": "The name of the process that triggered the event.",
        "processid": "The ID of the process that triggered the event."
      }
    },
    {
      "Name": "shutdown",
      "Description": "Triggers when CoyoteMIDI is safely closed.",
      "Syntax": "shutdown",
      "Variables": {}
    },
    {
      "Name": "startup",
      "Description": "Triggers when CoyoteMIDI launches.",
      "Syntax": "startup",
      "Variables": {}
    },
    {
      "Name": "sysex",
      "Description": "Triggers when CoyoteMIDI recieves a SysEx MIDI event from an input device.",
      "Syntax": "sysex (<device name>)",
      "Variables": {
        "device": "The name of the MIDI device.",
        "data": "The data sent by the device as a hex value string."
      }
    },
    {
      "Name": "time",
      "Description": "Triggers at fixed time intervals or at the specified time.",
      "Syntax": "time (hourly/minutely/secondly/at) (<hour>) (<minute>)",
      "Variables": {}
    },
    {
      "Name": "windowchanged",
      "Description": "Triggers when the focused window has changed. On Mac, this will only trigger when CoyoteMIDI loses or gains focus.",
      "Syntax": "windowchanged (<new window process name>)",
      "Variables": {
        "processname": "The process name of the window that the focus has changed to.",
        "processid": "The process ID of the window that the focus has changed to."
      }
    },
    {
      "Name": "windowssession",
      "Description": "Triggers when the Windows session is locked or unlocked. On Mac, this event is currently not available.",
      "Syntax": "windowssession (lock/unlock)",
      "Variables": {
        "action": "The action of the Windows session event."
      }
    },
    {
      "Name": "midi",
      "Description": "Triggers whenever CoyoteMIDI recieves a MIDI event from an input device.",
      "Syntax": "midi (<eventType>) (<note number/control channel/program/pitchbend value>) (<velocity/control value/aftertouch value>) (channel)",
      "Variables": {
        "type": "The type of MIDI event.",
        "midinumber": "The catch-all MIDI number associated with this event, regardless of event type.",
        "midivalue": "The catch-all MIDI value associated with this event, regardless of event type.",
        "channel": "The MIDI channel number, ranging from 0 to 15.",
        "note": "The note number, if the MIDI event type is Note or NoteAfterTouch.",
        "velocity": "The note velocity, if the MIDI event type is Note.",
        "controlnumber": "The control change number, if the MIDI event type is ControlChange.",
        "controlvalue": "The control change value, if the MIDI event type is ControlChange.",
        "pitchbend": "The pitchbend value, if the MIDI event type is PitchBend.",
        "program": "The program change number, if the MIDI event type is ProgramChange.",
        "aftertouchvalue": "The aftertouch value, if the MIDI event type is NoteAfterTouch or ChannelAfterTouch.",
        "device": "The name of the MIDI device.",
        "silenced": "Whether the event has been silenced or surpressed by CoyoteMIDI."
      }
    },
    {
      "Name": "osc",
      "Description": "Triggers whenever CoyoteMIDI recieves an OSC event.",
      "Syntax": "osc (<port>) (<address>)",
      "Variables": {
        "port": "The port that the OSC event was received on.",
        "address": "The address of the OSC event.",
        "arguments": "The arguments of the OSC event."
      }
    },
    {
      "Name": "uirefreshed",
      "Description": "Triggers when the CoyoteMIDI UI refreshes and the script provided UI elements need to be rebuilt.",
      "Syntax": "uirefreshed",
      "Variables": {}
    },
    {
      "Name": "uri",
      "Description": "Triggers when a URI scheme starting with 'coyotescript' was received by CoyoteMIDI.",
      "Syntax": "uri (<host>)",
      "Variables": {
        "uri": "The complete raw URI received by CoyoteMIDI.",
        "host": "The host specified in the URI, with The leading 'coyotescript' identifier stripped.",
        "rawhost": "The complete host specified in the URI with the internal identifier still included.",
        "path": "The path specified in the URI.",
        "query": "The query part of the URI."
      }
    },
    {
      "Name": "scriptsloaded",
      "Description": "Triggers when the script system loads or reloads the scripts.",
      "Syntax": "scriptsloaded",
      "Variables": {}
    }
  ]
}