Bind keyboard shortcuts to any actions
To create your configuration, you can use the configurator at https://rigwild.github.io/shortcut-hero-configurator/
See releases for pre-built binaries.
For debian or ubuntu based distributions, you need the following dependencies.
# Required for https://github.com/obv-mikhail/InputBot#build-dependencies
sudo apt install libx11-dev libxtst-dev libudev-dev libinput-devgit clone git@github.com:rigwild/shortcut-hero.git
cd shortcut-hero
cargo build --release
# If you have UPX, to compress the executable
upx --best --lzma -o target/release/shortcut-hero.upx.exe target/release/shortcut-hero.exe# Required for https://github.com/obv-mikhail/InputBot#build-dependencies
sudo apt install libx11-dev libxtst-dev libudev-dev libinput-dev
git clone git@github.com:rigwild/shortcut-hero.git
cd shortcut-hero
cargo build --release
# If you have UPX, to compress the executable
upx --best --lzma -o target/release/shortcut-hero.upx.bin target/release/shortcut-heroCreate a file named shortcut-hero.json in your current working directory, it will be created if not found.
Here is a configuration file example.
{
"openai_api_key": "sk-...",
"keyboard_shortcuts": [
{
"description": "When pressing `D`, set the variables `city1` and `city2` to `Bordeaux` and `Lyon` respectively, then check if they are equal. If they are, print `If was true!` to the console, else print `If was false!`.",
"keys": ["DKey"],
"actions": [
{
"action": "debug"
},
{
"action": "set_variable",
"name": "city1",
"value": "Bordeaux"
},
{
"action": "set_variable",
"name": "city2",
"value": "Lyon"
},
{
"action": "if_else_relative",
"operation": "string_equals",
"a": "{{city1}}",
"b": "{{city2}}",
"step_true": "+1",
"step_false": "+3"
},
{
"action": "print_console",
"content": "If was true!"
},
{
"action": "end_program"
},
{
"action": "print_console",
"content": "If was false!"
}
]
},
{
"description": "When pressing `Ctrl + B`, read the clipboard, print debug infos to console and show a dialog box with the clipboard content",
"keys": ["LControlKey", "BKey"],
"actions": [
{
"action": "read_clipboard"
},
{
"action": "debug"
},
{
"action": "show_dialog",
"title": "Hello World!",
"body": "{{input}}"
}
]
},
{
"description": "When pressing `Ctrl + M`, print `Loop iteration 0` to `Loop iteration 4` in the console",
"keys": ["LControlKey", "MKey"],
"actions": [
{
"action": "set_variable",
"name": "i",
"value": "0"
},
{
"action": "print_console",
"content": "Loop iteration {{i}}"
},
{
"action": "increment_variable",
"name": "i",
"amount": "1"
},
{
"action": "if_else_relative",
"operation": "<",
"a": "{{i}}",
"b": "5",
"step_true": "-2",
"step_false": "+1"
},
{
"action": "print_console",
"content": "End of the loop!"
}
]
}
]
}An OpenAI API key, only required if you use the Ask ChatGPT action.
List of keyboard keys to all be pressed at the same time to trigger the associated actions. List of available keys.
List of actions to run when triggering this shortcut.
Actions are synchronous functions that take some input and return some output, they can do anything.
The actions will run in the order they are defined. The result of each action is provided to the next action as an input (variable input).
If an action requires any parameter, you can use variables enclosed in tags {{input}} or {{my_variable}}, they will be replaced everywhere with the associated value.
Variables names are case-insensitive.
The first action in the list will receive an empty string as an input. You may want to start your list of actions with an action that read some data as input for the next actions.
Print the configuration, the provided input and the list of variables. Returns input.
{
"action": "debug"
}Set the value of a variable. Do not use tags like {{input}} or {{my_variable}} for the name of the variable, use the variable name directly input or my_variable. Otherwise, value can contain tags like {{input}} or {{my_variable}}.
Returns input.
Set the variable city to Bordeaux.
{
"action": "set_variable",
"name": "city",
"value": "Bordeaux"
}Set the variable input to Hello, I am rigwild! How are you? Bordeaux is a great place to live!.
With input = rigwild, city = Bordeaux
{
"action": "set_variable",
"name": "input",
"value": "Hello, I am {{input}}! How are you? {{city}} is a great place to live!"
}Increment the value of a variable. Returns input.
- Parameter
amountmust be a string containing a valid integer (can be negative). Default is1.
Increment the variable count by 1.
{
"action": "increment_variable",
"name": "count"
}Increment the variable count by 5.
With inc = 10
{
"action": "increment_variable",
"name": "count",
"amount": "{{inc}}"
}Decrement the variable i by 1.
{
"action": "increment_variable",
"name": "i",
"amount": "-1"
}Delete a variable. Returns input (if the deleted variable is input, returns nothing).
{
"action": "delete_variable",
"name": "my_variable"
}Wait for a given duration. Returns input.
- Parameter
duration_msmust be a string containing a valid positive integer, in milliseconds.
Wait for 1 second.
{
"action": "sleep",
"duration_ms": "1000"
}Wait for 5 seconds.
With wait_time = 5000
{
"action": "sleep",
"duration_ms": "{{wait_time}}"
}End the program.
{
"action": "end_program"
}Go to a given step in the list of actions (starts at 0). Returns input.
Will error out if the step is out of bounds.
- Parameter
stepmust be a string containing a valid integer.
Go to step 0.
{
"action": "go_to_step",
"step": "0"
}Go to step 5.
With my_step = 5
{
"action": "go_to_step",
"step": "{{my_step}}"
}Go 2 steps forward relative from the current step (the + symbol is optional).
{
"action": "go_to_step_relative",
"step_relative": "+2"
}Go 1 step backward relative from the current step.
{
"action": "go_to_step_relative",
"step_relative": "-1"
}Go 5 steps backward relative from the current step.
With my_step = -5
{
"action": "go_to_step_relative",
"step_relative": "{{my_step}}"
}Go to a given step in the list of actions (starts at 0) or another depending on condition. Returns input.
Will error out if the step is out of bounds.
- Parameter
step_trueandstep_falsemust be a string containing a valid integer.
Operation to perform on A and B to determine if the condition is true.
-
Real numbers comparisons:
==!=<<=>>=
-
String comparisons:
string_equalsstring_not_equalsstring_containsstring_not_contains,string_starts_withstring_ends_withstring_is_empty(only onA)string_is_not_empty(only onA)
Go to step 8 if A is equal to B, otherwise go to step 12.
{
"action": "if_else",
"operation": "==",
"a": "777",
"b": "777",
"step_true": "8",
"step_false": "12"
}Go to step 8 if A is equal to B, otherwise go to step 12.
With city_a = Bordeaux, city_a = Lyon, step_success = 8, step_failure = 12
{
"action": "if_else",
"operation": "string_equals",
"a": "{{city_a}}",
"b": "{{city_b}}",
"step_true": "{{step_success}}",
"step_false": "{{step_failure}}"
}Go 2 steps forward (the + symbol is optional) relative from current step if A contains B, otherwise got 7 steps backward.
{
"action": "if_else_relative",
"operation": "string_contains",
"a": "hello this is a string",
"b": "this is",
"step_true": "+2",
"step_false": "-7"
}Perform a loop of 5 iterations. Print Loop iteration 0 to Loop iteration 4 in the console
{
"action": "set_variable",
"name": "i",
"value": "0"
},
{
"action": "print_console",
"content": "Loop iteration {{i}}"
},
{
"action": "increment_variable",
"name": "i",
"amount": "1"
},
{
"action": "if_else_relative",
"operation": "<",
"a": "{{i}}",
"b": "5",
"step_true": "-2",
"step_false": "+1"
},
{
"action": "print_console",
"content": "End of the loop!"
}Spawn a system command. Returns the result of the command.
- Parameter
argsis optional, default value is empty list.
Evaluate a JavaScript program with Node.js.
With input = console.log('Hello world!').
{
"action": "spawn",
"command": "/usr/bin/node",
"args": ["-e", "{{input}}"]
}/usr/bin/node -e "console.log('Hello world!')"Find the files that are bigger than 1 MB in a directory.
With input = ~/.
{
"action": "spawn",
"command": "find",
"args": ["find", "{{input}}", "-type", "f", "-size", "+1M", "-exec", "ls", "-lh", "{}", "\\;"]
}find ~/ -type f -size +1M -exec ls -lh {} \;Execute an arbitrary command (dangerous).
With command = rm, path = /some/example.
{
"action": "spawn",
"command": "{{command}}",
"args": ["-rf", "{{path}}"]
}rm -rf /some/examplePrint the input to the console. Returns input.
- Parameter
contentis optional, default value is{{input}}.
Print Hello world! to the console.
{
"action": "print_console",
"content": "Hello world!"
}Print Hello world! to the console.
With input = Hello world!
{
"action": "print_console"
}Print Hello world! I am rigwild! I live in Bordeaux to the console.
With input = rigwild, city = Bordeaux
{
"action": "print_console",
"content": "Hello world! I am {{input}}! I live in {{city}}"
}Show the input in a native OS dialog box. Returns input.
- Parameter
titleis optional, default value isAction Result. - Parameter
bodyis optional, default value is{{input}}.
Show a dialog with title Action Result and body Hello World!.
With input = Hello world!
{
"action": "show_dialog"
}Show a dialog with title My Dialog Title and body Hello world!.
With input = Hello world!
{
"action": "show_dialog",
"title": "My Dialog Title"
}Show a dialog with title Result of operation "12 * 11" and body Result from action is: 132.
With input = 132, math_equation = 12 * 11
{
"action": "show_dialog",
"title": "Result of operation \"{{math_equation}}\"",
"body": "Result from action is: {{input}}"
}Read the content of the clipboard. Returns content of the clipboard.
{
"action": "read_clipboard"
}Write to the clipboard. Returns input.
- Parameter
contentis optional, default value is{{input}}.
Write Hello world! to the clipboard.
{
"action": "write_clipboard",
"content": "Hello world!"
}Write Hello world! to the clipboard.
With input = Hello world!
{
"action": "write_clipboard"
}Write The quick brown fox jumps over the lazy dog to the clipboard.
With animal = dog.
{
"action": "write_clipboard",
"content": "The quick brown fox jumps over the lazy {{animal}}"
}Ask something to ChatGPT. Returns the answer from ChatGPT.
- Parameter
pre_promptis optional, default value is no pre-prompt. - Parameter
promptis optional, default value is{{input}}.
Ask ChatGPT to answer.
With input = Who are you?!.
{
"action": "ask_chatgpt"
}Ask ChatGPT to answer Who are you?! with fixed pre-prompt Explain to me the following text by talking like I am a 5 years old.
With input = Who are you?!.
{
"action": "ask_chatgpt",
"pre_prompt": "Explain to me the following text by talking like I am a 5 years old",
"prompt": "{{input}}"
}Ask ChatGPT to play a theater game with dynamic participants and pre-prompt.
With:
character_assistant = rigwildcharacter_me = Lounacharacter_me_sentence = Can you buy me some peaches too?
{
"action": "openai_ask_chatgpt",
"pre_prompt": "You are playing a theater game where you are a character in a made-up story. You are in a scene, you are called {{character_assistant}}. You say: \"I am going to the store to buy some apples.\"",
"prompt": "- {{character_me}}: \"Hey {{character_assistant}}! {{character_me_sentence}}\""
}You can create your own actions by forking this repository and adding your own actions in the actions directory.
You will need to add your action in the Action enum to make it available for a shortcut.
Note: Your action's name will be converted to and from snake_case for the configuration file.
Be creative! ✌️
- Read the current OS time
- Get the current weather from some API
- Get the price of an item on Amazon by scrapping the website
- Get the current price of a cryptocurrency
- Send a message on Telegram using a bot
- Post in a Discord channel using a bot
- Open a URL in the default browser
- Read the content of a file
- Write the input to a file
- Star this GitHub repository (😉)
If you are looking to create a configuration generator front-end, TypeScript bindings are provided in the bindings directory.
