A Markdown editor and live preview for Windows in one 5 MB executable.
Download the newest JustViewMD.exe, double-click it, drop a .md file on the window, and
you are editing and previewing at once. GitHub Flavored Markdown, KaTeX math,
Mermaid diagrams and Prism syntax highlighting are all there, and the front end
that renders them - along with every library it uses - is compiled into the
executable, so there is nothing to unpack beside it and nothing to fetch from a
CDN.
It is native Win32 C++17 around the Microsoft Edge WebView2 runtime that already ships with Windows, and it is deliberately small: one file, no installer, no service, no account, no telemetry. The only two requests it ever makes are the pictures a document points at and, unless it is switched off, the update check - both made by the host, never by the page (see The offline guarantee).
+--------------------------------------------------------------------------+
| File Edit Options one.md x | notes.md x | Untitled.md x | + |
+-------------------------+------------------------------------------------+
| Markdown source | Rendered document |
| (textarea + gutter) ~ | GFM, math, diagrams, code |
+-------------------------+------------------------------------------------+
| one.md Ln 12, Col 4 120 words 1.2 kB split view |
+--------------------------------------------------------------------------+
| At a glance | |
|---|---|
| What it is | A Markdown editor with a live preview, for Windows |
| Download | One file, JustViewMD.exe, about 5 MB (x64) - no installer, nothing to unpack |
| Runs on | Windows 10 1809 or newer, Windows 11; ARM64 builds from source |
| Needs | The Microsoft Edge WebView2 Runtime, which ships with Windows |
| Markdown | GitHub Flavored Markdown: tables, task lists, footnotes, strikethrough, autolinks |
| Rendering | KaTeX math, Mermaid diagrams, Prism syntax highlighting (30+ languages) |
| Documents | .md, .markdown, .mdx, .txt, .text; drag and drop; Explorer context menu |
| Windows manners | Tabs, Ctrl+S, find and replace, line-number gutter, a formatting bar, light and dark themes, settings under %LOCALAPPDATA%, no admin rights |
| Privacy | No telemetry, no accounts, no analytics, nothing phoning home |
| Network | The pictures a document points at, and the update check (switchable off) - nothing else |
| Licence | Apache 2.0 |
Take JustViewMD.exe from the
latest release. There is
no installer and nothing to unpack: the executable contains the whole
application, including its front end. Put it wherever you like and run it.
- One file, about 5 MB, x64. The Microsoft Edge WebView2 Runtime has to be present (it ships with Windows 11 and current Windows 10 installs; see Requirements).
- Nothing is installed into
Program Filesand nothing is written to the registry by the release itself: settings, the session, unsaved buffers and the picture cache live under%LOCALAPPDATA%\JustViewMD. - Options → Check for update... shows the version in use and asks GitHub whether a newer release exists. That check also runs quietly in the background when the window opens.
| Split view | Options menu |
|---|---|
![]() |
![]() |
- One file, and it stays one file. No installer, no runtime to bundle, no
writes into
Program Files, no administrator rights. The executable carries its own front end and unpacks it under%LOCALAPPDATA%on first run; delete the file and the application is gone. - Offline by construction, not by good intentions. The page is served from
an internal
https://app.localorigin behind adefault-src 'none'Content Security Policy, so a document cannot pull anything from the network even if it tries. Pictures you link are fetched once by the host, cached locally, and served fromhttps://cache.local- and a cached copy that disappears while a document is open is fetched again, so the preview heals itself. - The preview follows your source line, not the scroll bar. Every rendered block remembers the line it came from, so editing near the bottom of a long document keeps both panes on the same paragraph instead of merely the same percentage of the way down.
- Editing is the point, not an afterthought. Tabs with dirty markers, find
and replace with case, whole-word and regular-expression switches, a gutter
that follows wrapped lines, zoom with
Ctrl+wheel, a file dropped anywhere in the window, and a*in the title bar whenever something is unsaved. - It behaves like a Windows program. Explorer context menu entries for
.mdfiles, a document icon for those files, associations you can switch on and off, per-monitor DPI awareness, a title bar that follows the theme, and settings in a plain text file you can read and edit by hand. - Your files are treated carefully. Line endings are preserved (a file with
CRLFstaysCRLF), every buffer is confirmed from the text as it is now before anything discards it, the settings file is written atomically, and a document that fails to load says why instead of showing nothing.
Tabs
- Several documents in one window, with
+for a new empty tab and anxon each tab (or a middle click) to close it. Drag a tab sideways to move it along the strip, and that order is the one the session restores. - New empty tabs are numbered as they appear -
Untitled.md,Untitled-2.md,Untitled-3.md- and a number is reused once its tab is gone, so no two open tabs ever share a name. Saving keeps that name as the suggested file name. Ctrl+TabandCtrl+Shift+Tabcycle through the open tabs.- Closing the last tab opens a fresh empty one instead of quitting the application.
- Opening a file that is already open activates its tab instead of loading it twice; a single untouched empty tab is reused rather than duplicated.
- Tab context menu: close, close others, copy full path.
Menus
- File - new tab, open, save, save as, reload from disk, print, save as PDF, close tab, close all tabs.
- Edit - undo, redo, cut, copy, paste, select all, find, find and replace.
- Options - view mode (split / editor only / preview only), zoom, line
wrapping, start with a blank document, set as default for
.mdfiles, dark mode, the size of the picture cache with a row that opens its folder, Windows default apps, and the update check with the version in use beside it. Every switch is remembered between runs. The menu stays open while its switches are flipped, so several can be changed in one go; clicking somewhere else or pressingEscapecloses it. The menu never removes a cached picture; it only tells you what is there. Alt+F,Alt+E,Alt+Oopen a menu with the keyboard. Once a menu is open, sliding along the menu bar switches menus; clicking somewhere else closes it.
Editing
- Split view with a resizable divider (drag it, or double-click to reset).
- Line-number gutter that follows wrapped lines.
- Status bar with cursor position, word, character and byte counts.
- A formatting bar at the bottom of the editor. One press inserts the Markdown
around the selection - bold, italic, strikethrough, inline code, superscript,
subscript, H1-H3, block quote, bulleted, numbered and task lists, fenced code,
table, horizontal rule, footnote, link, image, inline and display math, a
Mermaid diagram - and the result appears in the preview as it is written.
Pressing the same button again removes what it added, one
Ctrl+Zundoes the whole change, and every button names itself on hover. - Realtime preview, debounced while typing.
- Find and replace with match highlighting,
Enter/F3for next,Shift+F3for previous,Ctrl+Enterto replace, case, whole-word and regular-expression switches, and replace-all. Find works in the preview as well: it highlights the matches there, scrolls to the current one, and never changes the view you are in. Ctrl+Pprints the rendered document, or saves it as a PDF from the same dialog; the print stylesheet drops the editor, the tabs and the status bars, prints light colours even from the dark theme, and keeps headings with the text that follows them.- Zoom with
Ctrl+/-/0orCtrl+wheel. - Scroll to top and bottom from the pair of buttons in the bottom right corner. They appear while you scroll, fade a second after the last move, and dim the end you are already at.
- Light and dark themes, dark by default.
- Custom context menu (undo, redo, cut, copy, paste, select all, find, wrap, file and tab actions) instead of the browser's.
Rendering
- GitHub Flavored Markdown - tables, task lists, strikethrough, autolinks, footnotes.
- Math with KaTeX:
$inline$,$$display$$,\(inline\),\[display\]. - Diagrams with Mermaid, fenced as
```mermaid. - Syntax highlighting with Prism for 30+ languages.
- Pictures that sit next to the document: a relative address
(
assets/demo.png,../shared/x.png), an absolute Windows path or afile:///address is read from the disk by the host and shown where it lies. Nothing is copied into the picture cache, and a picture that is missing keeps the broken-image placeholder and says why on hover. - Pictures that a document points at on the web: the host downloads each one
once into
%LOCALAPPDATA%\JustViewMD\cacheand shows it from there, so a document with online images also renders offline once it has been opened. A picture that cannot be fetched keeps the broken-image placeholder and says why on hover. Nothing ever empties that cache but you: the Options menu shows how much it holds and opens the folder. - Scroll synchronisation driven by the document itself: each rendered block knows the source line it came from, so both panes follow the same line rather than merely the same percentage.
Files
Ctrl+Oopen,Ctrl+Ssave,Ctrl+Shift+Ssave as,Ctrl+Tnew tab.- Drag and drop a
.md,.markdown,.mdx,.txtor.textfile anywhere on the window, preview included; a drop never reaches the embedded browser, so the page cannot navigate away from the document being edited. Text selections can be dropped too and are inserted at the cursor. - The title bar shows the active file name, with
*while there are unsaved changes, and the version in use (README.md - JustViewMD v1.6); the active tab carries a dot. - Unsaved changes are confirmed before anything that would discard them: closing a tab, closing all tabs, or reloading from disk. Closing the window never asks; the buffers come back as they were on the next start.
- Open documents are remembered. The documents open at exit come back on the next start, in the same order, with the same tab active; there is no switch for this. A file named on the command line joins them as an extra tab (and becomes the active tab), so opening a second document does not throw the first one away; naming a file that is already open activates its tab instead of opening it twice. Files that have since been deleted are skipped.
- Start with blank (Options menu, off by default): when there is nothing to
reopen, the window opens on an empty
Untitled.mdinstead of the welcome document. The tab is numbered like any other new tab, so a second blank tab becomesUntitled-2.md. Documents from the previous run, unsaved buffers and a file named on the command line still win.
Updates
- The version in use is printed beside Options → Check for update.... When a
newer release exists, the row reads
1.5 → 1.6and a Get JustViewMD 1.6... row appears below it, which opens the release page in your browser. - The check runs in the background each time the window opens and never interrupts: it only ever shows up in the Options menu. Asking from the menu reports itself with a toast, because then you asked.
- Nothing is downloaded and nothing replaces the running executable; the check
reads the public releases API of this repository and nothing else.
checkForUpdate=0insettings.inistops the automatic check, and the row in the menu keeps working either way.
- Explorer context menu.
--registeradds "Open with JustViewMD" to the context menu of.md,.markdownand.mdxfiles belowHKEY_CURRENT_USER. The first run does it automatically;--no-registerand--unregisteropt out. - Default application. Options → Set as Default for .md Files registers
a
JustViewMD.DocumentProgID for.md,.markdownand.mdx, with the document icon for those files and this executable for the open command. Unchecking it removes only the entries this application wrote, so a choice made elsewhere survives. - Windows protects the user's own choice: when a
UserChoicekey hands an extension to another application, the menu says so instead of pretending the checkbox did more than it did, and offers Windows default apps for the rest of the job. That is also why the checkbox describes "registered", not "is currently the handler Windows uses". - Both icons are in the executable:
IDI_APPICON(window, taskbar, Alt+Tab) andIDI_DOCICON(associated documents, and theDefaultIconof the ProgID).
- Windows 10 1809 or newer (Windows 11 included), x64 or ARM64.
- The Microsoft Edge WebView2 Runtime. It ships with Windows 11 and with current Windows 10 installs; if it is missing, install Microsoft's free "Evergreen WebView2 Runtime".
- To build: CMake 3.20+, and either Visual Studio 2019/2022 with the C++
workload or MinGW-w64. Ninja is used when it is on
PATH. - Internet access the first time the project is configured, to fetch the
WebView2 SDK from NuGet (about 10 MB). Configure with
-DJVMD_WEBVIEW2_ROOT=<unpacked package>/build/nativeto skip that.
build.batThe script finds a compiler (MSVC first, then MinGW-w64), bumps the version,
configures, builds and copies the result to dist\:
dist\JustViewMD_v<version>.exe
Every build carries a version in its name, and bumps the last number unless you say otherwise:
| Command | Version the build carries |
|---|---|
build.bat |
minor +1, e.g. 1.5 becomes 1.6 |
build.bat --keep-version |
the number already in CMakeLists.txt, unchanged |
build.bat --version 2.0 |
exactly that number; nothing is bumped |
The number lives in one line of CMakeLists.txt, and the script edits that line
rather than a copy of it (see Releasing).
One file. On the first start of a version the executable unpacks its front end
into %LOCALAPPDATA%\JustViewMD\assets\<version>\ (about 4 MB, well under a
tenth of a second); MinGW builds also unpack the WebView2 loader into
%LOCALAPPDATA%\JustViewMD\bin\<version>\. Later starts reuse both, and a
rebuild replaces the copy, so development never serves yesterday's app.js.
To build by hand:
:: MSVC
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build:: MinGW-w64
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++
cmake --build buildMSVC links WebView2LoaderStatic.lib and the C runtime statically, so the result
is one self-contained executable. MinGW-w64 cannot link that library (it is
Visual C++ code and wants that CRT), so the redistributable
WebView2Loader.dll is compiled into the executable as a resource, unpacked
under %LOCALAPPDATA% on first use, and loaded by absolute path. Either way the
only imports are Windows system DLLs, and nothing has to travel next to the
executable.
Editing assets/* needs cmake --build build (the front end is part of the
executable, so a rebuild and relink is the point). For a front-end loop without
rebuilding, point the build at the source folder:
build\bin\JustViewMD_v<version>.exe --assets assetsThe MinGW-w64 configuration is the one this release was built and exercised with, warning-free under
-Wall -Wextra. The MSVC configuration keeps the same sources and flags (/W4) but has not been rebuilt on a machine with Visual Studio installed, so treat that path as untested until it has been.
A release is a tag X.Y with JustViewMD.exe as its only asset. The version
lives in exactly one place: the project(JustViewMD VERSION X.Y ...) line in
CMakeLists.txt.
tools/version.ps1 edits that one line - build.bat calls it before it
configures, and it adds one to the last number unless --keep-version or
--version says otherwise. CMake writes the number out to a generated header
that the resource script (the version in the file's properties), the host (the
window title, the Options menu, the update check and --help) and the build's
file name all use, so the tag, the file properties, the title bar and what the
application reports cannot drift apart. Bumping a release is build.bat,
a tag, and publishing the file it left in dist\ under the name
JustViewMD.exe: the build keeps its version in JustViewMD_vX.Y.exe, and the
release drops the version so the download can replace an older copy in place and
keep its "Open with" entry and default-app choice.
If you are changing this repository rather than just using it, read AGENTS.md first.
| Option | Effect |
|---|---|
<file> |
Open that document at startup. |
--register |
Add the Explorer context menu entries and exit. |
--unregister |
Remove the context menu entries and the file associations, then exit. |
--no-register |
Do not add the context menu entries on first run. |
--assets <dir> |
Serve the front end from that folder instead of the unpacked embedded copy (development). |
--devtools |
Open the WebView2 developer tools window at startup. |
--log [file] |
Write a diagnostic log. Without a file name it goes to %LOCALAPPDATA%\JustViewMD\justviewmd.log (UTF-16). |
--help, -h, /? |
Show usage. |
Exit codes: 0 success, 2 COM initialisation failed, 3 the front end could
not be unpacked (or the --assets folder has no index.html), 4 window class
registration failed, 5 the window could not be created, 6 WebView2 could not
be started.
| Keys | Action |
|---|---|
Ctrl+T / Ctrl+N |
New tab |
Ctrl+O |
Open a file in a new tab |
Ctrl+S / Ctrl+Shift+S |
Save / save as |
Ctrl+W |
Close the active tab |
Ctrl+Tab / Ctrl+Shift+Tab |
Next / previous tab |
Ctrl+F / Ctrl+H |
Find / find and replace |
Enter / F3 / Shift+F3 |
Next match / next / previous |
Ctrl+Enter |
Replace the current match |
Escape |
Close the find bar, menu or context menu |
Ctrl+1 / Ctrl+2 / Ctrl+3 |
Split / editor only / preview only |
Ctrl+Z / Ctrl+Y |
Undo / redo |
Ctrl+X / Ctrl+C / Ctrl+V / Ctrl+A |
Cut / copy / paste / select all |
Ctrl+B / Ctrl+I / Ctrl+K |
Bold / italic / link the selection |
Ctrl + / Ctrl - / Ctrl+0 / Ctrl+wheel |
Zoom in / out / reset |
Tab / Shift+Tab |
Indent / outdent the selected lines |
Alt+F / Alt+E / Alt+O |
Open the File / Edit / Options menu |
The options and the session live in one plain text file:
%LOCALAPPDATA%\JustViewMD\settings.ini
It is written whenever an option changes and when the window closes, always
through a scratch file and a rename, so a crash can never leave half a file
behind. The format is one key=value per line; lines starting with ; or #
are comments. Keys you do not list keep their default, and unknown keys are
ignored. The application rewrites the whole file, so edit it while the
application is closed.
The same folder also holds the WebView2 profile (WebView2\), the unpacked
front end (assets\<version>\), the pictures downloaded from the web
(cache\) and, on MinGW builds, the unpacked loader (bin\<version>\).
Deleting one of the unpacked copies is harmless: the next start writes it again
from the executable. The picture cache is never pruned by the application
itself; Options → Cached images / Open cache folder shows what it holds and
takes you to it, and clearing it only costs the pictures being fetched again.
A tab with unsaved changes is written to unsaved\ as well: one file per tab,
named with an id and the tab's own name, and an index.txt that says which
document each one belongs to. The copy follows typing a couple of seconds
behind, and closing the window keeps it: the next start opens it as a tab
again, still marked unsaved - any file type. Saving the tab, reloading it from
disk, or closing the tab itself with No removes the copy, through the
Recycle Bin like every other file the application deletes. Only the lines in
index.txt are read, so a stray file in that folder is ignored; copy a draft
elsewhere if you want to keep it.
| Key | Default | Meaning |
|---|---|---|
viewMode |
split |
split, editor or preview. |
darkMode |
1 |
Dark theme. |
wrapLines |
1 |
Soft wrap in the source pane. |
startBlank |
0 |
Open an empty tab instead of the welcome document. |
checkForUpdate |
1 |
Ask GitHub for a newer release in the background on start-up. |
sessionOpen |
- | One line per file to reopen, in tab order. |
sessionActive |
- | The file whose tab was active. |
Every start reopens the documents of the previous run; a keepOpenedFiles
line written by an earlier release is ignored and disappears the next time the
file is written.
Earlier releases kept these values in HKCU\Software\JustViewMD. The first
start of this version reads them, writes the file, and then removes the five
values and the Session subkey that it owned. Values left below that key by an
older, unrelated JustViewMD are not touched.
The page is kept off the network; the host does the one job that needs it:
assets/vendor/contains marked, KaTeX (script, stylesheet, woff2 fonts), Prism and Mermaid, all vendored at pinned versions.assets/index.htmlcarries a Content-Security-Policy ofdefault-src 'none'that allows only the page's own origin (https://app.local), the local picture cache (https://cache.local), the origin that serves pictures from the disk (https://local.local) andfile:. A picture on the web is therefore refused by the page itself, so nothing is ever loaded behind the reader's back.- The host maps the unpacked front end onto the virtual host
https://app.local/and navigates there, which gives the page a normal secure origin and keeps font and module loading reliable. If a runtime is too old for virtual host mapping, the host falls back to afile://URL. - The pictures a document points at are the only thing that ever leaves the
machine: the host downloads each one once (WinHTTP, following redirects)
into
%LOCALAPPDATA%\JustViewMD\cache, serves it to the page ashttps://cache.local/<name>, and reuses that copy on every later render - with no network at all once the cache holds it. That folder is never pruned by the application: Options reports its size and opens it in Explorer, and a picture removed from it is simply fetched again the next time a document needs it. A picture the document points at on its own disk is not fetched at all: the host reads that file and answers the page'shttps://local.localrequest with it, so it never enters the cache and never touches the network. - The update check is the second and last request: one
GETofapi.github.com/repos/finnuen/JustViewMD/releases/latest, the public document GitHub serves to anyone. It carries no account, no identifier and not even the version in use, and it is made by the host when the window opens (and when Options → Check for update... asks) - never by the page.checkForUpdate=0insettings.initurns the automatic one off; the tag and the page address of the newest release are all the application takes from the answer.
The embedded browser itself is started with --disable-background-networking,
--disable-component-update, --disable-sync and friends, so the engine stays
quiet as well.
CMakeLists.txt build configuration, fetches the WebView2 SDK
(the project version here is the one in the file
properties, the Options menu, the update check and
the executable's name)
build.bat one-click build (MSVC or MinGW), bumps the version
tools/version.ps1 reads, bumps or sets that one version
AGENTS.md the rules for changing this repository
.gitignore build and release output is never committed
docs/ screenshots used by this file
src/main.cpp the whole host: window, tabs, files, IPC, settings
src/resource.h resource identifiers
src/app.rc icons and version information
src/app.ico application icon, 16-256 px
src/file-icon.ico document icon for .md files, 16-256 px
src/app-icon.png source image for the application icon
src/file-icon.png source image for the .md document icon
assets/index.html page frame and content security policy
assets/style.css themes, panes, tabs, menus, find bar, gutter,
formatting bar
assets/app.js tabs, editor, formatting bar, render pipeline,
scroll sync, IPC client
assets/welcome.md the document shown when no file is given
(unless "Start with blank" is on)
assets/vendor/ marked, KaTeX, Prism, Mermaid (+ licenses)
tools/fetch-vendor.ps1 refresh the vendored libraries
tools/make-icon.ps1 regenerate the .ico files from their PNG artwork
Host. src/main.cpp creates a window, unpacks the front end that is
compiled into the executable (once per version, into
%LOCALAPPDATA%\JustViewMD\assets\<version>; MinGW builds unpack the WebView2
loader the same way into bin\<version>), starts the WebView2 environment
and controller, maps that folder to https://app.local/ and the picture cache
to https://cache.local/, navigates, and then
acts as a small file server for that page: it owns the tab list (paths, names,
active index), reads and writes documents, runs the common file dialogs, keeps
the window title and the dirty markers, owns the drop target, and reads and
writes the options and the session in %LOCALAPPDATA%\JustViewMD\settings.ini.
The drop target is registered on
the main window and on the WebView2's own child windows: a drop is delivered
to the window under the cursor and OLE does not climb to its parent, so the main
window alone only ever sees drops on the frame. AllowExternalDrop is off, and
the host turns every dropped file into a tab instead of letting the embedded
browser navigate to it.
Page. assets/app.js owns a <textarea> per tab, renders Markdown into
#markdown, and talks to the host over window.chrome.webview.postMessage.
Rendering is debounced, Mermaid runs asynchronously after the HTML is in place,
and KaTeX render errors fall back to the raw TeX so a typo cannot blank out the
preview. A picture with an http(s) address is never loaded by the page: the
render collects the addresses, asks the host for each one with fetchImage,
remembers the https://cache.local/<name> address it gets back, and swaps it in -
so the same picture is neither downloaded nor even requested twice. A cached
copy that has disappeared under it - the folder is the reader's to empty - is
noticed from the failed load and asked for again, so the preview recovers
without a restart. A picture given as a path is resolved against the folder of
the active document (.., file:///, drive letters, UNC paths and %20 all
handled) and requested from https://local.local, which the host answers from
the disk rather than from the cache.
Update check. The check behind Options → Check for update... - and the one
that runs when the window opens - is a single GET of
https://api.github.com/repos/finnuen/JustViewMD/releases/latest on a worker
thread, through the same WinHTTP path the pictures use. The release's tag_name
is compared with the version compiled into the executable; the page is told the
outcome (update) and shows it in the Options menu, and as a toast only when
the user asked for that check. Nothing is downloaded and nothing replaces the
running executable: Get JustViewMD x.y... opens the release page in the
reader's own browser.
Messages. Page to host:
| Type | Payload | Meaning |
|---|---|---|
ready |
- | The page is up and wants the tab list and the documents. |
dirty |
index, value, generation |
That tab changed since its last save. |
snapshot |
index, content, generation |
A copy of a buffer, sent while typing, when the window loses focus, and when a tab is switched. |
save, saveAs |
index, content |
Write that tab to its path, or through the file dialog. |
open |
- | The user asked for the file dialog. |
newTab |
- | Add an empty tab. |
tabSelect |
index |
The user activated a tab. |
tabClose |
index, content |
Close one tab, with its live text. |
tabReorder |
from, to, active |
A tab was dragged to a new place in the strip; the host keeps the same order, and the same session. |
closeAll, closeOthers |
keep |
Close every tab, or every tab but one. |
reload |
- | Re-read the active document from disk. |
content |
reason, index, content |
The buffer, answering a requestContent. |
zoom, zoomIn, zoomOut, zoomReset |
value |
Zoom level changes. |
fetchImage |
url |
The preview wants a picture from the web; the host downloads and caches it. |
setting |
name, value |
An Options menu switch. |
setDefaultApp |
- | Register or remove the file associations. |
openDefaultApps |
- | Open the Windows "default apps" page. |
cacheStats |
- | The Options menu was opened; report what the picture cache holds. |
openCacheFolder |
- | Open the picture cache folder in File Explorer. |
checkForUpdate |
- | The user asked for an update check; report it with a toast, unlike the one at start-up. |
openReleasePage |
- | Open the newest release's page in the default browser. |
log |
text |
A diagnostic line (page errors are reported this way). |
Host to page:
| Type | Payload | Meaning |
|---|---|---|
settings |
view, dark, startBlank, wrap, defaultApp |
The stored options. |
tabs |
generation, items (name, path), active |
The whole tab list; it is authoritative, so the page drops anything past its end. The generation counts the strip: a dirty or snapshot that repeats an older one is dropped, because the tab it named may not be at that index any more. |
document |
index, name, path, content, dirty, active |
One document's text. Restoring sends them all, the active one last. |
saved |
index, path, name |
A tab was written to disk. |
closeTab |
index, active |
Drop one tab. |
collapse |
keep |
Keep one tab, drop the rest; sent before the new tabs. |
requestContent |
reason, index |
Ask for a tab's text, with a timer that answers if the renderer never does. |
defaultAppState |
value, notice |
The association state, with an honest notice when Windows disagrees. |
insertText |
text |
Insert a dropped or pasted text at the cursor. |
zoom |
value |
The zoom level to apply. |
imageReady |
url, src |
That picture is in the cache; show it from src. |
imageFailed |
url, message |
That picture could not be fetched, or was not a picture; keep the placeholder. |
cacheSize |
files, bytes |
What the picture cache holds, for the Options menu. |
update |
state (idle/checking/uptodate/available/failed), current, latest, url, message, asked |
Where the update check stands. asked is the difference between the silent background check and one the user started. |
The host never guesses what is in a tab: a close, a close-all, a close-others, an open, a drop or a new document asks the page for the buffers it needs, so the confirmations always talk about the text as it is now.
# Refresh the front-end libraries at the versions pinned in the script.
powershell -ExecutionPolicy Bypass -File tools\fetch-vendor.ps1
# Regenerate the application icon from src\app-icon.png.
powershell -ExecutionPolicy Bypass -File tools\make-icon.ps1
# Regenerate the .md document icon from src\file-icon.png. That artwork carries
# a lot of empty canvas, so it is cropped to its content and scaled to fill the
# tile; without that the page sits small next to the other file icons.
powershell -ExecutionPolicy Bypass -File tools\make-icon.ps1 `
-SourcePath src\file-icon.png -OutputPath src\file-icon.ico `
-CropToContent -SkipRedraw -ContentFill 0.98fetch-vendor.ps1 also refreshes assets/vendor/licenses/. Committing the
result keeps a checkout buildable without any of those tools.
Apache License 2.0 - see LICENSE. Copyright 2026 finnuen.
The licence text is built into the program as well: Options -> About
JustViewMD shows it, so a copy of the licence travels with the single .exe
and nothing has to sit beside it.
JustViewMD itself is Windows-only C++17 and has no dependencies beyond the Windows SDK and the WebView2 loader. The front end bundles:
| Library | Version | License |
|---|---|---|
| marked | 18.1.1 | MIT |
| KaTeX | 0.19.0 | MIT |
| Prism | 1.30.0 | MIT |
| Mermaid | 10.9.8 | MIT |
Their license texts are in assets/vendor/licenses/.
Do I have to install anything? No. A release is a single .exe; put it
wherever you like and run it. The only thing that has to be present on the
machine is the WebView2 runtime, which is part of Windows 11 and of current
Windows 10 installs.
Does it need the internet? Not to open, edit or render a document: the whole
front end, libraries included, is inside the executable. Two things can touch
the network, both made by the host and both easy to reason about - pictures a
document links to (fetched once, then served from the local cache) and the
update check (checkForUpdate=0 switches the automatic one off).
Is it portable? It never needs installing, so it runs from a USB stick or
any folder you like. Its settings, tab session and picture cache live per user
in %LOCALAPPDATA%\JustViewMD rather than beside the executable, so a stick
carries the program but not your settings.
Does it write to the registry? Only for the Explorer integration you ask
for: the context menu entry, the .md associations, the document icon. All of
it is below HKEY_CURRENT_USER, needs no administrator rights, and
--unregister takes it back out. It also reads the old
HKCU\Software\JustViewMD values once, to migrate options from releases that
predate settings.ini, and then removes what it wrote there.
How large a document can it open? Up to 64 MB. Above roughly 1.5 MB of source, syntax highlighting and diagrams step aside so typing stays instant; the status bar says "Large document" while that is the case.
Which Markdown dialect? GitHub Flavored Markdown - tables, task lists,
footnotes, strikethrough, autolinks - plus the extras a Markdown document
usually comes with: $...$ and $$...$$ math, Mermaid fences, and fenced code
highlighting for 30+ languages.
Does it do WYSIWYG? No, and it does not pretend to: the left pane is the file, the right pane is what that file renders to. That is what keeps the file in front of you the one that gets saved, byte for byte, line endings included.
How do I know a new version exists? Options → Check for update... shows
the version in use; when a newer release exists the row reads 1.5 → 1.6
and a Get JustViewMD 1.6... row opens the release page in your browser. The
same check runs quietly in the background each time the window opens.
- The title bar follows the theme switch on both Windows 10 and Windows 11. Windows 11 additionally takes the exact caption and border colours; Windows 10 has no per-application caption colour, so its title bar uses the standard DWM dark caption rather than the page's own background.
- Syntax highlighting covers the languages Prism bundles here (30+ of the most
common ones). Adding another is a one-line change in
tools/fetch-vendor.ps1followed by a re-run. - Mermaid renders when the vocabulary the bundled version understands is used; unsupported diagram types show their error message in place of the diagram, exactly as they do in a browser.
- The application is per-user: the Explorer integration and the associations
live below
HKEY_CURRENT_USERand need no administrator rights. Windows may still show aUserChoiceset for the same extension by another application - the Options menu reports that instead of hiding it. - Pictures from the web are fetched by the host with the system's proxy and TLS settings, one request per address, four at a time, 32 MB per picture. Only bytes that really are a picture are written to the cache; a login page or an error document is refused and the document keeps the broken-image placeholder with the reason on hover. Nothing is ever sent back.
- WebView2 requires the runtime to be installed. If the app cannot start the engine, it says so instead of failing silently.
- A document larger than 64 MB is refused rather than opened, and above about 1.5 MB of source the preview turns syntax highlighting and diagrams off so that typing stays instant (the status bar says so).
- The application is single-instance per window: launching a second copy opens its own window, and the same document can be open in both.

