# cpdown, a browser extension that copies a page as clean markdown

> cpdown is a WXT browser extension that extracts the main content of any page, converts it to markdown and puts it on the clipboard, with a separate path for YouTube subtitles and a token count for pasting into an LLM. It does one small job, and the extension libraries it stands on do the hard work.

**ysm-dev/cpdown** — 📥 cpdown - Copy to clipboard any webpage content/youtube subtitle as clean markdown with one click or shortcut

- Repository: https://github.com/ysm-dev/cpdown
- Website: https://chromewebstore.google.com/detail/cpdown/knnaflplggjdedobhbidojmmnocfbopf
- Stars: 582 · Forks: 34
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/ysm-dev-cpdown

## One job, done from the toolbar

cpdown is a browser extension that lets you copy the content of any webpage as clean, formatted markdown, and if you are on YouTube you can also copy the subtitle as markdown. The project description calls it copying any webpage content or YouTube subtitle as clean markdown with one click or a shortcut.

The scope is deliberately narrow, and that is the appeal. The general problem of getting a readable version of a web page into a language model is solved by a family of tools, most of which are scripts you have to run somewhere, with a browser session to export, or a reader-mode bookmarklet. cpdown is an extension, so it is always present, it works on the page you are already looking at, and it produces markdown directly.

The feature list gives the mechanism. It uses Defuddle or Mozilla's Readability to extract the main content, it removes unnecessary HTML elements such as scripts, styles and iframes, it shows a token count for the copied content, which is there for LLM use, and it supports a keyboard shortcut.

The token count is the detail that tells you who this is for. Counting tokens of the extracted text before it reaches the clipboard is a feature for someone about to paste it into a model, because that number is what a context window is measured in. A general reader tool has no reason to compute it.

The audience is a developer or researcher who reads on the web and pastes into a language model, or into an issue tracker, or into a document. The licence is MIT, the last push to the repository was on 2026-10-19, and there are no GitHub releases, so it is consumed from the Chrome Web Store and the Firefox add-ons site.

## Defuddle and Readability, and the difference between them

The extraction is done by one of two libraries, and they are not interchangeable.

Mozilla Readability is the older and better known of the pair. It is the algorithm behind reader mode in Firefox, and it works by scoring the candidate elements of a page and picking the one most likely to be the article body. It has a decade of tuning behind it on real news sites, blogs and documentation pages.

Defuddle is the newer option, from the author of Obsidian Web Clipper, and the project describes it as being for main content extraction and markdown cleanup. The cleanup part matters. Readability's output is an HTML subtree, and you then need to convert that to markdown, whereas Defuddle is built to produce cleaned markdown directly.

The settings page exposes both as options, labelled Use Defuddle and Use Mozilla Readability, so you can switch between them per installation and judge for yourself which one handles the sites you read. That is a sensible design, because the two libraries disagree often enough on technical pages that a single choice would frustrate somebody.

The HTML to markdown conversion itself is done by Turndown, which is a separate dependency in the list, and Defuddle is listed for main content extraction and markdown cleanup while Turndown is listed for HTML to Markdown conversion. So the pipeline is roughly: pick an extractor, get a cleaned content region, convert the HTML to markdown, optionally wrap it, and count the tokens.

The removal of scripts, styles and iframes is a separate step from the extraction itself, and it is worth noting that it is the kind of thing that is easy to do and easy to overdo. Stripping iframes is right for most pages and wrong for any page whose content is inside a frame, which includes a fair number of documentation sites and every video embed.

## The options page and the small touches

The settings are five toggles, and each is small in a way that suggests the author uses this tool daily.

Use Defuddle and Use Mozilla Readability are the extractor choice. Wrap in Triple Backticks wraps the copied content in triple backticks for better readability, which is what you want when the markdown is going into a chat box or a code fence. Show Success Toast displays a notification when content is successfully copied, which is a small but real quality-of-life difference when you are pressing a shortcut and looking at the page. Show Raycast Confetti celebrates a successful copy with a confetti animation, and the setting is explicitly marked as being for Raycast users.

That last option is the only one that suggests anything about the author's own setup, and it is a nice signal about the intended workflow: copy from the browser, paste into a tool, and get a small confirmation that it worked.

The options page is reached through the extension details page, with a direct link to `chrome://extensions/?options=knnaflplggjdedobhbidojmmnocfbopf` documented, and the project notes that clicking the Options link in the extension's details page also works. The options UI itself is built with React, Shadcn UI and Tailwind, and the toasts use Sonner.

Everything happens locally, and that is worth noting. The extension copies to the clipboard on your machine, and there is no server component in the dependency list, no analytics library and no account system. The `privacy.md` file at the repository root is the place to check what that claim is based on. For a tool whose entire purpose is to move the contents of pages you visit into another application, the absence of a backend is the most important architectural fact about it.

## Building it locally with WXT and bun

The development stack is the modern extension toolchain, and the manual installation instructions are four steps.

```bash
   bun i
```

That installs the dependencies, and the build step is a second command.

```bash
   bun run build
```

Then you load the unpacked extension. The instructions say to open Chrome or Edge, go to `chrome://extensions`, enable Developer mode, click Load unpacked and select the `.output/chrome-mv3` directory. The output path tells you the manifest version, MV3, and the `.output` directory name is the WXT convention where builds land.

The stack list in the documentation names the tools. WXT is the Web Extension Toolkit and it is what handles the manifest, the build and the packaging, and it is why there is a single `wxt build` command rather than a webpack config per browser. React with Shadcn UI and Tailwind CSS is for the options UI. Sonner is for the toasts. Cursor appears in the list with the description for the vibe coding, which is a candid note about how the project is built. Defuddle, Mozilla Readability and Turndown do the extraction and conversion, and tiktoken does the counting, brought in as js-tiktoken.

The package manifest shows how the multi-browser story is handled. There is a `dev` script that runs `wxt`, a `dev:firefox` script that runs `wxt -b firefox --mv2`, and matching `build`, `build:firefox` and `zip` pairs. The Firefox build uses Manifest V2, which the `--mv2` flag selects, while the Chrome build is MV3 as the manual install path shows. The `submit` script packages the Chrome zip and hands it to `wxt submit` for the store.

There is also a `compile` script that runs `tsc --noEmit`, a type check with no emit, and a `check` script that runs `taze latest -l`, which is the linter configured through the `biome.jsonc` file at the repository root. The `postinstall` hook runs `wxt prepare`, which generates the type definitions WXT needs before the first build.

## YouTube subtitles, and the limits of a one-button tool

The YouTube path is a separate feature rather than a variant of the page extraction, and the reason is practical. A YouTube watch page is not an article, so the readability heuristics have nothing useful to grab, and the content you actually want is the transcript.

The subtitle handling pulls in `srt-parser-2` and `ofetch` for the network request, and the documentation lists copying YouTube subtitle as clean markdown as its own feature with its own click or shortcut. The result is a transcript in markdown rather than the video page's HTML, which is what you want if you are asking a model to summarise a talk.

The limitation of the whole approach shows up here first. Subtitles come in two kinds, human-authored and machine-generated, and the machine-generated ones carry no punctuation and no line structure worth keeping. A transcript in markdown that has lost all its punctuation is a poor input for a model and a poor thing to read, and the extension does not document any cleanup of the subtitle text.

The other boundary is the one that applies to every page. Extraction is a heuristic, and a heuristic can be wrong. The documentation is upfront that it uses Defuddle or Readability to extract the main content, and either can make a different judgement about what counts as content on an unusual page. Documentation sites with heavy navigation, sites that are one long application, and pages whose body is inside a frame are all cases where a one-click tool can disappoint.

What the extension does not attempt is any kind of site-specific handling. There is no per-site rules list, no selector override, no fallback to copy the raw HTML. For a general reading workflow that is the right simplification, because most pages are ordinary articles, and for a workflow with awkward pages it is a hard limit rather than a missing option.

## Conclusion

cpdown suits someone who reads documentation or articles in the browser and pastes them into a model, a note, or a ticket, and who wants markdown rather than a page full of navigation and script tags. It does not suit a workflow that needs the page verbatim, since the extraction is a readability heuristic and will drop content that the algorithm judges to be chrome. Verify first that Defuddle or Readability extracts the kind of page you read, since they disagree on some sites, and that the YouTube subtitle path handles the video you care about, since subtitle formats and auto-generated captions vary.

## FAQ

### What does cpdown do?

It is a browser extension that copies the content of any webpage as clean, formatted markdown, and on YouTube it can copy the subtitle as markdown. It extracts the main content with Defuddle or Mozilla Readability, strips scripts, styles and iframes, and shows a token count for the copied content.

### How do I install cpdown?

From the Chrome Web Store or the Firefox add-ons site. For a manual install, clone the repository, run bun i and bun run build, then open chrome://extensions in Chrome or Edge, enable Developer mode and use Load unpacked on the .output/chrome-mv3 directory.

### What is the difference between Defuddle and Readability in cpdown?

Both are offered as options in the settings page. Defuddle is used for main content extraction and markdown cleanup, while Mozilla Readability parses the page content and the HTML to Markdown conversion is handled by Turndown. Switching between them lets you compare the two on the sites you actually read.

### Does cpdown send the page content anywhere?

The documentation describes a fully local workflow with the extension copying to the clipboard on your machine, and there is no server component, analytics library or account system in the dependency list. The privacy.md file in the repository is where to check the details of that claim.

### What can cpdown not do?

It does not preserve the page verbatim, since extraction is a readability heuristic and will drop content judged to be chrome, and there is no per-site override or raw HTML fallback. YouTube subtitle quality depends on the caption track, and the documentation does not describe any cleanup of machine-generated captions.

## Sources

- [Issues](https://github.com/ysm-dev/cpdown/issues)
- [License: MIT](https://github.com/ysm-dev/cpdown/blob/main/LICENSE)
- [Project website](https://chromewebstore.google.com/detail/cpdown/knnaflplggjdedobhbidojmmnocfbopf)
- [README](https://github.com/ysm-dev/cpdown/blob/main/README.md)
- [ysm-dev/cpdown on GitHub](https://github.com/ysm-dev/cpdown)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ysm-dev-cpdown
