Summary ScriptKit now has snippets built-in, but they're lacking a couple of powerful features at the moment.

So I built this addon to handle the selection, cursor, clipboard, and evil script eval():

Here's an example snippet that's going to replace the selection with a script tag and set variables based on clipboard and ScriptKit input:

// Name: Expand TS
<script lang="ts">
$SELECTION$
$CURSOR$
const argument = '$$arg("Anything to say?")$$';
const clipboard = '$CLIPBOARD$';
const number = '$$arg("What is the magic number?", ['42', '7', '8'])$$';
</script>

If you like the script, retweet it 😇

Script:

// Name: Snippets on Steroids
// Author: pyronaur
// Twitter: @pyronaur
// Shortcut: cmd+shift+e
/**
* This script expands a given snippet and replaces placeholders with their respective values.
*
* ## Placeholders
* - $CURSOR$ - set the cursor position in the snippet after it's expanded.
* - $SELECTION$ - insert the currently selected text within the snippet.
* - $CLIPBOARD$ - insert the current clipboard text within the snippet.
*
* ## Code Evaluation
* You can place any JavaScript code within $$...$$ and it will be evaluated and replaced with the result.
*
* For example:
* The code inside the $$...$$ will be executed and the result will replace the placeholder.
* Example:
* ```
* const clipboard = '$$clipboard.readText()$$';
* const date = '$$new Date().toLocaleDateString()$$';
* const number = '$$arg("What is the magic number?", ['42', '7', '8'])$$';
* ```
* Note:
* The script execution is potentially dangerous and should be enabled with caution.
* You have to enable it by setting the `I_AM_THE_DANGER` variable to `true`.
*/
import "@johnlindquist/kit"
import { Choice } from '@johnlindquist/kit';
const { globby } = await npm("globby");
const snippet_path = kenvPath('snippets');
// 🔴 DANGER 🔴
// SETTING THIS TO TRUE ALLOW ANY SCRIPT TO BE EXECUTED BY SNIPPETS
const I_AM_THE_DANGER = false;
async function dangerous_evil_parse(content: string) {
const evil_regex = /\$\$(.*?)\$\$/g;
let match;
let matches = [];
while ((match = evil_regex.exec(content)) !== null) {
matches.push(match);
}
for (let match of matches) {
const script = match[1];
const result = I_AM_THE_DANGER ? await eval(script) : ''; // 🎩 😎
content = content.replace(`$$${script}$$`, result);
}
return content;
}
// Find $CURSOR$ and set cursor position
async function set_with_cursor(content: string) {
const cursor_index = content.indexOf('$CURSOR$');
if (cursor_index === -1) {
return false;
}
// Remove $$CURSOR$$
content = content.replace('$CURSOR$', '');
await setSelectedText(content);
// There's some async weirdness here
// so we'll just wait 100ms
await new Promise(resolve => setTimeout(resolve, 100));
const target_cursor_position = content.length - cursor_index;
const keystrokes = [];
for (let i = 0; i < target_cursor_position; i++) {
keystrokes.push(keystroke('left'));
}
await Promise.all(keystrokes);
return true;
}
function files_to_choices(files: string[]): Choice[] {
return files.map(file => ({
name: path.basename(file, '').split('.')[0],
value: file
}));
}
async function get_snippet_files() {
// Untested attempt to fix windows paths (I don't have a windows machine)
const snippet_files = await globby(`${snippet_path}/*`);
if (process.platform == 'win32') {
return snippet_files;
}
return snippet_files.map(file => file.replace(/\\/g, '/'));
}
async function get_content(snippet: string) {
const snippet_content = await readFile(snippet, 'utf8');
const snippet_lines = snippet_content.split('\n')
// Remove comments and empty lines until first line of snippet
return snippet_lines
.slice(snippet_lines.findIndex(line => !line.startsWith('//')))
.join('\n');
}
async function insert_selection(content: string) {
if (content.includes('$SELECTION$') === false) {
return content;
}
const selection = await getSelectedText();
content = content.replace('$SELECTION$', selection);
return content;
}
async function insert_clipboard(content: string) {
if (content.includes('$CLIPBOARD$') === false) {
return content;
}
const text = await clipboard.readText();
content = content.replace('$CLIPBOARD$', text);
return content;
}
// 🚀 Go!
const snippet_file = await arg("Which snippet?", files_to_choices(await get_snippet_files()));
let content = await get_content(snippet_file);
content = await insert_selection(content);
content = await insert_clipboard(content);
content = await dangerous_evil_parse(content);
if (!(await set_with_cursor(content))) {
await setSelectedText(content);
}

Props @johnlindquist for helpful pointers 👍