Today I’m excited relieved to release v1.0 of my long-running Markdown project, Zigdown. I’ve been working on this off and on for a few years now, and it’s reached a point where I’m ready to tie a bow on it and call it mostly done
Prebuilt binaries are available from the Releases link on Github.
Capabilities
See the README for more details (and feel free to reach out if you have any questions), but the main features are:
Pure Zig Markdown parser (no external C libraries required - although TreeSitter is used to provide syntax highlighting of within blocks)
Zigdown powers my personal website, and at work, I use it on a nearly daily basis to review documentation. Because the parser was written from scratch in Zig, I’ve found it fairly easy to add new Markdown extensions such as alert boxes. AFAIK, all the other Zig projects which parse Markdown use a 3rd-party parser library such as cmark or cmark-gfm.
I hope others might find Zigdown useful! If you’re looking for a more “professional” static site generator, you’re probably better off using Zine; and if you need full CommonMark compliance, you’re better off using cmark; but if you want to play around with a pure-Zig Markdown parser with some fun capabilities, maybe this is for you! Just note that you will undoubtedly encounter bugs in parsing, rendering, or both; I’ll continue fixing any I find, though. And with that, I can finally move on to my next project
The fact that it can render markdown in the terminal is great. I might consider using it for presentations in future.
An alternative use-case might be to browse through markdown files in the terminal. I use Obsidian and other markdown based tools all the time. It would be awesome to be able to view markdown files and even follow links in the terminal.
@_sh that’s a good idea; I’m planning to add table-of-contents generation to the HTML renderer based on the contents of a directory, so it should be possible to do the same thing in the terminal. My terminal output code wasn’t really designed for full interactive TUI’s, though, so making it “nice” might be hard. However, a “ToC Slide” that shows “slide numbers” for all the documents in a directory, then allows you to type a slide number to jump to, should be pretty simple!
FWIW you can kinda do this now using the present command with the -r (recursive) option, you just won’t get a table of contents.
Thank you so much for this! I’ve been wanting a cli markdown renderer that’s minimal in dependencies and written in a systems language. I’ll package this in the AUR unless someone else does that first
I finally got around to updating Zigdown to zig 0.16; the latest release is here:
I’m tentatively planning to split out the core parser to a separate repo, so that others can have a more lightweight, zero-dependency Markdown parser, leaving all of the tree-sitter dependencies for the renderer. I just have to think of a better name for the core parser library first
Great work! I’d like to know whether it is possbile to use just one markdown file as the input to render multiple pages of slides so that we can avoid passing “-s” and “-d”. Specifically, in Latex Beamer, we can render one page of slide by declaring a “frame” block. The final slides are compiled from a list of “frame” blocks.
Adding support for LaTeX-like commands is something I’ve considered, but ultimately it feels like that would be a slippery slope to, well, implementing a large subset of LaTeX
However, that doesn’t stop you from writing your own tool to do exactly what you want. Via the zigdown module I export, you have access to the parser and all renderers, so you could implement your own first-pass parser to split a file based on a delimiter of your own choosing, then render each section/slide/frame individually.
I’m also open to ideas around extension mechanisms - e.g. registering additional block types into the parser like some other Markdown packages do. I just haven’t spent any time thinking about it yet to come up with a design, but I’m sure it’s feasible. Whether there’s enough interest to justify the effort is a separate question.
Pretty cool, I have couple of notes if you’re interested in feedback.
I was a little surprised on attempting to render content that doesn’t fit into my viewport that this doesn’t have paging. IMO this is a fairly key feature for a md reader, as having to scroll all the way to the top of a document in order to begin reading feels like unnecessary work.
The serve command is handy, but would be handier if the url that the content is being served over were printed to the terminal.
None of this is meant as a dunk on your work, which I think is awesome.
If you mean paging in the terminal, you should be able to pipe the output to less -R -X? I use that with my own mdcat command that’s similar zigdown console.
I also quite liked zigdown when I tried it. I wanted to render the console output a little differently (like less ascii drawing around section headers), but found no way to control output styling apart from forking the renderer. That may be alright though as I guess one could take a copy of the existing renderer and then let LLMs adapt it to my own desired style.
I was a little surprised on attempting to render content that doesn’t fit into my viewport that this doesn’t have paging
Did the -p / --pager option not work for you? But as @nurpax pointed out, less is also a good option (and you can setup a bash alias for it). lmk if the built-in pager is broken for you!
We each have our own tastes, for sure If you want something more minimal, mdcat is probably a good option.
A big goal of my next refactor to rip out the parser and make it its own package is to make the system more modular overall. I’d like to make it easy for others to make their own changes to the tool, even if it means forking & making local changes (this might be the “optimal” solution vs. designing an ultra-complicated renderer extension mechanism - sometimes copy-paste is the solution).
We each have our own tastes, for sure If you want something more minimal, mdcat is probably a good option.
Yeah, for sure. I ended up building my own mdcat in Go using goldmark.
A big goal of my next refactor to rip out the parser and make it its own package is to make the system more modular overall. I’d like to make it easy for others to make their own changes to the tool, even if it means forking & making local changes (this might be the “optimal” solution vs. designing an ultra-complicated renderer extension mechanism - sometimes copy-paste is the solution).
I very much agree with this and support your refactoring plan too. When I first started looking for some extension mechanism, I thought maybe I should file an issue against zigdown about output formatting extensibility. But I have come to realize it’s not really such a big deal and learning some sort of renderer extension framework is probably more work than making a renderer from scratch. (Especially - and fully realizing that LLMs might not be so much in vogue here - as a zigdown renderer is something AI pretty much oneshots and the results is not bad at all.)
That looks like exactly what I had in mind, sorry, it seems I should have been more thorough. What’s the thinking re having that determined by a flag rather than as default behaviour though?