Chrome extension that renders pages with MIME type text/markdown (and .md files served as text/plain) as formatted Markdown with GitHub-style CSS — including syntax highlighting and Mermaid diagrams. Toolbar popup lets you switch theme and flip between rendered and raw views.
- Open
chrome://extensions - Toggle Developer mode (top-right)
- Click Load unpacked and select this folder
- (Optional, for local
.mdfiles) Click Details on the loaded extension → enable Allow access to file URLs
- Auto-renders
text/markdownandtext/x-markdownresponses - Falls back to URL extension detection (
.md,.markdown,.mdown,.mkd,.mkdn) when the server sendstext/plain - GitHub-flavored Markdown via marked
- HTML sanitized with DOMPurify
- Syntax highlighting via highlight.js Common build (~30 languages) — code colors follow the active theme
- Mermaid diagrams:
```mermaidblocks render as SVG via Mermaid (lazy-loaded only on pages that contain one), theme-synced and re-rendered on theme flip - Theme override: Auto (follows OS), Light, Dark — synced via
chrome.storage.sync - Toggle between rendered and raw view per page:
- Footer link on rendered page: See original / See rendered
- Toolbar popup button: same toggle
- Localized UI in 17 languages: English, Português (BR), Português (PT), Español, Français, Deutsch, Italiano, 日本語, 简体中文, 繁體中文, Русский, हिन्दी, বাংলা, ਪੰਜਾਬੀ, Türkçe, עברית, العربية
- Toolbar icon (16/32/48/128 PNGs)
- Opens the feedback issue when uninstalled
- Opens the changelog when Chrome updates the extension to a new version
- Local file: load
sample.md(after enabling file URL access):file:///C:/Document/DevData/pgp/projects/md-viewer-ultra/sample.md - Live: any URL serving
Content-Type: text/markdown, or a raw.mdURL served astext/plain(e.g.https://raw.githubusercontent.com/markedjs/marked/master/README.md).
content.jsruns on every page atdocument_end. Exits unless the response is markdown.- Reads the raw text (Chrome wraps text/* responses in a single
<pre>), parses with marked, sanitizes with DOMPurify, swaps the body for<article class="markdown-body">…</article>plus a footer. - Theme override: re-declares the same CSS variables
github-markdown.cssdefines inside its@media (prefers-color-scheme: …)blocks, but underhtml.mvu-force-{dark,light} .markdown-bodyselectors. Higher specificity wins, so the override beats the OS preference. - Popup ↔ content script communication:
chrome.tabs.sendMessagecarriesgetState/toggleRendermessages. If the active tab isn't a markdown page, the popup shows a "Not a markdown page" notice instead of the toggle button. - Theme preference lives in
chrome.storage.sync({ darkMode: "auto" | "light" | "dark" }). The content script listens tochrome.storage.onChangedso flipping the popup updates every open markdown tab live. - Mermaid:
```mermaidcode blocks become<div class="mvu-mermaid">nodes holding the diagram source. The first time a page has one,mermaid.min.js+mermaid-bootstrap.js(both declared inweb_accessible_resources) are injected and run in the page main world withsecurityLevel: "strict". The bootstrap picks its theme from themvu-force-*classes and re-renders on amvu-mermaid-renderDOM event thatcontent.jsdispatches on raw/render toggle and theme flip. No extra permissions — onlystorage.
md-viewer-ultra/
├── manifest.json # MV3 declaration (action + content_script + bg)
├── background.js # Service worker — uninstall URL + opens changelog on update
├── content.js # Detection, render, raw/render toggle, theme apply, hljs
├── styles.css # Page chrome, footer, raw view, force-theme vars
├── hljs-theme.css # Maps hljs token classes to GitHub prettylights vars
├── github-markdown.css # Vendored github-markdown-css@5.6.1
├── marked.min.js # Vendored marked@14.1.3
├── purify.min.js # Vendored dompurify@3.1.7
├── highlight.min.js # Vendored highlight.js@11.10.0 (Common build)
├── mermaid.min.js # Vendored mermaid@11.6.0 (UMD) — lazy-injected on demand
├── mermaid-bootstrap.js # Main-world init/render of .mermaid nodes, theme-synced
├── popup.html # Toolbar popup
├── popup.js # Popup logic (theme + toggle + i18n + version)
├── popup.css # Popup styling
├── icons/ # 16/32/48/128 PNGs
├── _locales/ # i18n strings — en (default) + 16 translations
│ ├── en/messages.json
│ ├── pt_BR/messages.json
│ └── …
├── store-assets/ # Web Store promo tile, marquee, screenshots (not shipped)
├── sample.md # Test fixture covering all formatting (incl. Mermaid)
├── samples/ # Per-locale showcase fixtures (samples/<locale>.md)
├── PRIVACY.md # Privacy policy (Chrome Web Store URL)
├── store-listing.md # Web Store listing copy + submission flow
├── CLAUDE.md # Guidance for Claude Code
└── README.md
- Render/raw toggle state is per-tab and resets on reload (intentional — the page already starts in rendered view).
- Servers that send
text/htmlfor markdown won't be detected (very rare). - Syntax highlighting uses the Common build (~30 languages). For rarer languages (Erlang, Nim, Crystal, etc.) you'll get plain-text fallback.
- Mermaid renders in the page's main world, so a strict page Content-Security-Policy (
script-src 'self') can block it; the block then falls back to showing the diagram source. Local.mdfiles and rawtext/plainsources (e.g. raw.githubusercontent.com) are unaffected.
curl -sSL -o marked.min.js https://cdn.jsdelivr.net/npm/marked@14.1.3/marked.min.js
curl -sSL -o github-markdown.css https://cdn.jsdelivr.net/npm/github-markdown-css@5.6.1/github-markdown.css
curl -sSL -o purify.min.js https://cdn.jsdelivr.net/npm/dompurify@3.1.7/dist/purify.min.js
curl -sSL -o highlight.min.js https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.10.0/build/highlight.min.js
curl -sSL -o mermaid.min.js https://cdn.jsdelivr.net/npm/mermaid@11.6.0/dist/mermaid.min.js