Thanks for your interest in contributing. Here's how to get set up.
- Fork and clone the repo
- Open
chrome://extensions/in Chrome - Enable Developer mode
- Click Load unpacked and select the cloned
demarkup/folder - Make changes — the extension reloads when you click the refresh icon on the extensions page
No build step, no dependencies to install.
- Create a branch from
main - Make your changes
- Test on a few different page types (articles, docs, code-heavy pages, pages with heavy nav/ads)
- Verify context menu, keyboard shortcut (
Cmd+Shift+M), and popup all work - Open a PR
| Directory | Purpose |
|---|---|
background/ |
Service worker — context menus, keyboard shortcuts, message routing, script injection |
content/ |
Content script — HTML extraction, cleanup, Turndown conversion, front matter |
popup/ |
Popup UI — copy/download buttons, mode toggle, stats display |
options/ |
Settings page — preferences stored via chrome.storage.sync |
lib/ |
Vendored libraries (Turndown.js, GFM plugin) and shared utilities (logger) |
- Keep PRs focused — one feature or fix per PR
- Plain vanilla JS — no TypeScript, no frameworks, no build tools
- Use
const/let— nevervar - Prefer
async/awaitover.then()chains - Do not edit vendored files in
lib/turndown.jsorlib/turndown-plugin-gfm.js - Keep permissions minimal — don't add new Chrome permissions without justification
- Test diverse pages — articles, tables, code blocks, pages with heavy nav/ads
- Content script is injected dynamically by the service worker, not declared in the manifest
- The service worker is ephemeral (Manifest V3) — no persistent state, use
chrome.storage - Three conversion modes: main content extraction, full page, and selection
- Token count is estimated (
chars / 4), not from a real tokenizer - All conversion logic lives in
content/content.js— keep it self-contained
Open an issue — happy to help.