Day 30 Dev Notes

I did some searching online today, looking for a Markdown editor that I could easily embed in a page, so that I could provide a better editing experience for posts. But while I was searching, I had an idea - what if I just built a Markdown previewer alongside a regular textarea element.

So I gave it a quick go, and it worked!

I had to make a bunch of changes to get this working, but ultimately it boils down to a view that takes a POST request, and converts the content it receives into Markdown. This is rendered with a simple template that either displays error text or the rendered HTML content.

The view is fairly simple:

def preview_markdown(request: HttpRequest) -> HttpResponse:
    """View for the preview markdown page."""
    html = ""
    error = ""

    if request.method == "POST" and "content" in request.POST:
        try:
            html = render_markdown(request.POST["content"])
        except Exception as e:
            msg = f"Error rendering markdown: {e}"
            logger.exception(msg)
            error = msg

    return render(
        request,
        "djpress_admin/preview_markdown.html",
        {"html": html, "error": error},
    )

I went to the effort of error handling, but right now I don't know how to trigger an error, since the Markdown renderer faithfully converts just about any garbage I give it. I will probably add some tag sanitizing in the future, so the error handling will come in useful then. But right now, the Markdown rendering is fairly simple:

import markdown
from django.conf import settings

md = markdown.Markdown(extensions=settings.MARKDOWN_EXTENSIONS, output_format="html")


def render_markdown(markdown_text: str) -> str:
    """Return the Markdown text as HTML."""
    html = md.convert(markdown_text)
    md.reset()

    return html

That will render just about anything you give it. Including <script> tags and all sorts of JavaScript!

And of course, the most important component in the puzzle, is the amazing HTMX. I attached all the HTMX logic to the preview button, and this works great.

        <button
          hx-headers='{"X-CSRFToken": "{{ csrf_token }}"}'
          hx-post="{% url "djpress_admin:preview_markdown" %}"
          hx-params="content"
          hx-target="#preview"
          type="button"
          class="btn btn-success">
          Preview content
        </button>

It's far from perfect, but for a quick and dirty demo, this worked amazingly well. I did this all in a new app called djpress_admin, which as the name suggests will be an optional package that can be used to add some admin views to manage DJPress content.

GitHub link

Screenshot:

DJPress Admin Markdown Preview