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

wmnnd
Hey folks, I wanted to share with you a project I’ve been working on for a while: It’s an Open Source (AGPLv3) newsletter tool. Think Ma...
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
alexslade
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog It says that Fly is going all-in on sprites, which is a worry ...
New
sntran
In my third year of college, around 2010, I found Erlang. A few years later, I found Elixir. I liked the ideas at once: many small proces...
New
Ganna
Hi everyone, I have 8 years of production experience with Erlang/OTP and I’m just starting to get to know Elixir. What would you learn f...
New
johnkirkham
A note on form. Joe Armstrong, co-creator of the Erlang programming language, died in April 2019. What follows is a work of narrative non...
New
gtcode
I’ve been tinkering with Lynx (by José) via hold_ledger. I used AI to write the code, a separate specification, the tests, and the Lean p...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews