Open-source project
pilotmoon/PopClip-Extensions avatar
pilotmoon/PopClip-Extensions

PopClip's extension repo now splits support, signing and submission

Source code for extensions maintained by @pilotmoon in the official PopClip Extension Directory

2,015 stars469 forksJavaScriptMIT

At a glance

What is it?
pilotmoon/Popclip-Extensions holds the JavaScript sources behind part of the PopClip Extensions Directory, split across a folder its author supports and a folder of experimental leftovers. Signing has moved to the directory server, so the installable zips this repository used to carry no longer exist in it.
Who is it for?
Adopt this repository when you are fixing an extension that already ships in the PopClip directory and want the change to reach code people actually install. Do not adopt it as a launchpad for a new extension: submissions run through a separate page, and the server handles signing now.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two folders answer the question of who fixes a bug

Every extension in this repository sits in one of two folders, and the folder decides who is answerable for it. `source` holds the sources for the published extensions and is the part its author maintains and supports. `contrib` is where unpublished, user-contributed, experimental, niche and abandoned work waits, and nothing inside it is maintained or supported. The boundary takes one line each and is then never revisited:

text
- `source` - sources for the published extensions (maintained and supported by
  me)
- `contrib` - folder for unpublished / user-contributed / experimental / niche /
  archived extensions (not maintained or supported)

That split is the most useful thing to know before opening a pull request here. A fix to something in `source` changes code that directory users install, while the same edit applied to something in `contrib` changes code nobody has promised to keep alive. There is no CODEOWNERS file, no per-folder manifest and no other ownership marker among the top-level entries, so the folder name is the entire contract. Contributors who target the wrong folder will hear about it from the maintainer rather than from any automated check.

Signing left the repository and the old zips went with it

Extension signing used to produce `.popclipextz` zip files kept in an `extensions/` directory, and those files are gone. Signing now happens automatically on the server, which means this repository holds no signed artifact for anything inside it, and `extensions/` is absent from the top-level entries entirely. What survives is the tool that used to work on them. package.json still wires the `convert` script to `misc/pcxconvert`, and `misc/` remains a top-level directory, so the tree keeps a converter for a format it no longer emits. That mismatch is the clearest single example of what the author means when he calls this a large build up of over a decade of cruft he is trying to tame down bit by bit. The script was left wired up, so it still looks runnable to anyone reading package.json from top to bottom.

One directory now draws extensions from many repositories

This repository used to be the only place extensions came from, and that has changed. The directory backend accepts extensions from multiple repositories, so this one is now just one of many extension repos, with its structure being reshaped to match. Two consequences follow for anyone holding an extension. New extensions no longer arrive as pull requests, because the repository is closed for new submissions and authors are pointed at a separate submit page, while pull requests that fix or improve extensions already in `source` stay welcome. And the project's own GitHub description still advertises it as the source code for extensions maintained in the official directory, which no longer agrees with what the same file says further down. An author who reads only the description will plan a contribution the repository will refuse.

The check script is the only gate package.json wires up

Two scripts, and that is the whole command surface:

json
{
  "scripts": {
    "check": "tsc --noEmit",
    "convert": "misc/pcxconvert"
  }
}

The `check` script runs the TypeScript compiler with no emit, so it type-checks and does nothing else. There is no build script, no test script and no bundler entry point in the file, which means nothing in this repository compiles an extension into something a user could install. Sitting right beside a package.json that never calls it, `biome.json` and the `@biomejs/biome` 2.0.0 dependency define formatting and lint rules that no command here enforces. `.node-version` pins the Node a checkout is expected to match, `tsconfig.json` carries the compiler settings, and the checker is pinned at typescript 7.0.2, so a contributor on another major version of the compiler gets a different verdict from the same code. The README does not document how to invoke either script.

Two validators and two HTTP clients share one dependency tree

A single package.json carries more than thirty runtime dependencies, and several jobs are covered twice. zod 3.25.76 and valibot 1.1.0 both validate input. axios 1.12.2 and superagent 9.0.2 both reach the network, and only the second one has a matching type package at `@types/superagent` 8.1.11. Hashing is split between crypto-js 4.2.0 with `@types/crypto-js` 4.2.2 and `@noble/hashes` 1.8.0. HTML handling runs through sanitize-html 2.17.0, html-entities 2.6.0, entities 7.0.0, htmlparser2 10.0.0, dom-serializer 2.0.0 and linkedom 0.18.12, the last being a DOM implementation for extensions that need one inside a text pipeline. Math arrives twice as well, through mathjs 10.6.4 and latextomathml 0.0.4, and Chinese conversion rides alongside js-yaml 4.1.0 in tongwen-core 4.1.1. Bundling support sits in browserify 17.0.1, esbuild 0.21.5 and core-js 3.50.0. At the tail of the same file the two validators land one line apart:

json
"valibot": "1.1.0",
"voca": "1.4.1",
"zod": "3.25.76"

Nothing in the repository says which of the two a new extension is meant to use, so both stay pinned until someone removes one by hand.

Four directories ship where the layout section names two

The README calls its list the main folders and offers two of them. The actual top-level entries run longer: .gitignore, .node-version, biome.json, contrib/, lib/, misc/, package.json, popclip-directory.yaml, source/ and tsconfig.json, plus two paperwork files handled further on. Four directories ship where two are described, and `lib/` never appears in prose at all. It shows up exactly once, as the target of a local dependency:

json
"@popclip/helpers": "file:lib/popclip-helpers",
"@popclip/types": "2.6208.0",

`@popclip/helpers` is not fetched from a registry, it resolves to a package inside `lib/`, so a working tree needs that folder before anything installs cleanly. `@popclip/types` at 2.6208.0 is the only version number in the tree that does not read like a semantic version, and the README does not explain the numbering. The other unexplained entry is `popclip-directory.yaml`, the one top-level name that sounds like the manifest a directory backend would read to learn what this repository offers. No file describes its keys, its schema, or whether the submit page even reads it.

Getting an extension from this repository to a user

There is no install command here, and that is closer to a property of the design than a gap in the documentation: nothing in this repository builds an application. The top-level entries hold no application entry point, and the two scripts are a type check plus a format converter. End users take extensions from the directory at popclip.app/extensions/, where signing is now handled automatically on the server. A contributor's route is short but entirely social: open a pull request against an extension already in `source`, stay inside the ownership boundary its folder draws, and let the server handle packaging. Anyone starting something new does not start it here. The README sends them to a submit page whose requirements it never spells out, so what that page asks for, how long review takes and who approves all sit outside this repository.

Where the authoring rules moved, and what a clone no longer carries

The rules for writing an extension are not in this repository any more. Developer documentation has moved to popclip.app/dev/, and the old CHEATSHEET.md is replaced by an authoring skill published at popclip.app/dev/skills/popclip-extension-authoring/SKILL.md. That file lives on the website rather than in git, so a fresh clone carries examples of extensions other people wrote but not the instructions for writing one. Credits sit in each extension's own readme file, which scatters provenance across `source/` and `contrib/` instead of collecting it in one place. Two top-level files cover the paperwork rather than the code: LICENSE.txt holds the MIT text the README points at, and CHANGELOG.md sits there even though the project publishes no GitHub releases and every extension keeps its own history in its readme file. The last push was on 2026-09-28, so this is moving code rather than a frozen copy.

Editorial conclusion

Adopt this repository when you are fixing an extension that already ships in the PopClip directory and want the change to reach code people actually install. Do not adopt it as a launchpad for a new extension: submissions run through a separate page, and the server handles signing now. Before a first pull request, confirm which folder the extension sits in, run the check script against your own Node version, and read the authoring rules on popclip.app/dev/ rather than in the clone. Folder placement, not the type checker, decides whether a change gets taken.

Frequently asked questions

Where does a new PopClip extension get submitted now?

New submissions do not go through this repository. Authors are sent to the submit page at popclip.app/extensions/submit, and the repository states that it is closed for new submissions while still accepting pull requests that fix or improve extensions already published there.

What is the difference between the source and contrib folders?

The source folder holds the sources for published extensions and is maintained and supported. The contrib folder holds unpublished, user-contributed, experimental, niche and older extensions, none of them maintained or supported.

Which licence covers the code in pilotmoon/Popclip-Extensions?

All source code is published under the MIT License, unless an individual extension readme states otherwise. Those same per-extension readme files carry the credits and the changelog for each extension.

Can I install an extension straight from this repository?

No install path exists for end users here. Extensions are published through the PopClip Extensions Directory, signing is handled automatically on the server, and this repository only carries the JavaScript sources behind them.

What does the check script actually verify?

It runs tsc --noEmit, a type check that emits nothing. The file defines only two scripts, check and convert, and convert runs misc/pcxconvert. There is no build script and no test script, so nothing in the repository compiles an extension for installation.

Does Popclip-Extensions publish any of its own packages?

One dependency does not come from a registry: @popclip/helpers is declared as file:lib/popclip-helpers and resolves to a package inside the repository. Everything else in package.json is pinned to a registry version.

Official sources

  1. Issues
  2. License: MIT
  3. pilotmoon/PopClip-Extensions on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pilotmoon-popclip-extensions.svg)](https://hysenlabs.com/projects/pilotmoon-popclip-extensions)

Was this useful?