# JavaScript actions A JavaScript action runs code in PopClip's own [JavaScript environment](https://www.popclip.app/dev/js-environment.md), with access to the selected text and to PopClip itself through the global `popclip` object. It is the simplest way to run code in PopClip. In a [code snippet](https://www.popclip.app/dev/snippets.md#inverted-syntax), everything after the header is the action's code, run when the action is clicked: ```javascript // #popclip // name: Uppercase // icon: square filled AB // after: paste-result return popclip.input.text.toUpperCase(); ``` ## Properties A JavaScript action is defined by a code snippet whose config header uses the `//` comment prefix. Alternatively, a config snippet may define a `javaScript` or `javaScriptFile` field, as follows: | Key | Type | Description | | ---------------- | ------ | ------------------------------------------------------- | | `javaScript` | String | A JavaScript text string to load. | | `javaScriptFile` | String | Path to a `.js` or `.ts` file in the package directory. | For example, here is a config snippet with the action's code inline in the `javaScript` field: ```yaml #popclip name: Word Count icon: square 123 javaScript: popclip.showText(popclip.input.text.split(/\s+/).length + " words") ``` A code snippet is equivalent to a config snippet whose `javaScriptFile` is the snippet itself. ### Script format The script's entry point is at the top level of the file. Internally, PopClip loads the provided script and wraps it as a function. When the action is run, PopClip calls the function. **Function wrapper detail** As an example, imagine the following JavaScript is provided in the `javaScript` field: ```javascript return "foo"; ``` Internally, this will be wrapped in an async arrow function definition like this: ```javascript const main = async () => { return "foo"; }; ``` When the action is run, PopClip calls this internal `main` function with no arguments. In addition to pure JavaScript, PopClip can load TypeScript from files named with a `.ts` extension. See [TypeScript support](https://www.popclip.app/dev/js-environment.md#typescript-support). ## Input and output Scripts take their input from the [global `popclip` object](https://www.popclip.app/dev/js-environment.md#global-popclip-object). If the script exits by returning a string, it will be passed to the `after` step. **Note: Return type** To return a value to the `after` step, it must be of type `string`. If the script returns a value of any other type, such as `number` or `object`, PopClip will ignore it. ## Indicating errors Scripts should indicate success by completing normally (either by explicitly returning a value, or implicitly returning `undefined`) and should indicate failure by throwing an error. PopClip will catch any erros thrown by the script and display the shaking-'X'. To indicate an error with the user's settings, and pop up the extension's settings UI, throw an error message starting with the specific words `settings error` or `not signed in` (not case sensitive). For example: ```javascript throw new Error("Settings error: missing API key"); ``` ## Examples These examples are all complete [code snippets](https://www.popclip.app/dev/snippets.md#inverted-syntax) — select the whole block to install one. Paste the selected text, then press Return — two PopClip primitives chained with `await`: ```javascript // #popclip // name: Paste & Enter // icon: symbol:return // requirements: [paste] await popclip.pasteText(popclip.input.text); await popclip.pressKey("return"); ``` Send the selection to a web app that has no query parameter for it. A [URL action](https://www.popclip.app/dev/url-actions.md) can't help here, so instead the script opens the page, waits with [`sleep()`](https://www.popclip.app/dev/js-environment.md#asynchronous-operations-and-async-await) for it to load, puts the text on the clipboard and pastes it with a key press. A workaround, but it works: ```javascript // #popclip // name: Gemini // icon: symbol:sparkles // description: Open Gemini and paste the selection. // popclipVersion: 6221 await popclip.openUrl("https://gemini.google.com/app"); await sleep(1000); pasteboard.text = popclip.input.text; await popclip.pressKey("command v"); ``` Look up the selected word in the macOS dictionary, then speak the definition aloud through the `say` command — using the [`$` shell tag](https://www.popclip.app/dev/external-scripts.md): ```javascript // #popclip // name: Speak Definition // icon: symbol:character.book.closed // entitlements: [script] const word = popclip.input.text.trim(); const definition = util.getDictionaryDefinition(word) ?? "no definition found"; await $`say ${definition}`; ``` Fetch the page at the selected URL and show its title — network access, a bundled module, and the `after` step working together: ```javascript // #popclip // name: Page Title // icon: symbol:globe // requirements: [url] // entitlements: [network] // after: show-result const axios = require("axios"); const response = await axios.get(popclip.input.data.urls[0]); return ( String(response.data).match(/]*>([^<]*)