A small, self-contained markdown previewer written in Go. Point it at a .md
file or a folder. It opens a rendered preview in your browser, with mermaid
and d2 diagrams and live reload on save.
binos example.mdImportant
This project was almost entirely AI generated.
- GitHub-flavored markdown. Tables, task lists, strikethrough, and autolinks.
- Syntax highlighting for fenced code blocks, via chroma.
- Mermaid diagrams.
```mermaidblocks render in the browser. - D2 diagrams.
```d2blocks render to SVG on the server. No externald2binary is needed. - Live reload. Save the file and the browser updates in place, keeping your scroll position.
- Directory tree. A resizable sidebar lists every markdown file under the served folder. Folders collapse, and the current file is highlighted. Each entry shows the document's title with the filename in a small badge. On narrow windows the sidebar becomes a drawer behind a menu button.
- Table of contents. A column on the right outlines the current document's headings and tracks your position as you scroll. It only appears when the document has more than one heading.
- Fuzzy file switcher. Press
⌘KorCtrl-Kto jump to any markdown file under the served folder. The search matches both the title and the filename. This is handy for note collections where filenames are IDs. - Relative images.
and similar links are served from the document's folder. Remote and data-URI images work too. - Fully offline. Mermaid, the Web Awesome UI components, and all styles and icons are embedded in the binary. Nothing is fetched from the network.
A document's title is its first markdown heading. If there is no heading, a
title: key in YAML front matter is used instead. The title appears in the
sidebar, the switcher, and the browser tab.
With Homebrew:
brew tap c9rgreen/tap
brew install binosOr build from source. This needs Go 1.26 or newer, Node 20 or newer, and pnpm (see Frontend dependencies).
# Build a local binary
make build
# Put it on your PATH, e.g.
mv binos /opt/homebrew/bin/ # or ~/.local/bin, etc.Or install straight to $(go env GOPATH)/bin. Make sure that folder is on
your PATH.
make installA plain go build or go install fails until the frontend has been built
once. The make targets above handle that step for you.
binos [flags] [file.md | directory]| Flag | Default | Description |
|---|---|---|
--port |
0 |
Port to listen on (0 picks a free one) |
--no-open |
false |
Don't open the browser automatically |
With a file, binos serves that file's folder and opens the file. With a folder,
it serves the folder and opens README.md, or the first markdown file if there
is no README. With no argument, it serves the current folder. Files ending in
.md or .markdown count as markdown. Hidden folders, node_modules, and
vendor are skipped.
The server binds to 127.0.0.1 and prints the URL. Press Ctrl+C to stop.
Pages use the Modus palette and follow the system light/dark setting. Diagrams and syntax highlighting follow it too. Printing always uses plain black on white, and hides the sidebar and table of contents.
Note
The previewed files are treated as trusted content. Raw HTML in the markdown
is passed through, and d2 diagrams are embedded as inline SVG. The whole
served folder is also available over 127.0.0.1 so relative images load.
Directory listings are disabled, and paths cannot escape that folder. This is
meant for files you wrote. Avoid previewing untrusted markdown.
A mermaid block:
```mermaid
flowchart LR
A --> B --> C
```A d2 block:
```d2
client -> server: request
server -> client: response
```A syntax error in one diagram shows an inline error box. The rest of the page still renders.
main.go CLI: flags, free port, browser open
internal/render/render.go goldmark pipeline + mermaid/d2 interception
internal/render/d2.go d2 source -> inline SVG
internal/render/title.go document title from first heading or front matter
internal/server/server.go HTTP server, SSE live reload, fsnotify watcher
internal/assets/ page template; embeds the frontend bundle and icons
frontend/ page JS and CSS, bundled with Web Awesome and mermaid
testdata/example.md sample document
The UI uses mermaid for diagrams, Web Awesome web components, and icons from Font Awesome. All three are pnpm dependencies. Nothing is vendored.
This project requires pnpm. package.json pins the version through
packageManager, and a preinstall guard rejects npm and yarn. With corepack:
corepack enable pnpm.
The page's own JavaScript and CSS live in frontend/. pnpm run build uses
Vite+ to bundle frontend/main.js into
internal/assets/dist. Vite+ is the OXC toolchain in one
vp command: its build is Vite 8 on Rolldown, minified
with oxc-minify, and it also ships Oxlint and Oxfmt. vite.config.js holds
the build, lint, and format settings. The bundle contains:
- The Web Awesome components the page uses, one import per
<wa-*>tag. A Go test checks that the import list matches the tags the page emits. - Web Awesome's stylesheet and
frontend/styles.css. - Mermaid. Its per-diagram modules become chunks that load on demand.
The Font Awesome icons listed in the Makefile are copied into
internal/assets/icons, because <wa-icon> fetches icons by URL. Both output
folders are gitignored and embedded in the binary. They must live under
internal/assets because go:embed only sees files inside the package
directory.
make frontend runs both steps and installs node_modules on first use.
make build, make install, and make test all run it first. A bare
go build fails with a no matching files found embed error until
make frontend has run once.
Common changes:
- To use a new Web Awesome component, add its import to
frontend/main.js. - To add an icon, add it to the
ICONSlist in theMakefile. - To upgrade a package, bump it in
package.jsonand rebuild.
The frontend is checked with three linters. make lint runs them all, and
make check runs it after the Go vet and tests.
- Oxlint lints the JavaScript
with its
correctnessrules as errors andsuspiciousrules as warnings. - Oxfmt checks that the
JavaScript, CSS, JSON, and markdown are formatted.
make fmtformats them (and runsgofmt), andpnpm run fixalso applies Oxlint's safe fixes. - stylelint lints the hand-written stylesheet,
frontend/styles.css, withstylelint-config-standardplusselector-max-id: 0, so styles use classes and design tokens, never IDs.
make lint
make fmtMIT — see LICENSE.
