gmile

gmile

I am looking for a tool to preview contents of a Markdown file, as I edit it. After checking plenty of existing tools, all of them appear to have different shortcomings:

(I got to be honest I haven’t check each and every Markdown editor out there)

I’ve also tried using an online “Demo” page for Markedjs, only to have lost a couple of hours of writing when my Chrome crashed :sob: But that’s a lesson for me, I guess.

Instead of using a dedicated “Markdown editor” tool for this, I’d like to use:

  • plain text .md files on my hard drive,
  • my editor of choice, e.g. neovim or Zed, or even a TextEdit on macOS,
  • a “small” server that would notice changes to the file, then render an .html out of it and cue browser to reload the page (see next item),
  • a browser, that would render an .html version of the .md file.

I’ve largely achieved this by using a node HTTP server, with a combination of marked, highlight.js and socket.io libraries:

I know this is ugly
const http = require('http');
const fs = require('fs');
const socketIo = require('socket.io');

const { Marked } = require('marked');
const { markedHighlight } = require('marked-highlight');
const hljs = require('highlight.js');

const server = http.createServer((req, res) => {
  if (req.url === '/') {
    fs.readFile('./index.html', 'utf8', (err, content) => {
      if (err) {
        res.writeHead(500, { 'Content-Type': 'text/plain' });
        res.end('Internal server error');
        return;
      }
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.end(content);
    });
  } else if (req.url === '/socket.io/socket.io.js') {
    res.writeHead(200, { 'Content-Type': 'application/javascript' });
    res.end(socketIo.clientSource);
  } else if (req.url === '/highlight.js/styles/github.css') {
    res.writeHead(200, { 'Content-Type': 'text/css' });
    res.end("node_modules/highlightjs/styles/obsidian.css")
  }
});

const io = socketIo(server);

let htmlContent = '';

const updateHtmlContent = () => {
  const mdContent = fs.readFileSync('post-1.md', 'utf8');

  const marked = new Marked(
    markedHighlight({
      langPrefix: 'hljs language-',
      highlight(code, lang) {
        const language = hljs.getLanguage(lang) ? lang : 'plaintext';
        return hljs.highlight(code, { language }).value;
      }
    })
  );

  htmlContent = marked.parse(mdContent);
};

// Initial read
updateHtmlContent();

io.on('connection', (socket) => {
  console.log('Client connected');

  socket.emit('updateContent', htmlContent);

  socket.on('disconnect', () => {
    console.log('Client disconnected');
  });
});

fs.watchFile('article.md', () => {
  console.log('article.md changed');
  updateHtmlContent();

  io.emit('refreshContent');
});

server.listen(3000, () => {
  console.log('Server running on http://localhost:3000/');
});

With the above, I’m able to edit the file locally and see it’s preview in Safari. This works good enough, but as the length of article grows, there’s a noticeable lag between saving the file and the contents appearing on the web page. Perceivably, the lag comes from 2 sources:

  • there’s some kind of delay between when the file is changed, and when the change is “noticed”,
  • on top of that, there’s a full page re-render needs to happen every time the new contents is about to appear on page.

Because in Elixir ecosystem there exist tools like earmark, phoenix_live_view and phoenix_live_reload - couldn’t the above script in node be implemented much more optimally? E.g.:

  1. (server) on start,
    1. load .md file from disk,
    2. convert .md to .html, “memoize” it,
    3. serve .html,
    4. establish a WebSocket connection with browser,
  2. (server) listen for changes on .md file; when a change occurs:
    1. convert .md to .html,
    2. figure out diff between existing .html by using a previous “memoized” version of .html,
    3. send diff over the WebSocket connection to browser,
    4. let JavaScript in browser apply up the diff.

Conceptually, it would look like this:

Or more precisely, like this:

I don’t know a lot about LiveView, and not sure where to start about this one. I’ve noticed that LiveView actually is dependent on Phoenix itself, so actually a full-blown phoenix project is likely be needed here, but I am ok with that.

The benefits of such a server as I see it:

  • all .md files are local, so chances of loosing changes is small,
  • the preview generation is blazing fast,
  • I’d be using tools from Elixir ecosystem, and potentially contribute back (for example if earcut is lacking support for some things from CommonMark).

Where would one start on something like this? :thinking:

Showing Posts 1 to 5

gmile

gmile OP

:information_source: someone seems to have taken a stab at a similar idea:

https://github.com/nickdichev/markdown-live

The difference appears to be that they offer a on-page editor, as opposed to editing the file on the disk

D4no0

D4no0

From what I know, the actual html diff is a internal part of the liveview system and it triggers based on the change of the assignments. What I want to say with this is that currently liveview (from what I know) doesn’t have a system to generate html diffs from a full data-source directly, the way it generates them is an internal implementation that laverages on data(assigns).

If my assumption is correct, you will not be able to use liveview directly to patch the frontend html, because the markdown parser generates an entire html document, not some partial changes that liveview would understand how to operate with.

derek-zhou

derek-zhou

I send whole markdown from client to server with a small debounce (100ms) after key strokes, and render the whole markdown server side and send everything back. It works reasonably well for:

  • Smallish text, like a few paragraphs
  • Desktop browsers. Not because mobile is slow; but because the text input system on mobile is complex and the re-rendering of the textarea mess up the input states
smathy

smathy

Have you seen the MarkdownPreview plugin for nvim? It’s pretty good.

cmo

cmo

Logseq is an obsidian alternative.

Editor plugin also works great.

— All posts loaded —

Where Next? Top

Trending in Discussions Top

cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New
achempion
I’ve been using Emacs as my main code editor for more than a two years. It’s a custom build version although I’ve tried doom emacs and sp...
New
axelson
Hi there! :wave: @frigidcode and I (but mostly him) have been running an Elixir Book club, we’re almost done with Designing Elixir Syste...
New
budgie
I love Elixir. It’s one of 2 programming languages I’ve ever fallen in love with. But I don’t use it anymore. Serverless was the promis...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
georgeguimaraes
Just published claude-code-elixir, a plugin marketplace for Claude Code with Elixir support. These are the plugins I’ve been using for my...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews