You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
i18n for the preview webview UI — The webview's user-facing widgets (context menu, footer, topbar, backlinks, in-preview markdown editor, image helper, floating actions) are now translated. Hosts pass the UI language via the new WebviewConfig.locale field (e.g. from vscode.env.language); shipped locales: en, zh-cn, zh-tw, ja, ko, es, fr, nl, pt-br, tr — matching vscode-markdown-preview-enhanced's setting localizations. Unknown locales and unknown keys fall back to English (then to the key), so a missing translation can never break the UI. Locale packs live in src/webview/locales/ and their key parity is enforced by tests. Universal terms (HTML, ePub, Pandoc, theme file names, VS Code) stay untranslated.
Preview context menu: Copy item, Escape to close, and an opt-out — The custom right-click menu now shows a Copy item at the top when text is selected (the selection is captured as it changes, so clicking the item copies what was selected when the menu opened), and closes on Escape like native menus. Hosts can also turn the custom menu off entirely via the new enablePreviewContextMenu config (default true): when disabled, right-clicks fall through to the browser's native context menu with its usual copy/paste entries. Fixes vscode-mpe#2356 and vscode-mpe#2363. Reported by @andy-clapham and @miblooming.
Browser/HTML exports follow the reader's system color scheme — generateHTMLTemplateForExport used to hardcode github-light.css + prism_theme/github.css for every export path whenever printBackground was off. That forced-light behavior is now scoped to paper output (Chrome PDF / Prince), while screen output (Open in Browser, HTML export) embeds the configured theme together with its dark counterpart under @media (prefers-color-scheme: dark) — plus <meta name="color-scheme" content="light dark"> so the page background and GitHub-style <picture> images resolve correctly on dark-OS readers. Pairing covers every shipped light/dark family (atom, github, one, solarized) for both page and code-block themes; unpaired themes (e.g. night.css, monokai.css) are embedded as-is. Ported from @TaurusWood's extension-side PR vscode-mpe#2327 (vscode-mpe#2271).
Explicit opt-in channel for user preview scripts (notebook.previewScriptsEnabled) — Host applications can now set Notebook.previewScriptsEnabled = true to re-enable custom preview JavaScript that 0.9.31 disabled (#446). When opted in, <script src="./copy-buttons.js"> tags in .crossnote/head.html are kept and rewritten to file URLs, and @import "*.js" file imports are emitted again. The channel is file-based and workspace-scoped: inline scripts, URL-scheme sources (https://, file://, data:, …), and paths resolving outside the notebook directory (symlinks included) are always removed. The flag is deliberately not part of NotebookConfig — .crossnote/config.js is untrusted repository content and can never turn it on; only the host (e.g. an application-scope VS Code setting) can. HTML/eBook exports continue to strip all scripts unconditionally.
Add a translation toggle to the preview context menu — The webview now reads an isShowingTranslation flag from WebviewConfig (passed by the host via initPreview's <meta data-config>, so it survives webview reloads) and renders a context-menu item that switches between "Translate" and "Show Original". Clicking it posts translateDocument or restoreOriginal back to the host, which drives the AI translation feature in the vscode-markdown-preview-enhanced extension (#459 by @loorr).
Security
Harden @import "*.js" script emission in the preview webview — generateJSAndCssFilesForPreview() used to emit <script src> tags for any collected path, including remote https?:// URLs written in note content (reachable when enableScriptExecution was on). Script emission now additionally requires notebook.previewScriptsEnabled, rejects URL-scheme and protocol-relative sources, and confines resolved paths to the notebook directory (via realpath, so symlinks cannot escape). Stylesheet imports are unaffected.
Updates
Update mermaid to 11.17.2, including the vendored offline preview bundle (now downloaded from the official jsDelivr dist via scripts/update-mermaid-bundle.mjs) and the CDN fallback.
Bug fixes
Restore the vendored dependencies/mermaid/mermaid.min.js that an unrelated PR had committed as a 0-byte file — #468 accidentally swept a truncated bundle (a failed wget -O empties its output file) into its diff, so every mermaid diagram rendered blank from the offline bundle on develop since then. The file is restored byte-identical to the official jsDelivr dist of 11.17.2, and a new test/mermaid-bundle.test.ts guards the size + IIFE signature so an empty or truncated bundle fails CI instead of shipping. scripts/update-mermaid-bundle.mjs itself was sound — its sanity checks reject empty downloads. Reported by @shd101wyy.
Fix "Open in Browser" doing nothing on WSL distros without xdg-utils — openFile() unconditionally ran xdg-open on Linux, but WSL has no Linux browser by default and plain Ubuntu images don't even ship xdg-utils, so the spawn failed with ENOENT and nothing opened (NixOS-WSL only worked because it patches xdg-open to dispatch to Windows). On WSL, openFile() now tries wslview (wslu) first, then xdg-open (WSLg setups with a real Linux browser), and finally falls back to wslpath -w + explorer.exe — opening the Windows default browser, which matches the Windows-consumable file:////wsl.localhost/… URLs the browser export already generates. This also fixes clicked links in the preview opening nothing on the same setups. All spawns remain shell-free (execFile), preserving the injection hardening.
Fix blockquote/list markers leaking into $$…$$ display math — The block-level math rule scanned the raw document source for the closing $$, so a display-math block inside a blockquote absorbed the > quote markers of its continuation lines and rendered them as greater-than signs inside the formula (> $$ / > x / > $$ became a formula containing > x >). The rule now reconstructs the block's logical text line by line via markdown-it's marker-stripped bMarks/tShift/eMarks, which also stops list-item indentation from leaking into math. Fixes vscode-mpe#2361. Reported by @calculus1437.
Fix # in project path breaking image loading in preview — file:// URLs were built by string concatenation without percent-encoding, so a # in a directory name (e.g. 2026-06-10#1-AI-platform-arch/) was parsed as a fragment separator by the browser and every image (and @imported script/stylesheet) under it 404'd. addFileProtocol() and toFileURL() now build URLs via Node's pathToFileURL() (also normalizing Windows backslashes), and removeFileProtocol() percent-decodes back to a filesystem path so ebook/@import file reads keep working. Fixes #453. Reported by @Hubbitus.
Fix toc: ordered: true front matter option having no effect — generateSidebarToCHTML() accepted the ordered option but never read it, so both the [TOC] block and the sidebar TOC always rendered as the default collapsible <details> tree. When toc.ordered: true is set in the front matter, the TOC now renders as a nested ordered list (<ol>/<li>) with browser-provided numbering, in both the document body [TOC] and the sidebar TOC. Fixes #451. Reported by @KarlYao-SystemDesign.
Fix embedded d2 diagrams failing to render when they reference relative images — ```d2 fences using local assets (e.g. icon: ./icons/x.svg) rendered blank because renderD2 wrote its temp input to os.tmpdir(), and d2 resolves relative image paths against the input file's own directory. The temp input is now written beside the source document (falling back to the temp dir when it is missing or not writable), and fileDirectoryPath is included in the render checksum to avoid cross-folder cache collisions.
Fix d2 image/render errors and a missing d2 binary both rendering blank — d2's own "failed to bundle local images … no such file or directory" error was misclassified as a missing binary (the not found heuristic was too broad), so a missing icon silently hid the whole diagram; the heuristic is now limited to a genuine ENOENT/"not recognized as an internal or external command". Separately, when the d2 binary is not installed the ```d2 fence now falls back to a plain-text code block instead of being removed entirely.
Fix slow rendering of documents with long lines.
The ^block-id transform's cost grew quadratically with line length, so large documents written one sentence per line could take seconds per preview refresh.
It now runs in linear time, including on lines ending in long whitespace runs.
(#455 by @zeyutang)