MfGames.Markdown.Gemtext v1.2.1

As part of yesterday's post, I wanted to create a table in Gemtext that I could also produce in my Markdown to HTML pipeline. However, that was one step I skipped because it was overwhelming when I first wrote. However, the need to rant and warm pushed me over the board so I implemented tables.

What is MfGames.Markdown.Gemtext?

Let's start with the biggest question. This is a library that uses Markdig to parse Markdown and output gemtext instead of HTML. This is the limited markup format used by Gemini browsers which is kind of a low-overhead and simplified browsing.

The limitations of Gemtext are a big one. There are a few I struggle with, specifically a lack of italics and bolds, but it also doesn't let you have more than one link per paragraph, no Javascript, and generally is “what you see is exactly what you get”. For a writer, it fits nicely with that especially when used with a lovely client such as Lagrange.

I wrote the library inspired by md2gemini which creates a “reasonable” output from Markdown which is my native writing format for novels and posts.

At the moment, I don't have a SSL certificate to sign the library so I can't post it on NuGet, so I have my libraries at MyGet. In specific, MfGames.Markdown.Gemtext gives the information for downloading and using it.

Note, if you want want to multiple NuGet sources, the following NuGet.config works well for my packages:

<?xml version="1.0" encoding="utf-8"?>
    <add key="" value="" protocolVersion="3" />
    <add key="mfgames" value="" protocolVersion="3" />

I have this checked into any project that uses them. I have… opinions about single source repositories but getting a SSL certificate isn't quite in my cards for at least half a year.

The source is located on Gitlab along with ticket-tracking and the usual slew of tools.

Using MfGames.Markdown.Gemtext

The bulk of the code can be seen in the unit tests, but I want to work on better documentation. It's just waiting for Nitride. Until then, here are some basics.

// using MfGames.Markdown.Gemtext;
string input = "This is input.";
string expected = "This is input.";
string actual = MarkdownGemtext.ToGemtext(input);

Assert.Equal(expected, actual);

This is pretty much how the base Markdig processing is done. I had to jump through a few extra hoops because of hard-coding, but overall, this is it.

The biggest thing is converting a paragraph with links into something Gemtext. There are a couple of formats, but my preferred (and the one I use on my Gemini version of this site) is a table of links after the paragraph.

var input = string.Join(
    "This is a paragraph with an [inline]( link.",
    "Here is [another]( link, part of the same paragraph.",
    "This is a second paragraph, with a different [link]( in it.");
string actual = MarkdownGemtext.ToGemtext(
    new MarkdownPipelineBuilder()
        .Use(new SetBlockLinkHandling(BlockLinkHandling.ParagraphEnd))


/* Output is:
This is a paragraph with an inline[10] link. Here is another[11] link, part of the same paragraph.

=> 10:
=> 11:

This is a second paragraph, with a different link[12] in it.

=> 12:


Tables are not part of Gemtext. Before this version, if you passed in a table such as:


You would get everything on a single line.

aaa|bbb|ccc --:|---|:-: 1|2|3 4|5|6

With this version, you can pass a table parsing that produces a normalized table based on formatting using the excellent ConsoleTableEx library.

//using MfGames.Markdown.Gemtext;
//using MfGames.Markdown.Gemtext.Extensions;

MarkdownPipeline pipeline = new MarkdownPipelineBuilder()
        new GemtextPipeTableExtension(
            new GemtextPipeTableOptions()
                OmitPreformatLines = true,
                ConfigureTableBuilder = (x) => x.WithFormat(ConsoleTableBuilderFormat.MarkDown),
string input = string.Join(
string expected = string.Join(
    "| aaa | bbb | ccc |",
    "|   1 | 2   |  3  |",
    "|   4 | 5   |  6  |",
string actual = MarkdownGemtext.ToGemtext(input, pipeline);

Assert.Equal(expected, actual);

I went with the library because I like how ConsoleTableEx will make sure everything is lined up. Plus, you can change the formatting to change the borders around and between the cells, different layouts, and the like. Basically, the options configured in the ConfigureTableBuilder lets the developer set the format instead of me deciding on a One True Way™️.

What's Next

I have a couple minor little things with this library, but a lot of it is based on getting Nitride cleaned up and usable. This site uses Nitride and it runs nicely, but it isn't quite ready for prime time. So, I'm taking lessons learned, refactoring that library, and hopefully get it ready for the next big site (