PopClip Developer Documentation

website·Indexed 34 days ago

https://www.popclip.app/

Documentation for creating PopClip extensions, including guides on JavaScript module extensions, shell script actions, AppleScript interfaces, and shortcut actions. Covers the JavaScript environment (popclip, util, pasteboard), config formats, action properties, icon implementation, and authentication for external services.

Tokens
28.7K
Snippets
116
Records
242
Agent score
70%

What's inside PopClip

  1. Understand the difference between PopClip Actions and Extensions

    In PopClip, actions and extensions are related but distinct concepts:

    • Action: The actual button that appears in the PopClip bar. You can rearrange, rename, and duplicate actions.
    • Extension: The package that provides the action. You install, update, and remove extensions.

    One extension can provide multiple instances of an action (e.g., one translation extension configured as two separate actions for different languages). Deleting all actions associated with an extension uninstalls the extension, except for built-in extensions which can be restored from the Manage Extensions sheet.

  2. Understand the PopClip JavaScript environment and supported APIs

    PopClip uses Apple's JavaScriptCore engine. Scripts run in a secure sandbox and cannot access the filesystem directly.

    Language Support:

    • Supports ES2023 features on macOS 13 and later (via core-js polyfills).

    Available Web APIs:

    • URL, URLSearchParams
    • XMLHttpRequest
    • setTimeout, clearTimeout, setInterval, clearInterval
    • structuredClone

    Node.js Globals:

    • Buffer is available.
    • Blob, TextEncoder, atob, and btoa are provided as reduced compatibility shims; use Buffer or util encoding functions instead.
  3. Available PopClip script variables

    The following variables are available to shell scripts and AppleScripts to provide context about the selected text and the environment.
    Shell ScriptAppleScriptDescription
    POPCLIP_TEXT{popclip text}The part of the selected plain text matching the specified regex or requirement.
    POPCLIP_FULL_TEXT{popclip full text}The selected plain text in its entirety.
    POPCLIP_HTML{popclip html}Sanitized HTML for the selection (requires captureHtml).
    POPCLIP_URLENCODED_TEXT{popclip urlencoded text}URL-encoded form of the matched text.
    POPCLIP_RAW_HTML{popclip raw html}Original unsanitized HTML (requires captureHtml).
    POPCLIP_MARKDOWN{popclip markdown}HTML converted to Markdown (requires captureHtml).
    POPCLIP_URLS{popclip urls}Newline-separated list of detected web URLs.
    POPCLIP_MODIFIER_FLAGS{popclip modifier flags}Numeric value of keys held down during click.
    POPCLIP_BUNDLE_IDENTIFIER{popclip bundle identifier}Bundle ID of the source app (e.g., com.apple.Safari).
    POPCLIP_APP_NAME{popclip app name}Name of the source app (e.g., Safari).
    POPCLIP_BROWSER_TITLE{popclip browser title}Title of the web page (supported browsers only).
    POPCLIP_BROWSER_URL{popclip browser url}URL of the web page (supported browsers only).
    POPCLIP_OPTION_*{popclip option *}Value for an option specified in the extension's options config. Booleans are 0 or 1.
    POPCLIP_EXTENSION_IDENTIFIER{popclip extension identifier}The extension's identifier.
    POPCLIP_ACTION_IDENTIFIER{popclip action identifier}The identifier specified in the action's configuration.
  4. Implement username and password sign-in

    To authenticate with a username and password, define options with identifiers username and password.

    Crucially, an option of type password is never stored by PopClip; it is passed to the auth function once to exchange it for a token. Only the resulting token (the authsecret) is persisted in the keychain.

    // #popclip
    // name: Pinboard
    // entitlements: [network]
    import axios from "axios";
    const api = axios.create({
      baseURL: "https://api.pinboard.in/v1/",
      params: { format: "json" },
    });
    defineExtension({
      options: [
        { identifier: "username", type: "string", label: "Username" },
        { identifier: "password", type: "password", label: "Password" },
      ],
      auth: async (info) => {
        // info contains the username and password options
        const response = await api.get("user/api_token", { auth: info });
        return response.data.result; // This becomes the authsecret
      },
      action: async (input, options, context) => {
        const auth_token = `${options.username}:${options.authsecret}`;
        await api.get("posts/add", { params: { /* ... */ auth_token } });
      },
    });
  5. Create a PopClip action using AppleScript

    AppleScript actions allow for automation and control of other macOS applications. They can be defined as a code snippet with a -- #popclip config header or via a JSON configuration using appleScript (inline source) or appleScriptFile (path to .applescript or .scpt file).

    In plain text scripts, use placeholders like {popclip text} which PopClip replaces before execution. Scripts can return a string (e.g., return "foo") to be used by a subsequent after step.

    -- #popclip
    -- name: LaunchBar
    -- icon: LB
    tell application "LaunchBar"
      set selection to "{popclip text}"
    end tell
  6. Uninstall PopClip

    To completely remove PopClip from your system:

    1. Quit PopClip if it is currently running.
    2. Drag the PopClip app from the Applications folder (or the Setapp folder) to the Trash.
    3. To remove all settings, extensions, and supporting files, manually delete the files and folders listed in the 'Paths and identifiers' documentation.
  7. Create an Open URL action

    Open URL actions allow PopClip to open a URL generated from a template. If the URL scheme is http: or https: and the current app is a known browser, PopClip will use that browser; otherwise, it uses the macOS default app for the scheme. Selected text is inserted into the URL, replacing the {popclip text} or *** placeholder. PopClip automatically trims leading/trailing whitespace and URL-encodes the text.
    # Example: Search Rotten Tomatoes
    name: Rotten Tomatoes
    icon: iconify:simple-icons:rottentomatoes
    url: https://www.rottentomatoes.com/search?search=***
  8. Understand text narrowing and matching order

    PopClip processes filters in a specific sequence to determine the text passed to an action:

    1. Requirements: If url, isurl, email, or path are used, PopClip narrows the working text to the detected value and normalizes it (e.g., adding https:// to URLs or expanding ~ in paths).
    2. Regex: If a regex is specified, it is applied to the narrowed text from step 1. If it matches, the match result becomes the text passed to the action.

    Developers can access different versions of the text:

    • Shell/AppleScript: POPCLIP_TEXT (narrowed) and POPCLIP_FULL_TEXT (original selection).
    • JavaScript: popclip.input.matchedText (narrowed), popclip.input.regexResult (match array with captures), and popclip.input.text (full string).
    # Example: Narrow to URL, then extract host via regex
    #popclip
    name: Domain WHOIS
    requirements: [url]
    regex: (?<=://)[^/]+
    url: https://www.whois.com/whois/***
  9. Sync PopClip extensions and actions via iCloud

    PopClip can synchronize your setup across multiple Macs using iCloud.

    What is synced:

    • Installed extensions.
    • The arrangement of the actions list (including folders, separators, custom names, and icons).
    • Individual action settings.
    • Secrets (API keys, sign-in credentials) are synced separately via the iCloud Keychain.

    What is NOT synced:

    • App-wide settings (General and App tabs).
    • Website or app exclusion rules.

    Behavior:

    • Merging: When first enabled, PopClip merges the local actions list with the iCloud list rather than overwriting it.
    • Convergence: Changes propagate via iCloud and may not be instant. macOS Low Power Mode can significantly delay synchronization.
  10. Require selected text to be a single URL

    Use the isurl key in the extension requirements array to ensure the selected text is a single URL. This is more restrictive than the url key, which matches text containing a URL. This requirement makes the popclip.input.isUrl property available to non-JavaScript extensions.
  11. Use TypeScript for PopClip extensions

    PopClip supports TypeScript for JavaScript actions and module extensions. To use it:

    • Specify a .ts file in the javascript file or module field.
    • For snippets, set the language field to typescript.
    • Use the @popclip/types NPM package for type definitions.
  12. Specify icons in PopClip extensions

    Icons in PopClip are defined using a text-based specifier string. A specifier consists of space-separated keywords: the final keyword is the base icon, and any preceding keywords are modifiers.

    Developers can use the built-in Icon Picker (Tools menu in settings or ⌥⌘I) to search Iconify libraries and preview modifiers. Alternatively, PNG renderings of specifiers can be fetched via: https://icons.popclip.app/icon?specifier=<url-encoded specifier>.