A markdown editor that runs entirely in your browser and writes straight to your own files — a local folder, or a directory on any server you can SSH into.
edit.computer — no account, no install, no upload.
It reads and writes plain .md files in place, so an Obsidian vault, a Jekyll
_posts directory, or a folder of notes all work as-is. Nothing is copied to a
server owned by this project: with a local folder the files never leave your
machine, and over SSH they go straight between your browser and your own box.
- Live preview inline — syntax marks hide until the cursor touches them, so you edit the rendered text rather than a split pane
- Tables, images, task lists rendered in place;
and![[embed]]both resolve against the vault - Wikilinks —
[[Note]]and[[Note|alias]]navigate on click, and create the note if it doesn't exist yet - Code blocks don't trap the cursor — Enter on a blank line at the end of a fenced block steps out of it, writing the closing fence if it was left open
- File tree with folders — create, rename, and delete them (empty ones only, so a folder never takes your notes down with it)
- Drag to file — move a note onto a folder, or into the space below the tree to bring it back to the root. Works with a mouse and with a finger (press and hold, so a swipe still scrolls)
- Sort by name, by date edited, or manually — drag notes into whatever order you want and it sticks. Recent keeps its own order either way
- Pinned notes — pin a note from its row and it sits in its own section at the top of the sidebar; drag to reorder, and each vault remembers its own pins. A pinned note stays out of Recent — no point listing it twice
- Search (⌘K) across names and note text, with the matching line shown beside each hit. Names answer as you type; the text scan follows and remembers what it read, so searching again is instant
- Outline panel that tracks your scroll position and jumps on click
- Self-naming notes — a new note takes its name from the first
# headingonce you move off that line, and never overwrites a name you chose or a file that already exists - Autosave on idle plus ⌘S, and it picks up edits made elsewhere (Obsidian, Syncthing, git) when the window regains focus
- Dark mode, word count, adjustable tab size, and a Vesper theme
- Every prompt is drawn by the app — no browser dialogs telling you what the hostname says
- Keyboard throughout — one tab stop for the file tree, then arrows to move, left/right to open and shut folders, Enter to open a note. Dialogs trap focus and hand it back to whatever you pressed
- Works on phones and tablets, including drag-to-resize by touch
Two ways in, with different browser requirements:
| How | Works in | |
|---|---|---|
| Local folder | Pick a directory; the permission is remembered between visits | Chromium desktop only — Firefox and Safari don't implement showDirectoryPicker() |
| Over SSH | Host, username, password or key, and a path | Any modern browser, including iOS and Android |
No mobile browser can open a local folder, so SSH is the route on a phone or iPad. There is nothing to install on the server — just the sshd and SFTP subsystem that are already there.
The editor is a static React + CodeMirror app on Cloudflare Workers. It talks to a vault through one small interface, with two implementations:
- Local — the File System Access API. The directory handle is kept in IndexedDB so the vault reopens on your next visit with a single confirmation.
- SSH — a JavaScript SSH client and an SFTP v3 client running in the page.
Browsers can't open TCP sockets, so the Worker exposes
/relay, a WebSocket-to-TCP pipe to port 22. The SSH session is established end to end from the browser, which means the relay only ever carries ciphertext: it never sees your password, your key, or your files.
Host keys are pinned on first connect (trust on first use). If a host key later changes, the connection is refused with an explanation rather than reconnecting silently.
Credentials are stored in the browser only if you tick "Remember on this device"; otherwise they live in memory for the session.
- The browser SSH client implements RSA and ECDSA, not ed25519. Keys must be PEM or PKCS#8 — the app detects the common unusable formats and tells you the exact command to convert a copy.
/relaychecks theOriginheader, which stops other web pages from using it, but a non-browser client can forge that header and use it as a generic TCP proxy. If you deploy your own, consider putting a Turnstile token or a signed nonce in front of it.- Conflict handling is deliberately simple: if a file changed on disk while you have unsaved edits, your buffer wins. There is no merge UI.
- Every folder in the vault is listed, including ones holding no notes — an
attachmentsfolder shows up looking empty, because the tree only lists.mdfiles. That is what makes a folder you just made, and a folder you want to drag a note into, visible at all.
npm install
npm run dev
npm run build typechecks and bundles, npm run lint runs oxlint.
The tests run against real implementations rather than mocks:
| command | what it covers |
|---|---|
npm test |
the SFTP client and vault operations against a throwaway sshd |
npm run test:relay |
the Worker's WebSocket↔TCP relay, in both directions |
npm run test:search |
name matching, snippet extraction and the cached text scan |
npm run test:sort |
the name, date and manual ordering rules |
npm run test:layout |
touch resizing, the phone drawer, folders, drag-to-file, search, note naming, keyboard navigation and reduced motion, in real Chromium |
test:layout needs a build plus npm i --no-save playwright-core @playwright/browser-chromium. It stands up its own sshd, relay and static
server, so the browser connects over SSH exactly as it does in production. Set
SHOTS=<dir> to write phone and tablet screenshots as it goes.
npm run build
npx wrangler deploy
Change name in wrangler.jsonc first, and either drop the routes block to
use the generated workers.dev URL or point it at a domain in your own
Cloudflare account.
MIT © Vlad Arbatov.
Inspired by writer-computer.
