MGT Copy Markdown - Chrome extension
![]()
Rebuilding an old Chrome extension for Manifest V3

I went looking for a little extension I used to rely on — Copy Markup Markdown: select some text on a page, right-click, get it back as Markdown instead of a pile of pasted HTML. It’s still listed on the Chrome Web Store, but Chrome refuses to actually install it, with the unhelpful “it’s not compatible anymore” message — so I pulled the raw .crx with a crx-downloader tool instead, since the Store’s own install button won’t hand it to you once it’s been disabled like that.
Turned out the reason was mundane: it was Manifest V2, and Chrome has been phasing MV2 out for a while now. Nothing sinister — but since the file came in sideways rather than through Chrome’s normal install flow, it seemed worth checking before trusting it with anything. A .crx is really just a Cr24 header plus a signature blob glued onto an ordinary zip, so it’s a two-minute job to strip the header and go through the manifest and JS by hand: permissions, any eval/remote script loading, network calls. This one was clean — broad <all_urls> access, but nothing exfiltrating anything, and the update_url pointed straight at Google’s own Web Store endpoint, matching the fact that it’s a genuine, currently-listed CWS publish rather than a repackaged one.
Rather than patch a five-year-old codebase, I rebuilt it from scratch on Manifest V3: an event-driven service worker instead of a persistent background page, navigator.clipboard.writeText() with a legacy fallback, DOMPurify instead of a 330KB sanitizer bundle that had somehow dragged in unrelated postcss code, and Readability.js — the same engine behind Firefox’s Reader View — so a full-page copy skips the nav bar, sidebar, and ad slots instead of dumping the whole DOM. Added a few things the original never had: downloading straight to a .md file, keyboard shortcuts, and an actual options page instead of a static “about” screen.

The one genuinely interesting bug: dragging a selection down a column of an admin data grid — highlighting just three cells per row — was pulling in the entire row’s hidden markup: checkboxes, action buttons, icon SVGs, none of it visible, all of it copied. Turns out the browser only paints the highlight over what you dragged across, but the underlying Range object still spans everything structurally between the start and end point in DOM order. Range.cloneContents() faithfully serializes all of it. selection.toString(), on the other hand, is computed by the browser with proper table-aware logic and gets it right — so that’s what the fix ended up using instead of the cloned HTML, for exactly this case.
It’s live now: MGT Copy Markdown on the Chrome Web Store, if any of the above sounds useful.