WEML Preview
Live preview of WEML (White Estate Markup Language) and HTML documents in VS Code,
rendered with weml-stylesheet from this repository — the same
stylesheet and script that web applications render WEML with.
A .weml file normally links no stylesheet and no script: it is content, not a page. The
preview supplies both, so every w-para, w-list, w-note, w-page, … renders the way
docs/styling.md specifies, without touching the file.
Modelled on html-preview-vscode
(itself derived from VS Code's Markdown preview) and made to live next to it. Both use the
same keys, told apart by the language mode in the status bar: html-preview answers in
HTML mode, this preview in WEML mode. Switch an .html file to WEML (Change Language
Mode, or click the language in the status bar) and the same keys open the WEML preview.
To make that permanent for a folder:
// .vscode/settings.json
"files.associations": { "*.html": "weml" }
Features
- Open WEML Preview and Open WEML Preview to the Side — the keys, the editor
title button (book icon;
Alt+click opens in place) and the editor's right-click menu
(with Open in Browser) in WEML language mode; the tab and Explorer context menus and the command palette for any .weml, .html, .htm or
.xhtml file, whatever its mode.
- Live update while typing. When only the
<body> changed, the new body is swapped into
the loaded page — no reload, no flash, scroll position kept. A change to <head> or to
the <html>/<body> attributes reloads the page.
- The weml-stylesheet is always applied — the page build
weml.css and the auto
script (lists' start/marker, foreground/background colors, note tooltips). The
file's own <script>s and any <link> to a weml-stylesheet are dropped by default
(wemlPreview.removeDocumentScripts), so a stale copy referenced by the file cannot
fight the bundled one. Other <link>/<style> in <head> are kept and load after
weml.css.
- Theme: follows the VS Code color theme by default (
data-weml-theme is set from it and
updated when the theme changes); can be pinned to light/dark or left to
prefers-color-scheme. An explicit data-weml-theme in the file is overridden.
- Scroll sync both ways, by source line: block elements (
div, w-para, w-heading,
w-text-block, w-list, table, …) are tagged with data-weml-line when the page is
built. Double-click in the preview jumps to that line in the editor. The button in the
preview's title bar ($(sync) on, $(sync-ignored) off) and Toggle WEML Preview Scroll Sync
switch both directions at once (the two settings below, one each way). The line mapping is
shared with the web preview (weml-language-core's preview/).
- Relative URLs (images in
figure, custom stylesheets) resolve against the document's
folder.
- Links:
http(s) and mailto open in the browser; egw:// links show the link with a
Copy action; #id scrolls within the preview; a relative link opens that file.
- Open in Browser with WEML Styles: writes a styled copy to the temp folder (with
<base>
pointing back at the document's folder) and opens it in the system browser.
- Follow / lock: an unlocked preview switches to whichever editor in WEML mode becomes
active (
wemlPreview.followActiveEditor); Toggle Preview Locking pins it to its file.
- Previews are restored when VS Code restarts.
Keybindings
The same keys as html-preview, active in WEML language mode (and in the WEML preview):
| Command |
Windows / Linux |
macOS |
When |
| Open WEML Preview |
Ctrl+Shift+V |
Cmd+Shift+V |
editor in WEML mode |
| Open WEML Preview to the Side |
Ctrl+K V |
Cmd+K V |
editor in WEML mode |
| Open in Browser with WEML Styles |
Ctrl+K W |
Cmd+K W |
WEML mode / WEML preview |
| Show WEML Source |
Ctrl+Shift+V |
Cmd+Shift+V |
WEML preview focused |
The weml language mode itself comes from WEML Language Support (see below).
Settings
| Setting |
Default |
Description |
wemlPreview.theme |
vscode |
vscode, system (prefers-color-scheme), light, dark |
wemlPreview.followActiveEditor |
true |
An unlocked preview follows the active WEML/HTML editor |
wemlPreview.scrollPreviewWithEditor |
true |
Editor scroll → preview scroll |
wemlPreview.scrollEditorWithPreview |
true |
Preview scroll → editor scroll |
wemlPreview.doubleClickToSwitchToEditor |
true |
Double-click in the preview opens the source at that line |
wemlPreview.updateDelay |
300 |
ms after the last edit; at least 1000 for documents over 2 MB |
wemlPreview.removeDocumentScripts |
true |
Drop the document's <script>s and weml-stylesheet <link>s |
wemlPreview.customStyles |
[] |
Extra stylesheets after weml.css: absolute, workspace-relative, or http(s) |
wemlPreview.fontFamily |
"" |
Body font family (empty: VS Code webview default) |
wemlPreview.fontSize |
0 |
Body font size in px (0: default) |
wemlPreview.lineHeight |
0 |
Body line height (0: default) |
Custom styles can override any stylesheet token, e.g.
:root {
--weml-w-entity-h: 300;
}
Requires
WEML Language Support (EGWWritings.weml-support, the extension at the root
of this repository) owns the weml language: the id, the .weml extension, the grammar,
completion and validation. The preview is declared as depending on it
(extensionDependencies), so VS Code installs it alongside, and goes by its language mode
rather than defining the language a second time.
Developing
npm ci # at the repository root (npm workspaces), weml-stylesheet included
npm run build # dist/: extension, preview script, bundled ../weml-stylesheet
npm run watch # the same, rebuilding on change
npm run check-types # tsc over src/ (node) and preview-src/ (DOM)
npm test # build, then node --test (tests load src/htmlBuilder.ts directly; Node ≥ 22.18)
npx @vscode/vsce package --no-dependencies # without it vsce packs the workspace's node_modules
F5 (Run WEML Preview) builds and starts an Extension Development Host on ../../samples.
Layout
src/extension.ts activate(): commands
src/previewManager.ts open previews, follow-active-editor, editor scroll → preview, panel serializer
src/preview.ts one webview panel: render / body swap, messages, file watcher
src/htmlBuilder.ts pure: split the document, clean <head>, tag data-weml-line, build the page
src/browser.ts Open in Browser
src/links.ts links clicked in the preview
preview-src/index.ts the webview's script: theme, scroll sync (core's scroll map), body swap, clicks
src/scrollSyncToggle.ts the scroll sync command and title-bar button (both settings at once)
esbuild.mjs → dist/extension.js, dist/webview/{preview.js, weml-auto.js, weml.css}
The stylesheet
The source of truth is the sibling package ../weml-stylesheet, not
the published npm package, so the preview always ships the stylesheet of the same commit.
esbuild.mjs copies its build/css/weml.css and bundles its auto entry — an ES module
importing weml.js and notes.js — into one classic script, dist/webview/weml-auto.js,
since a module graph does not load from file:// (Open in Browser).
build/ there is gitignored, so esbuild.mjs runs npm run build in ../weml-stylesheet
whenever the build is missing or older than its src/, tools/, package.json or
tsconfig.build.json. That needs the stylesheet's own build tools, which npm ci at the
repository root installs with every other workspace package.
npm run watch also re-copies weml.css whenever the stylesheet is rebuilt.
License
MIT